feat(stammbaum): first-load touch affordance hint (#692)

Add StammbaumAffordance: a touch-only "drag to explore · pinch to zoom" hint
that auto-dismisses on the first canvas pointer interaction (wired via the
gesture action's onGestureStart) or the explicit close, and stays dismissed for
30 days via a localStorage timestamp (boolean gate only, never rendered).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-29 17:13:36 +02:00
parent 1dffb430ac
commit 11b70d814f
4 changed files with 133 additions and 1 deletions

View File

@@ -7,6 +7,7 @@ import StammbaumTree from '$lib/person/genealogy/StammbaumTree.svelte';
import StammbaumSidePanel from '$lib/person/genealogy/StammbaumSidePanel.svelte';
import StammbaumBottomSheet from '$lib/person/genealogy/StammbaumBottomSheet.svelte';
import StammbaumControls from '$lib/person/genealogy/StammbaumControls.svelte';
import StammbaumAffordance from '$lib/person/genealogy/StammbaumAffordance.svelte';
import {
type PanZoomState,
DEFAULT_VIEW,
@@ -36,6 +37,7 @@ let selectedId = $state<string | null>(
const selectedNode = $derived(data.nodes.find((n) => n.id === selectedId) ?? null);
let view = $state<PanZoomState>(data.initialView);
let canvasActivity = $state(false);
function zoomIn() {
view = { ...view, z: clampZoom(view.z + ZOOM_STEP_KB) };
}
@@ -124,8 +126,10 @@ $effect(() => {
panZoom={view}
centreOnId={centreOnId}
onPanZoom={(v) => (view = v)}
onActivity={() => (canvasActivity = true)}
onSelect={(id) => (selectedId = id)}
/>
<StammbaumAffordance dismissed={canvasActivity} />
<StammbaumControls onZoomIn={zoomIn} onZoomOut={zoomOut} onFit={fitToScreen} />
</div>
{#if selectedNode}