diff --git a/web/src/routes/[username]/[workspace]/[collection]/+page.svelte b/web/src/routes/[username]/[workspace]/[collection]/+page.svelte index 127d143a..9ff6f9a3 100644 --- a/web/src/routes/[username]/[workspace]/[collection]/+page.svelte +++ b/web/src/routes/[username]/[workspace]/[collection]/+page.svelte @@ -9,6 +9,7 @@ import TableView from '$lib/components/collections/TableView.svelte'; import FilterBar from '$lib/components/collections/FilterBar.svelte'; import QuickActionsMenu from '$lib/components/common/QuickActionsMenu.svelte'; + import BottomSheet from '$lib/components/common/BottomSheet.svelte'; import { onDestroy, onMount } from 'svelte'; import { sseService } from '$lib/services/sse.svelte'; import { syncService } from '$lib/services/sync.svelte'; @@ -392,6 +393,40 @@ let filtersOpen = $state(false); let hasActiveFilters = $derived(searchQuery.trim() !== '' || Object.keys(activeFilters).length > 0); + // ── Viewport detection ─────────────────────────────────────────────── + // On mobile the 3-icon view toggle (list/board/table) is swapped for a + // chip trigger that opens a BottomSheet with labeled options — the raw + // icon glyphs are ambiguous on touch and a labeled sheet is clearer. + // Desktop keeps the segmented toggle unchanged. + let isMobile = $state(false); + $effect(() => { + if (typeof window === 'undefined') return; + const mq = window.matchMedia('(max-width: 639.98px)'); + isMobile = mq.matches; + const onChange = (e: MediaQueryListEvent) => { + isMobile = e.matches; + // If the viewport crosses above the mobile breakpoint while the + // sheet is open (e.g. rotation), close it so a return to mobile + // doesn't immediately re-mount the open sheet. + if (!e.matches) { + viewSheetOpen = false; + } + }; + mq.addEventListener('change', onChange); + return () => mq.removeEventListener('change', onChange); + }); + + let viewSheetOpen = $state(false); + let viewModeLabel = $derived( + viewMode === 'list' ? 'List' : viewMode === 'board' ? 'Board' : 'Table' + ); + + function selectViewMode(mode: ViewMode) { + saveViewMode(mode); + updateUrlFilters(); + viewSheetOpen = false; + } + function singularName(): string { if (!collection) return 'item'; const name = collection.name; @@ -803,29 +838,88 @@