confirm dialog for map peak toggles, search below map, themed attribution
This commit is contained in:
@@ -288,12 +288,16 @@
|
|||||||
:global(.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon) {
|
:global(.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon) {
|
||||||
filter: none;
|
filter: none;
|
||||||
}
|
}
|
||||||
:global(.maplibregl-ctrl-attrib) {
|
:global(.maplibregl-ctrl-attrib),
|
||||||
background: color-mix(in srgb, var(--surface-1) 80%, transparent);
|
:global(.maplibregl-ctrl-attrib.maplibregl-compact) {
|
||||||
|
background: color-mix(in srgb, var(--surface-1) 85%, transparent) !important;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
:global(.maplibregl-ctrl-attrib a) {
|
:global(.maplibregl-ctrl-attrib a) {
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary) !important;
|
||||||
|
}
|
||||||
|
:global(.maplibregl-ctrl-attrib-button) {
|
||||||
|
filter: var(--map-icon-filter);
|
||||||
}
|
}
|
||||||
:global(.maplibregl-popup.peak-tip .maplibregl-popup-content) {
|
:global(.maplibregl-popup.peak-tip .maplibregl-popup-content) {
|
||||||
background: var(--surface-1);
|
background: var(--surface-1);
|
||||||
|
|||||||
@@ -56,6 +56,19 @@
|
|||||||
// the "In view" list only offers peaks still to climb; climbed ones live in "My ascents"
|
// the "In view" list only offers peaks still to climb; climbed ones live in "My ascents"
|
||||||
const toClimb = $derived(viewPeaks.filter((p) => !p.climbed_at));
|
const toClimb = $derived(viewPeaks.filter((p) => !p.climbed_at));
|
||||||
|
|
||||||
|
// map clicks confirm via a small dialog - easy to hit a peak while panning/zooming
|
||||||
|
let confirmPeak: ViewPeak | null = $state(null);
|
||||||
|
|
||||||
|
function onMapPeakClick(id: number) {
|
||||||
|
confirmPeak = viewPeaks.find((p) => p.id === id) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmToggle() {
|
||||||
|
if (!confirmPeak) return;
|
||||||
|
await toggle(confirmPeak.id);
|
||||||
|
confirmPeak = null;
|
||||||
|
}
|
||||||
|
|
||||||
const highest = $derived(ascents[0] ?? null);
|
const highest = $derived(ascents[0] ?? null);
|
||||||
|
|
||||||
const mapPeaks = $derived(
|
const mapPeaks = $derived(
|
||||||
@@ -134,6 +147,8 @@
|
|||||||
Zoom in to reveal less prominent summits; click a peak to cross it off.
|
Zoom in to reveal less prominent summits; click a peak to cross it off.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<Map bind:this={mapRef} peaks={mapPeaks} height="480px" onpeakclick={onMapPeakClick} {onviewport} />
|
||||||
|
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
@@ -159,7 +174,35 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Map bind:this={mapRef} peaks={mapPeaks} height="480px" onpeakclick={toggle} {onviewport} />
|
{#if confirmPeak}
|
||||||
|
<div
|
||||||
|
class="dialog-backdrop"
|
||||||
|
role="presentation"
|
||||||
|
onclick={(e) => {
|
||||||
|
if (e.target === e.currentTarget) confirmPeak = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="dialog card" role="dialog" aria-modal="true" aria-label="Peak">
|
||||||
|
<p class="d-name">{confirmPeak.name}</p>
|
||||||
|
<p class="d-sub">
|
||||||
|
{confirmPeak.elevation_m
|
||||||
|
? `${Math.round(confirmPeak.elevation_m).toLocaleString('en-US')} m`
|
||||||
|
: 'elevation unknown'}
|
||||||
|
{#if confirmPeak.climbed_at}
|
||||||
|
· reached {fmtDate(confirmPeak.climbed_at)}
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
<div class="d-actions">
|
||||||
|
{#if confirmPeak.climbed_at}
|
||||||
|
<button class="btn danger" onclick={confirmToggle}>Remove ascent</button>
|
||||||
|
{:else}
|
||||||
|
<button class="btn" onclick={confirmToggle}>Mark as reached</button>
|
||||||
|
{/if}
|
||||||
|
<button class="btn ghost" onclick={() => (confirmPeak = null)}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="tabs" role="group" aria-label="Peak lists">
|
<div class="tabs" role="group" aria-label="Peak lists">
|
||||||
<button class="filter-btn" class:on={tab === 'view'} onclick={() => (tab = 'view')}>
|
<button class="filter-btn" class:on={tab === 'view'} onclick={() => (tab = 'view')}>
|
||||||
@@ -225,7 +268,7 @@
|
|||||||
<style>
|
<style>
|
||||||
.search {
|
.search {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-bottom: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
max-width: 420px;
|
max-width: 420px;
|
||||||
}
|
}
|
||||||
.search input {
|
.search input {
|
||||||
@@ -288,6 +331,35 @@
|
|||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
.dialog-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1200;
|
||||||
|
background: rgba(0, 0, 0, 0.35);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
.dialog {
|
||||||
|
padding: 1.25rem 1.4rem;
|
||||||
|
min-width: 260px;
|
||||||
|
max-width: 360px;
|
||||||
|
}
|
||||||
|
.d-name {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
.d-sub {
|
||||||
|
margin: 0.2rem 0 1rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
.d-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
.tabs {
|
.tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user