From 2f751b2cecdb4e250c2b777e8b557b4fd18b35b1 Mon Sep 17 00:00:00 2001 From: Martin Jensen Date: Sat, 18 Apr 2026 17:10:09 +0200 Subject: [PATCH] Add collapsible gem list and viewport filter - Sheet header now has a collapse toggle (chevron) and a viewport filter button (crosshair icon) - Clicking the handle, title, or chevron collapses/expands the list - Viewport filter restricts the list to gems visible in the current map bounds, updating live on pan/zoom - Sheet title shows "X of Y in view" when filter is active - Empty state for viewport filter case ("No gems in this area") - Action button active states use terracotta palette from new design system Co-Authored-By: Claude Sonnet 4.6 --- index.html | 138 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 117 insertions(+), 21 deletions(-) diff --git a/index.html b/index.html index 26ee71a..e6227a3 100644 --- a/index.html +++ b/index.html @@ -158,14 +158,55 @@ flex-shrink: 0; } + .sheet-header { + display: flex; + align-items: center; + padding: 0 8px 10px 16px; + flex-shrink: 0; + gap: 4px; + } + .sheet-title { font-size: 10.5px; font-weight: 600; color: var(--muted-light); letter-spacing: 0.1em; text-transform: uppercase; - padding: 0 16px 10px; - flex-shrink: 0; + flex: 1; + cursor: pointer; + user-select: none; + } + + .sheet-header-actions { + display: flex; + gap: 2px; + } + + .sheet-action { + width: 28px; + height: 28px; + border: none; + background: transparent; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: var(--muted-light); + transition: background 0.15s, color 0.15s; + padding: 0; + } + .sheet-action:active { background: var(--warm-sand); } + .sheet-action.active { color: var(--terracotta); background: var(--warm-sand); } + + .sheet-collapse-icon { + transition: transform 0.2s; + } + .sheet.collapsed .sheet-collapse-icon { + transform: rotate(180deg); + } + .sheet.collapsed .gem-list { + display: none; } .gem-list { @@ -879,8 +920,26 @@

Hidden Gems

-
-
Your gems
+
+
+
Your gems
+
+ + +
+
🌿Tap + to add your first hidden gem
@@ -1047,28 +1106,65 @@

Hidden Gems

// ── List ────────────────────────────────────────────────────────────────────── +let filterByViewport = false; + +function toggleSheet() { + document.getElementById('sheet').classList.toggle('collapsed'); +} + +function toggleViewportFilter() { + filterByViewport = !filterByViewport; + document.getElementById('viewportFilterBtn').classList.toggle('active', filterByViewport); + if (filterByViewport) { + map.on('moveend zoomend', renderList); + } else { + map.off('moveend zoomend', renderList); + } + renderList(); +} + function renderList() { const list = document.getElementById('gemList'); const title = document.getElementById('sheetTitle'); - title.textContent = gems.length === 1 ? '1 gem' : `${gems.length} gems`; - if (gems.length === 0) { - list.innerHTML = `
- - - - - - - - - - -
No gems yet
-
Tap + to drop your first hidden gem on the map
-
`; + + let visible = gems; + if (filterByViewport && map) { + const bounds = map.getBounds(); + visible = gems.filter(g => g.lat && g.lng && bounds.contains([g.lat, g.lng])); + } + + if (filterByViewport) { + title.textContent = `${visible.length} of ${gems.length} in view`; + } else { + title.textContent = gems.length === 1 ? '1 gem' : `${gems.length} gems`; + } + + if (visible.length === 0) { + if (filterByViewport && gems.length > 0) { + list.innerHTML = `
+
No gems in this area
+
Pan or zoom the map to find your gems
+
`; + } else { + list.innerHTML = `
+ + + + + + + + + + +
No gems yet
+
Tap + to drop your first hidden gem on the map
+
`; + } return; } - list.innerHTML = gems.map(g => { + + list.innerHTML = visible.map(g => { const thumb = g.photo_url ? `` : `
${g.emoji || '🌿'}
`;