feat(timeline): render a same-year curated event as its cluster card header
A curated event with letters in its own band now becomes the contained card header (glyph, title, date, provenance, edit pencil) instead of a separate floating pill — the title reads once. Derived life-events, world-bands, and letterless event pills are unchanged (REQ-001 amended for curated-with-letters; the identity fixture now links its letter to the curated event so the letterless world band stays a band). Refs #827
This commit is contained in:
@@ -41,6 +41,7 @@ let {
|
||||
|
||||
type Row =
|
||||
| { t: 'event'; entry: TimelineEntryDTO }
|
||||
| { t: 'eventcard'; entry: TimelineEntryDTO; bucket: LetterBucketModel }
|
||||
| { t: 'letter'; entry: TimelineEntryDTO; side: 'left' | 'right' }
|
||||
| { t: 'strip' }
|
||||
| { t: 'bucket'; bucket: LetterBucketModel; nested: boolean };
|
||||
@@ -52,26 +53,30 @@ const bucketMode = $derived(groupingMode === 'thema' ? 'thema' : 'event');
|
||||
const rows = $derived.by<Row[]>(() => {
|
||||
const out: Row[] = [];
|
||||
|
||||
// Ereignis: events stay on the axis (REQ-001); each curated event's letters nest directly
|
||||
// beneath its pill — the pill IS the header, so the title is never repeated. A cluster whose
|
||||
// pill lives in another year band (or was filtered out) keeps its own header here, and the
|
||||
// unlinked letters fall to the single "Weitere Briefe" bucket (REQ-003/006/019).
|
||||
// Ereignis: events stay on the axis (REQ-001). A curated event WITH letters in this band
|
||||
// becomes the contained card's header (no separate pill — its title reads once, #827
|
||||
// redesign); a letterless/derived/world event stays a plain pill/band. A cluster whose event
|
||||
// lives in another year band (or was filtered out) renders as a text-header card here, and
|
||||
// the unlinked letters fall to the single "Weitere Briefe" drawer (REQ-003/006/019).
|
||||
if (groupingMode === 'event') {
|
||||
const buckets = bucketLetters(letters, 'event', eventLookup);
|
||||
const hasPill = (bucketKey: string) =>
|
||||
year.entries.some((e) => e.kind === 'EVENT' && `event:${e.eventId}` === bucketKey);
|
||||
// Each pill renders, then its same-year cluster nests directly beneath it (no header).
|
||||
const sameYearBucket = (id: string | undefined) =>
|
||||
id ? buckets.find((b) => b.kind === 'event' && b.key === `event:${id}`) : undefined;
|
||||
for (const entry of year.entries) {
|
||||
if (entry.kind !== 'EVENT') continue;
|
||||
out.push({ t: 'event', entry });
|
||||
const bucket = entry.eventId
|
||||
? buckets.find((b) => b.kind === 'event' && b.key === `event:${entry.eventId}`)
|
||||
: undefined;
|
||||
if (bucket) out.push({ t: 'bucket', bucket, nested: true });
|
||||
const bucket = sameYearBucket(entry.eventId);
|
||||
// A curated event with same-year letters becomes the card header (card replaces pill);
|
||||
// otherwise it stays a plain pill/world-band.
|
||||
if (bucket) out.push({ t: 'eventcard', entry, bucket });
|
||||
else out.push({ t: 'event', entry });
|
||||
}
|
||||
// Clusters whose pill is in another band keep their header; then the fallback, last.
|
||||
// Cross-year clusters (no matching event entry in this band) and the fallback drawer
|
||||
// render after the axis entries, with their own text header.
|
||||
for (const bucket of buckets) {
|
||||
if (bucket.kind === 'fallback' || !hasPill(bucket.key)) {
|
||||
if (
|
||||
bucket.kind === 'fallback' ||
|
||||
!year.entries.some((e) => e.kind === 'EVENT' && `event:${e.eventId}` === bucket.key)
|
||||
) {
|
||||
out.push({ t: 'bucket', bucket, nested: false });
|
||||
}
|
||||
}
|
||||
@@ -131,6 +136,14 @@ function rowKey(row: Row): string {
|
||||
{:else}
|
||||
<EventPill entry={row.entry} canWrite={canWrite} />
|
||||
{/if}
|
||||
{:else if row.t === 'eventcard'}
|
||||
<LetterBucket
|
||||
bucket={row.bucket}
|
||||
mode="event"
|
||||
year={year.year}
|
||||
event={row.entry}
|
||||
canWrite={canWrite}
|
||||
/>
|
||||
{:else if row.t === 'letter'}
|
||||
<div class="letter-row" data-side={row.side}>
|
||||
<span data-testid="letter-dot" class="letter-dot bg-surface" aria-hidden="true"></span>
|
||||
|
||||
Reference in New Issue
Block a user