Raise chip labels from 10px to 12px (text-xs) in StammbaumCard, StammbaumSidePanel and StammbaumTree SVG text. Widen zoom buttons from 32px to 44px for senior-audience touch targets. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
119 lines
3.5 KiB
Svelte
119 lines
3.5 KiB
Svelte
<script lang="ts">
|
||
import { m } from '$lib/paraglide/messages.js';
|
||
import { page } from '$app/state';
|
||
import StammbaumTree from '$lib/components/StammbaumTree.svelte';
|
||
import StammbaumSidePanel from '$lib/components/StammbaumSidePanel.svelte';
|
||
import type { components } from '$lib/generated/api';
|
||
|
||
type PersonNodeDTO = components['schemas']['PersonNodeDTO'];
|
||
type RelationshipDTO = components['schemas']['RelationshipDTO'];
|
||
|
||
interface Props {
|
||
data: { nodes: PersonNodeDTO[]; edges: RelationshipDTO[] };
|
||
}
|
||
|
||
let { data }: Props = $props();
|
||
|
||
const focusId = $derived(page.url.searchParams.get('focus'));
|
||
const canWrite = $derived<boolean>(page.data.canWrite ?? false);
|
||
|
||
let selectedId = $state<string | null>(null);
|
||
$effect(() => {
|
||
if (focusId && data.nodes.some((n) => n.id === focusId)) {
|
||
selectedId = focusId;
|
||
}
|
||
});
|
||
|
||
const selectedNode = $derived(data.nodes.find((n) => n.id === selectedId) ?? null);
|
||
|
||
let zoom = $state(1);
|
||
function zoomIn() {
|
||
zoom = Math.min(2, zoom + 0.1);
|
||
}
|
||
function zoomOut() {
|
||
zoom = Math.max(0.4, zoom - 0.1);
|
||
}
|
||
</script>
|
||
|
||
<!-- 4.25rem = 4rem navbar (h-16) + 0.25rem accent strip (h-1).
|
||
-my-6 cancels <main>'s py-6 so the canvas sits flush against the navbar
|
||
on top and the viewport edge on the bottom. -->
|
||
<div class="-my-6 flex h-[calc(100dvh-4.25rem)] flex-col">
|
||
<header
|
||
class="flex shrink-0 items-center justify-between border-b border-line bg-surface px-6 py-4"
|
||
>
|
||
<h1 class="font-serif text-2xl text-ink">{m.nav_stammbaum()}</h1>
|
||
{#if data.nodes.length > 0}
|
||
<div class="flex items-center gap-2">
|
||
<button
|
||
type="button"
|
||
onclick={zoomOut}
|
||
aria-label={m.stammbaum_zoom_out()}
|
||
class="inline-flex h-11 w-11 items-center justify-center rounded-sm border border-line bg-surface text-ink-2 transition hover:bg-muted"
|
||
>
|
||
−
|
||
</button>
|
||
<button
|
||
type="button"
|
||
onclick={zoomIn}
|
||
aria-label={m.stammbaum_zoom_in()}
|
||
class="inline-flex h-11 w-11 items-center justify-center rounded-sm border border-line bg-surface text-ink-2 transition hover:bg-muted"
|
||
>
|
||
+
|
||
</button>
|
||
</div>
|
||
{/if}
|
||
</header>
|
||
|
||
{#if data.nodes.length === 0}
|
||
<div class="flex flex-1 items-center justify-center p-8">
|
||
<div
|
||
class="mx-auto max-w-md rounded-sm border border-line bg-surface p-10 text-center shadow-sm"
|
||
>
|
||
<svg
|
||
class="mx-auto mb-4 h-12 w-12 text-ink-3"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="1.5"
|
||
aria-hidden="true"
|
||
>
|
||
<circle cx="12" cy="5" r="2.5" />
|
||
<circle cx="6" cy="14" r="2.5" />
|
||
<circle cx="18" cy="14" r="2.5" />
|
||
<path stroke-linecap="round" d="M12 7.5v3M9.5 12.5L9 14M14.5 12.5l.5 1.5" />
|
||
</svg>
|
||
<h2 class="mb-2 font-serif text-xl text-ink">{m.stammbaum_empty_heading()}</h2>
|
||
<p class="mb-4 font-serif text-sm text-ink-2">{m.stammbaum_empty_body()}</p>
|
||
<a
|
||
href="/persons"
|
||
class="inline-block font-sans text-sm font-medium text-primary hover:underline"
|
||
>
|
||
{m.stammbaum_empty_link()}
|
||
</a>
|
||
</div>
|
||
</div>
|
||
{:else}
|
||
<div class="flex flex-1 overflow-hidden">
|
||
<div class="flex-1 overflow-auto bg-muted/20">
|
||
<StammbaumTree
|
||
nodes={data.nodes}
|
||
edges={data.edges}
|
||
selectedId={selectedId}
|
||
zoom={zoom}
|
||
onSelect={(id) => (selectedId = id)}
|
||
/>
|
||
</div>
|
||
{#if selectedNode}
|
||
<aside class="w-[320px] shrink-0 overflow-y-auto border-l border-line bg-surface">
|
||
<StammbaumSidePanel
|
||
node={selectedNode}
|
||
canWrite={canWrite}
|
||
onClose={() => (selectedId = null)}
|
||
/>
|
||
</aside>
|
||
{/if}
|
||
</div>
|
||
{/if}
|
||
</div>
|