Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
785e71e
refactor(map): make drawer gesture controller factory reusable
escapedcat Jun 12, 2026
9bf8349
feat(map): add nearby pill count formatter
escapedcat Jun 12, 2026
64984bf
feat(i18n): add single-input search keys
escapedcat Jun 12, 2026
2b5266d
feat(map): single search input with nearby count pill, drop floating …
escapedcat Jun 12, 2026
4bd27dd
feat(map): filtered nearby tab count and search-worldwide empty state
escapedcat Jun 12, 2026
8d3a125
feat(map): mobile search bottom sheet with drawer-style snap points
escapedcat Jun 12, 2026
2326c09
fix(map): harden search sheet gestures, a11y, e2e, and translations a…
escapedcat Jun 12, 2026
443edf4
feat(map): drag the search sheet from the input and filter rows
escapedcat Jun 12, 2026
1a3bf65
fix(map): slim search sheet peek to 84px with adaptive safe-area
escapedcat Jun 12, 2026
894ca9b
fix(map): address review feedback on search sheet
escapedcat Jun 14, 2026
2e36dee
feat(map): floating search peek card with drag handle
escapedcat Jun 14, 2026
72aef85
feat(map): single search input — drop Worldwide/Nearby toggle
escapedcat Jun 14, 2026
7644fe3
chore(i18n): remove dead toggle/CTA keys and analytics events
escapedcat Jun 14, 2026
217f33d
feat(map): compact attribution + float search above the bottom chrome
escapedcat Jun 14, 2026
5c43955
fix(map): populate nearby count on first load, collapse attribution
escapedcat Jun 14, 2026
42fd7fe
test(map): update panel e2e for the single-input search model
escapedcat Jun 14, 2026
552b591
feat(map): show nearby count inside the open desktop panel
escapedcat Jun 14, 2026
e728acd
fix(map): only compact the attribution on mobile
escapedcat Jun 14, 2026
1ada5db
fix(map): harden search + drop dead formatNearbyCount
escapedcat Jun 15, 2026
e8be146
feat(map): shared CollapseButton caret with round background
escapedcat Jun 15, 2026
113b316
test(map): assert nearby count shows in the open desktop panel
escapedcat Jun 15, 2026
d0b0fe1
fix(map): address CodeRabbit review
escapedcat Jun 15, 2026
4f6d8e7
fix(map): address Copilot review
escapedcat Jun 15, 2026
d303044
docs(map): correct stale SEARCH_SHEET_PEEK_HEIGHT comment
escapedcat Jun 15, 2026
553d26c
fix(map): guard nearby status-row zoom-in link while list loads
escapedcat Jun 15, 2026
7799621
refactor(map): anchor the search sheet to the bottom edge
escapedcat Jun 18, 2026
20b196d
fix(map): guard areas without a tags object in getCommunitiesAtCoordi…
escapedcat Jun 18, 2026
434115a
fix(map): only preventDefault cancelable touchmove in collapse drag
escapedcat Jun 18, 2026
1b00316
refactor(map): drop merchant peek swipe hint and tighten peek height
escapedcat Jun 18, 2026
a0ce6cc
refactor(map): remove cluster hover-hull green polygon
escapedcat Jun 18, 2026
c426f8e
fix(map): address Copilot review on nearby count cap
escapedcat Jun 18, 2026
0031287
docs(map): drop Google Maps references from code comments
escapedcat Jun 18, 2026
079d68a
test(map): detect mobile drawer via dialog role not swipe-hint text
escapedcat Jun 18, 2026
c5ff53a
fix(map): keep the search peek clear of the bottom gesture bar
escapedcat Jun 19, 2026
fa8500e
refactor(map): drive bottom-chrome lift from the peek-height const
escapedcat Jun 19, 2026
776082c
fix(map): search peek stuck at full height after select then back
escapedcat Jun 19, 2026
15ed3f6
fix(map): guard search error path and trim the query before fetch
escapedcat Jun 19, 2026
e7418e2
fix(map): close the list when the merchant drawer opens on mobile
escapedcat Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
"@nazka/map-gl-js-spiderfy": "^2.0.0",
"@tanstack/match-sorter-utils": "^8.19.4",
"@tanstack/svelte-table": "^8.21.3",
"@turf/convex": "^7.3.5",
"@turf/helpers": "^7.3.5",
"@zerodevx/svelte-toast": "^0.9.6",
"axios": "^1.15.0",
"axios-retry": "^4.5.0",
Expand Down
71 changes: 0 additions & 71 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions src/components/CollapseButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts">
import Icon from "$components/Icon.svelte";

