diff --git a/src/lib/components/ElevationChart.svelte b/src/lib/components/ElevationChart.svelte index 44ebc62..7f5d93e 100644 --- a/src/lib/components/ElevationChart.svelte +++ b/src/lib/components/ElevationChart.svelte @@ -1,10 +1,13 @@ - -
- - diff --git a/src/lib/components/SpeedChart.svelte b/src/lib/components/SpeedChart.svelte index 47c3732..e8c3918 100644 --- a/src/lib/components/SpeedChart.svelte +++ b/src/lib/components/SpeedChart.svelte @@ -3,10 +3,13 @@ let { timed, + hoverD = null, onhover, onselect }: { timed: { d: number; t: number }[]; + /** shared hover distance controlled by the page, so charts stay in sync */ + hoverD?: number | null; onhover?: (d: number | null) => void; onselect?: (d: number) => void; } = $props(); @@ -82,21 +85,23 @@ : '' ); - let hover = $state<{ d: number; v: number } | null>(null); - - function onmove(event: PointerEvent) { - const rect = (event.currentTarget as SVGRectElement).getBoundingClientRect(); - const frac = Math.min(1, Math.max(0, (event.clientX - rect.left) / rect.width)); - const target = frac * totalDist; + // snap the shared hover distance to this chart's nearest point + const hover = $derived.by(() => { + if (hoverD === null || series.length === 0) return null; let lo = 0; let hi = series.length - 1; while (hi - lo > 1) { const mid = (lo + hi) >> 1; - if (series[mid].d < target) lo = mid; + if (series[mid].d < hoverD) lo = mid; else hi = mid; } - hover = target - series[lo].d < series[hi].d - target ? series[lo] : series[hi]; - onhover?.(hover.d); + return hoverD - series[lo].d < series[hi].d - hoverD ? series[lo] : series[hi]; + }); + + function onmove(event: PointerEvent) { + const rect = (event.currentTarget as SVGRectElement).getBoundingClientRect(); + const frac = Math.min(1, Math.max(0, (event.clientX - rect.left) / rect.width)); + onhover?.(frac * totalDist); } const tooltipLeft = $derived(hover ? Math.min(Math.max(x(hover.d), 70), width - 70) : 0); @@ -144,10 +149,7 @@ fill="transparent" onpointermove={onmove} onclick={() => hover && onselect?.(hover.d)} - onpointerleave={() => { - hover = null; - onhover?.(null); - }} + onpointerleave={() => onhover?.(null)} /> {#if hover} diff --git a/src/lib/map-state.svelte.ts b/src/lib/map-state.svelte.ts index 2881978..3255b52 100644 --- a/src/lib/map-state.svelte.ts +++ b/src/lib/map-state.svelte.ts @@ -13,6 +13,7 @@ let detailTrack = $state(null); let hoverPoint = $state<{ lat: number; lon: number } | null>(null); let carrier = $state(null); let active = $state(false); +let wanted = $state(false); let home: HTMLElement | null = null; let peakClickHandler: ((id: number) => void) | null = null; @@ -42,6 +43,12 @@ export const mapState = { get active() { return active; }, + get wanted() { + return wanted; + }, + setWanted(value: boolean) { + wanted = value; + }, setPeaks(value: MapPeak[]) { peaks = value; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 923999b..99c4804 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -9,11 +9,20 @@ let mapHome: HTMLDivElement; let mapCarrier: HTMLDivElement; + let layoutSlot: HTMLDivElement; $effect(() => { if (mapCarrier && mapHome) mapState.registerCarrier(mapCarrier, mapHome); }); + // show the persistent map in its fixed spot whenever the page asks for it + $effect(() => { + if (mapState.wanted && layoutSlot && mapState.carrier) { + mapState.attach(layoutSlot); + return () => mapState.detach(); + } + }); + let mapComponent: Map | undefined = $state(); $effect(() => { if (mapComponent) { @@ -136,6 +145,11 @@
+
{@render children()}
@@ -378,4 +392,8 @@ width: 100%; height: 100%; } + .layout-map-slot { + height: min(440px, 52vh); + margin-bottom: 1.75rem; + } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 7f83d3f..075ff39 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -30,8 +30,7 @@ -

Worldmap

- +

Recent activities

{#if data.activities.length === 0} diff --git a/src/routes/activities/[id]/+page.svelte b/src/routes/activities/[id]/+page.svelte index 6e92c05..c34e983 100644 --- a/src/routes/activities/[id]/+page.svelte +++ b/src/routes/activities/[id]/+page.svelte @@ -1,282 +1,328 @@ - {a.name} · Streba + {a.name} · Streba ← Overview
- {#if editing} -
{ - saving = true; - return async ({ update, result }) => { - saving = false; - if (result.type === 'success') editing = false; - await update(); - }; - }} - > - - - - {#each ['hike', 'run', 'ride', 'ski', 'climbing', 'walk', 'snowshoe'] as t (t)} - - {/each} - - - - {#if form?.error}{form.error}{/if} -
- {:else} -
-

- {a.name} - {#if data.canDelete} - - {/if} -

-

- {a.username} · {fmtDate(a.date)} · - {a.type} -

-
- {/if} - {#if data.canDelete && !editing} -
{ - if (!confirm('Delete this activity? Peaks it reached stay in your list.')) cancel(); - }} - > - -
- {/if} + {#if editing} +
{ + saving = true; + return async ({ update, result }) => { + saving = false; + if (result.type === "success") editing = false; + await update(); + }; + }} + > + + + + {#each ["hike", "run", "ride", "ski", "climbing", "walk", "snowshoe"] as t (t)} + + {/each} + + + + {#if form?.error}{form.error}{/if} +
+ {:else} +
+

+ {a.name} + {#if data.canDelete} + + {/if} +

+

+ {a.username} · {fmtDate( + a.date, + )} · + {a.type} +

+
+ {/if} + {#if data.canDelete && !editing} +
{ + if ( + !confirm("Delete this activity? Peaks it reached stay in your list.") + ) + cancel(); + }} + > + +
+ {/if}
- - - - {#if avgSpeed} - - {:else} - - {/if} + + + + {#if avgSpeed} + + {:else} + + {/if}
{#if data.bagged.length > 0} -
- ⛰ Peaks reached: - {#each data.bagged as peak, i (peak.id)}{i > 0 ? ', ' : ' '}{peak.name} ({peak.elevation_m} m){/each} -
+
+ ⛰ Peaks reached: + {#each data.bagged as peak, i (peak.id)}{i > 0 ? ", " : " "}{peak.name} + ({peak.elevation_m} m){/each} +
{/if} -

Map

- + {#if data.profile.length > 1} -

Elevation profile

-
- -
- Low {fmtElevation(a.elev_min_m)} - High {fmtElevation(a.elev_max_m)} -
-
+

Elevation profile

+
+ +
+ Low {fmtElevation(a.elev_min_m)} + High {fmtElevation(a.elev_max_m)} +
+
{/if} {#if data.timed.length > 2} -

Speed

-
- -
+

Speed

+
+ +
{/if} diff --git a/src/routes/peaks/+page.svelte b/src/routes/peaks/+page.svelte index 2ab12ec..4b3df0e 100644 --- a/src/routes/peaks/+page.svelte +++ b/src/routes/peaks/+page.svelte @@ -158,7 +158,7 @@ Zoom in to reveal less prominent summits; click a peak to cross it off.

- +