feat(timeline): global /zeitstrahl timeline (Concept A) — #779 #831

Merged
marcel merged 22 commits from feat/issue-779-zeitstrahl into main 2026-06-13 21:56:46 +02:00
2 changed files with 24 additions and 10 deletions
Showing only changes of commit 7902f4e6ac - Show all commits

View File

@@ -4,6 +4,7 @@ import WorldBand from './WorldBand.svelte';
import LetterCard from './LetterCard.svelte';
import YearLetterStrip from './YearLetterStrip.svelte';
import { isDense } from './timelineDensity';
import { entryKey } from './entryKey';
import type { components } from '$lib/generated/api';
type TimelineYearDTO = components['schemas']['TimelineYearDTO'];
@@ -42,16 +43,6 @@ const rows = $derived.by<Row[]>(() => {
}
return out;
});
function entryKey(entry: TimelineEntryDTO): string {
return (
entry.kind +
':' +
(entry.eventId ??
entry.documentId ??
`${entry.derivedType}:${(entry.linkedPersonIds ?? []).join('-')}`)
);
}
</script>
<section class="py-2">

View File

@@ -0,0 +1,23 @@
import type { components } from '$lib/generated/api';
type TimelineEntryDTO = components['schemas']['TimelineEntryDTO'];
/**
* Stable `{#each}` key for a timeline entry. Prefers the entry's own identity
* (`eventId` for curated events, `documentId` for letters); derived life-events
* carry neither, so they key on `derivedType` + their linked person ids — which
* keeps two derived births in the same year distinct. The `kind` prefix keeps an
* event and a letter that happen to share an id from colliding.
*
* Used by both `YearBand` (per-band rows) and `TimelineView` (the undated
* bucket), where entries can be events without a `documentId`.
*/
export function entryKey(entry: TimelineEntryDTO): string {
return (
entry.kind +
':' +
(entry.eventId ??
entry.documentId ??
`${entry.derivedType}:${(entry.linkedPersonIds ?? []).join('-')}`)
);
}