Files
familienarchiv/frontend/src/lib/person/genealogy/StammbaumBottomSheet.svelte
Marcel 1dffb430ac feat(stammbaum): centre-on-person control in the panel title row (#692)
Add an onCentre control to StammbaumSidePanel (title row, both desktop aside
and mobile sheet). The page drives a one-shot centreOnId so StammbaumTree
recentres the canvas on the focal node (US-PAN-005). Also tighten the panel
spec's deathYear fixture to a valid type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 17:10:49 +02:00

76 lines
2.2 KiB
Svelte

<script lang="ts">
import { m } from '$lib/paraglide/messages.js';
import { trapFocus } from '$lib/shared/actions/trapFocus';
import StammbaumSidePanel from '$lib/person/genealogy/StammbaumSidePanel.svelte';
import type { components } from '$lib/generated/api';
type PersonNodeDTO = components['schemas']['PersonNodeDTO'];
interface Props {
node: PersonNodeDTO;
canWrite: boolean;
onClose: () => void;
onCentre?: () => void;
}
let { node, canWrite, onClose, onCentre }: Props = $props();
// Swipe the sheet down past this threshold to dismiss it (Leonie).
const SWIPE_DISMISS_PX = 80;
let dragY = $state(0);
let dragging = false;
let startY = 0;
function onHandleDown(event: PointerEvent) {
dragging = true;
startY = event.clientY;
(event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId);
}
function onHandleMove(event: PointerEvent) {
if (dragging) dragY = Math.max(0, event.clientY - startY);
}
function onHandleUp() {
if (!dragging) return;
dragging = false;
if (dragY >= SWIPE_DISMISS_PX) onClose();
dragY = 0;
}
function onKeydown(event: KeyboardEvent) {
if (event.key === 'Escape') onClose();
}
</script>
<!-- Backdrop: a full-screen button so tap-outside dismiss is keyboard- and
screen-reader-accessible without a static-element click handler. -->
<button
type="button"
class="fixed inset-0 z-30 bg-black/30 md:hidden"
aria-label={m.stammbaum_close_panel()}
onclick={onClose}
></button>
<div
role="dialog"
aria-modal="true"
aria-label={node.displayName}
class="fixed inset-x-0 bottom-0 z-40 max-h-[60dvh] overflow-y-auto rounded-t-xl border-t border-line bg-surface shadow-lg md:hidden"
style="transform: translateY({dragY}px);"
use:trapFocus
onkeydown={onKeydown}
>
<!-- Drag handle grip — swipe down to dismiss. -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="flex cursor-grab justify-center py-2 active:cursor-grabbing"
onpointerdown={onHandleDown}
onpointermove={onHandleMove}
onpointerup={onHandleUp}
onpointercancel={onHandleUp}
>
<div class="h-1 w-10 rounded-full bg-line" aria-hidden="true"></div>
</div>
<StammbaumSidePanel node={node} canWrite={canWrite} onClose={onClose} onCentre={onCentre} />
</div>