feat(topbar): add expandable metadata drawer with Details toggle (#175)

- DocumentMetadataDrawer: 3-column grid (≥1024px), single-column mobile
  Shows document date, location, status, person cards, tag chips
  Person names link to /persons/{id}, tags link to filtered search
  Empty states for missing persons/tags, receiver cap with expand button
- DocumentTopBar: "Details" toggle button with animated SVG chevron
  44×44px tap target, aria-expanded, Svelte slide transition
  Semantic color tokens for dark mode compatibility
- Remove DocumentBottomPanel from document detail page
  Bottom panel replaced by topbar drawer for metadata access
  Simplify +page.server.ts (remove comments loading)
  Update page.server.spec.ts for new load signature

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-05 11:22:38 +02:00
parent 234f83c40b
commit 5211e0b9f7
6 changed files with 197 additions and 152 deletions

View File

@@ -3,9 +3,7 @@ import { onMount } from 'svelte';
import { page } from '$app/state';
import DocumentTopBar from '$lib/components/DocumentTopBar.svelte';
import DocumentViewer from '$lib/components/DocumentViewer.svelte';
import DocumentBottomPanel from '$lib/components/DocumentBottomPanel.svelte';
import AnnotationSidePanel from '$lib/components/AnnotationSidePanel.svelte';
import type { DocumentPanelTab } from '$lib/types';
let { data } = $props();
@@ -62,38 +60,17 @@ let annotateMode = $state(false);
let activeAnnotationId = $state<string | null>(null);
let activeAnnotationPage = $state<number | null>(null);
// Close the panel when entering annotate mode so the PDF is fully visible.
$effect(() => {
if (annotateMode) panelOpen = false;
});
// ── Navigation / init ─────────────────────────────────────────────────────────
// ── Bottom panel state ────────────────────────────────────────────────────────
let panelOpen = $state(false);
let panelHeight = $state(0); // set to full height on mount
let navHeight = $state(0);
let activeTab = $state<DocumentPanelTab>('metadata');
onMount(() => {
navHeight = document.querySelector('header')?.getBoundingClientRect().height ?? 0;
const topbar = document.querySelector('[data-topbar]');
panelHeight = window.innerHeight - navHeight - (topbar?.getBoundingClientRect().height ?? 0);
if (targetAnnotationId) {
// Deep-link into an annotation comment: open the side panel
activeAnnotationId = targetAnnotationId;
} else if (targetCommentId) {
// Deep-link into a document-level comment: open discussion tab
panelOpen = true;
activeTab = 'discussion';
} else if (!doc?.filePath) {
// No file yet — open to metadata so the panel is immediately useful.
panelOpen = true;
activeTab = 'metadata';
}
// Track last-visited document for the dashboard resume strip
if (doc?.id) {
localStorage.setItem(
'familienarchiv.lastVisited',
@@ -106,8 +83,6 @@ onMount(() => {
if (activeAnnotationId) {
activeAnnotationId = null;
activeAnnotationPage = null;
} else if (panelOpen) {
panelOpen = false;
}
}
}
@@ -160,16 +135,4 @@ onMount(() => {
}}
/>
</div>
<DocumentBottomPanel
doc={doc}
comments={(data.comments ?? []) as never[]}
canComment={canComment}
currentUserId={currentUserId}
canAdmin={canAdmin}
targetCommentId={targetCommentId}
bind:open={panelOpen}
bind:height={panelHeight}
bind:activeTab={activeTab}
/>
</div>