// Collapse caret used by the bottom drawers/sheets (merchant drawer, search
// sheet). A round light background makes it read as a button. pointerdown is
// stopped so pressing it never starts a sheet drag.
export let onClick: () => void;
export let ariaLabel: string;
let className = "";

export { className as class };
</script>

<button
type="button"
on:pointerdown|stopPropagation
on:click={onClick}
class="grid h-8 w-8 touch-auto place-items-center rounded-full bg-gray-100 text-primary transition-colors hover:bg-gray-200 dark:bg-white/10 dark:text-white dark:hover:bg-white/20 {className}"
aria-label={ariaLabel}
{...$$restProps}
>
<Icon w="20" h="20" icon="keyboard_arrow_down" type="material" />
</button>
5 changes: 4 additions & 1 deletion src/components/LoadingIndicator.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ import LoadingSpinner from "$components/LoadingSpinner.svelte";
export let visible = false;
export let status = "";
export let progress: number | undefined = undefined;
// Bottom/left position classes — overridable so the map's tile indicator can
// clear the anchored search sheet on mobile.
export let positionClass = "bottom-6 left-2 md:bottom-8 md:left-4";
</script>

{#if visible}
<div
role="status"
aria-live="polite"
transition:fade={{ duration: 200 }}
class="pointer-events-none fixed bottom-6 left-2 z-[1000] flex items-center gap-1.5 rounded-md bg-white px-2 py-1.5 shadow-lg md:bottom-8 md:left-4 md:gap-2 md:rounded-lg md:px-3 md:py-2 dark:border dark:border-white/20 dark:bg-dark"
class="pointer-events-none fixed z-[1000] flex items-center gap-1.5 rounded-md bg-white px-2 py-1.5 shadow-lg md:gap-2 md:rounded-lg md:px-3 md:py-2 dark:border dark:border-white/20 dark:bg-dark {positionClass}"
>
<LoadingSpinner size="h-3 w-3 md:h-4 md:w-4" color="text-link" />
<span class="text-xs text-primary md:text-sm dark:text-white">
Expand Down
13 changes: 10 additions & 3 deletions src/components/SearchInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export let value: string = "";
export let placeholder: string = "";
export let ariaLabel: string = "Search";
export let rounded: boolean = false;
// Filled-field variant: gives the input a grey rounded surface so it reads
// clearly as a tappable search field (used inside the merchant list panel)
export let filled: boolean = false;

let inputElement: HTMLInputElement;

Expand All @@ -30,10 +33,14 @@ export function focus() {
type="search"
{placeholder}
aria-label={ariaLabel}
class="w-full border-0 bg-transparent py-3 pr-10 pl-10 text-base text-primary outline-none placeholder:text-gray-400 dark:text-white dark:placeholder:text-white/50 [&::-webkit-search-cancel-button]:hidden
{rounded ? 'rounded-lg' : ''}"
class="w-full border-0 py-3 pr-10 pl-10 text-base text-primary outline-none placeholder:text-gray-400 dark:text-white dark:placeholder:text-white/50 [&::-webkit-search-cancel-button]:hidden
{rounded ? 'rounded-lg' : ''}
{filled ? 'rounded-xl bg-gray-100 dark:bg-white/5' : 'bg-transparent'}"
/>
<div class="absolute top-1/2 right-3 -translate-y-1/2">
<!-- pointer-events-none so passive content (count pill, spinner) doesn't
block taps on the input underneath; interactive slotted buttons must
re-enable with pointer-events-auto -->
<div class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2">
<slot name="trailing" />
</div>
</div>
7 changes: 4 additions & 3 deletions src/lib/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import { dev } from "$app/environment";

const ANALYTICS_HOSTNAME = "btcmap.org";

type EventName =
export type EventName =
| "search_query"
| "search_input_focus"
| "search_sheet_swipe_expand"
| "search_sheet_swipe_collapse"
| "search_sheet_tap_expand"
| "category_filter"
| "boost_layer_toggle"
| "worldwide_mode_click"
| "nearby_mode_click"
| "home_button_click"
| "show_all_on_map_click"
| "merchant_list_item_click"
Expand Down
12 changes: 7 additions & 5 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const MAP_FIT_BOUNDS_PADDING = 50;
// MERCHANT LIST ZOOM BEHAVIOR:
// ┌─────────────────────────────────────────────────────────────────────────┐
// │ Zoom < 10 │ No data shown - "zoom in" message │
// │ Zoom 10-14 │ API search (1.5x radius): nearest 99; "zoom in" if >750
// │ Zoom 10-14 │ API search (1.5x radius): nearest 250; "zoom in" if >750 │
// │ Zoom 15+ │ Use loaded markers with 1.5x bounds, names visible, enrich when open │
// └─────────────────────────────────────────────────────────────────────────┘
// All zoom levels use 1.5x radius multiplier for consistent "nearby" count.
Expand All @@ -66,16 +66,18 @@ export const MERCHANT_LIST_MIN_ZOOM = 15;
// matches exceed MERCHANT_LIST_FETCH_CEILING (a payload guard).
export const MERCHANT_LIST_LOW_ZOOM = 10;

// Max merchants to display in list and count shown on button
// When count exceeds this, button shows ">99" and list shows 99 items
export const MERCHANT_LIST_MAX_ITEMS = 99;
// Max merchants to display in the nearby list and count shown on the badge.
// When the count exceeds this, the badge shows ">250" and the list renders
// the nearest 250 rows. (Worldwide search results are uncapped; this only
// bounds the at-rest nearby browse list.)
export const MERCHANT_LIST_MAX_ITEMS = 250;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Relevance/render guard for the low-zoom API search: above this many matches
// we blank the list and show "zoom in" rather than render an unhelpfully dense
// set. Applied client-side AFTER the full response is fetched, so it does not
// reduce the download — a server-side distance-sorted limit would be needed for
// that. Distinct from MERCHANT_LIST_MAX_ITEMS (the display/slice cap) and far
// higher, so dense areas show the nearest 99 rather than blanking.
// higher, so dense areas show the nearest 250 rather than blanking.
export const MERCHANT_LIST_FETCH_CEILING = 750;

// Radius multiplier for "nearby" search (extends beyond viewport for context)
Expand Down
11 changes: 9 additions & 2 deletions src/lib/drawerConfig.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
// Drawer gesture configuration constants
// These values control the behavior of the mobile drawer's swipe gestures

// Drawer height states
export const PEEK_HEIGHT = 140; // Collapsed state height - shows merchant name and quick info
// Peek (collapsed) heights for the two bottom sheets.
// Merchant drawer: bottom-anchored, full-width; shows name + payment/verify row
// (snug — no trailing swipe hint).
export const PEEK_HEIGHT = 110;
// Search sheet: bottom-anchored, full-width (like the merchant drawer); peek
// shows the grabber + single input. Rounded top corners at peek, square once
// expanded. Tall enough that the input clears the bottom gesture bar / curved
// corners on bezelled phones (the facade also adds a safe-area bottom inset).
export const SEARCH_SHEET_PEEK_HEIGHT = 88;

// Gesture thresholds
export const VELOCITY_THRESHOLD = 0.5; // px/ms - minimum velocity for flick gesture detection
Expand Down
Loading
Loading