Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions src/routes/map/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,7 @@
}}
nearbyCount={$merchantList.totalCount}
isLoadingCount={$merchantList.isLoadingList}
zoomLevel={currentZoom}
/>
</div>
{/if}
Expand Down
67 changes: 67 additions & 0 deletions src/routes/map/components/MapCategoryFilters.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<script lang="ts">
import { merchantList } from '$lib/merchantListStore';
import { CATEGORY_ENTRIES } from '$lib/categoryMapping';
import { MERCHANT_LIST_LOW_ZOOM } from '$lib/constants';

export let zoomLevel: number;
export let activeCategory: string = 'all';

type CategoryKey =
| 'all'
| 'restaurants'
| 'shopping'
| 'groceries'
| 'coffee'
| 'atms'
| 'hotels'
| 'beauty';

// Show filters only when zoomed in enough to show nearby places
$: shouldShowFilters = zoomLevel >= MERCHANT_LIST_LOW_ZOOM;

function handleCategoryClick(category: string) {
const categoryKey = category as CategoryKey;
merchantList.setSelectedCategory(categoryKey === activeCategory ? 'all' : categoryKey);
}

function getCategoryButtonClass(key: string, selectedCategory: string): string {
if (selectedCategory === key) return 'bg-link text-white';
return 'bg-white text-gray-600 hover:bg-gray-50 dark:bg-dark dark:text-white/70 dark:hover:bg-white/10';
}
</script>

{#if shouldShowFilters}
<div class="map-category-filters" role="radiogroup" aria-label="Filter by category">
<h3 class="sr-only">Filter by category</h3>
<div class="flex flex-nowrap gap-1.5 whitespace-nowrap md:gap-1.5">
{#each CATEGORY_ENTRIES as [key, category] (key)}
<button
type="button"
role="radio"
on:click={() => handleCategoryClick(key)}
aria-checked={activeCategory === key}
class="rounded-full px-3 py-1.5 text-xs font-medium whitespace-nowrap shadow-sm transition-colors {getCategoryButtonClass(
key,
activeCategory
)}"
>
{category.label}
</button>
{/each}
</div>
</div>
{/if}

<style>
.map-category-filters {
display: flex;
align-items: center;
z-index: 1000;
}

@media (max-width: 1024px) {
.map-category-filters {
display: none !important;
}
}
</style>
31 changes: 28 additions & 3 deletions src/routes/map/components/MapSearchBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { merchantList } from '$lib/merchantListStore';
import Icon from '$components/Icon.svelte';
import SearchInput from '$components/SearchInput.svelte';
import MapCategoryFilters from './MapCategoryFilters.svelte';
import { trackEvent } from '$lib/analytics';
import { formatNearbyCount } from '$lib/utils';

Expand All @@ -12,6 +13,7 @@
export let onNearbyClick: (() => void) | undefined = undefined;
export let nearbyCount = 0;
export let isLoadingCount = false;
export let zoomLevel = 0;

$: formattedCount = formatNearbyCount(nearbyCount);

Expand All @@ -22,6 +24,7 @@
$: mode = $merchantList.mode;
$: isSearching = $merchantList.isSearching;
$: isOpen = $merchantList.isOpen;
$: selectedCategory = $merchantList.selectedCategory;

// Placeholder based on mode
$: placeholder = mode === 'search' ? 'Search worldwide...' : 'Search nearby...';
Expand Down Expand Up @@ -73,7 +76,9 @@

<!-- Floating search bar - hidden when panel is open (panel has its own search in same position) -->
{#if !isOpen}
<div class="pointer-events-auto flex w-full flex-col-reverse gap-2 md:w-80 md:flex-col">
<div
class="pointer-events-auto flex w-full flex-col-reverse gap-2 md:relative md:w-80 md:flex-col"
>
<!-- Search input -->
<div class="rounded-lg bg-white shadow-lg dark:bg-dark dark:shadow-black/30">
<SearchInput
Expand Down Expand Up @@ -115,7 +120,7 @@
aria-checked={mode === 'search'}
on:click={() => handleModeSwitch('search')}
class="rounded-full px-4 py-2.5 text-sm font-medium shadow-sm transition-colors md:px-3 md:py-1.5 md:text-xs
{mode === 'search'
{mode === 'search'
? 'bg-link text-white'
: 'bg-white text-gray-600 hover:bg-gray-50 dark:bg-dark dark:text-white/70 dark:hover:bg-white/10'}"
>
Expand All @@ -127,13 +132,33 @@
aria-checked={mode === 'nearby'}
on:click={() => handleModeSwitch('nearby')}
class="rounded-full px-4 py-2.5 text-sm font-medium shadow-sm transition-colors md:px-3 md:py-1.5 md:text-xs
{mode === 'nearby'
{mode === 'nearby'
? 'bg-link text-white'
: 'bg-white text-gray-600 hover:bg-gray-50 dark:bg-dark dark:text-white/70 dark:hover:bg-white/10'}"
>
Nearby{#if isLoadingCount}<span class="opacity-60"> ...</span>{:else if formattedCount}
{formattedCount}{/if}
</button>
</div>
<!-- Category filters - shown when zoomed in enough -->
<div class="category-filters-container">
<MapCategoryFilters {zoomLevel} activeCategory={selectedCategory} />
</div>

<style>
.category-filters-container {
position: absolute;
top: 8px;
right: 0;
transform: translateX(calc(100% + 1rem));
z-index: 1000;
}

@media (max-width: 1024px) {
.category-filters-container {
display: none !important;
}
}
</style>
</div>
{/if}
Loading