isDense(count) thresholds dense year bands at >12 letters (REQ-012); monthHistogram(letters, year) buckets a band's letters into exactly 12 month buckets via the shared fillDensityGaps, counting each letter on its eventDate anchor month and ignoring undated entries (REQ-027). Imports shared only. Refs #779 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
33 lines
1.3 KiB
TypeScript
33 lines
1.3 KiB
TypeScript
import type { components } from '$lib/generated/api';
|
|
import { fillDensityGaps, type MonthBucket } from '$lib/shared/utils/monthBuckets';
|
|
|
|
type TimelineEntryDTO = components['schemas']['TimelineEntryDTO'];
|
|
|
|
/**
|
|
* A year band with more letters than this renders as a compact density strip
|
|
* (count + 12-month sparkline) instead of one card per letter (REQ-012).
|
|
*/
|
|
export const DENSE_THRESHOLD = 12;
|
|
|
|
export function isDense(letterCount: number): boolean {
|
|
return letterCount > DENSE_THRESHOLD;
|
|
}
|
|
|
|
/**
|
|
* Buckets a band's letters into exactly 12 month buckets (`{year}-01`..`{year}-12`)
|
|
* for the density sparkline. Each letter counts on its `eventDate` month; coarser
|
|
* precisions (SEASON/YEAR/APPROX) count on whatever anchor month the backend put
|
|
* in `eventDate`. Entries without an `eventDate` (e.g. UNKNOWN) are ignored — they
|
|
* live in the "Ohne Datum" bucket, not a dated band. (REQ-027)
|
|
*/
|
|
export function monthHistogram(letters: TimelineEntryDTO[], year: number): MonthBucket[] {
|
|
const counts = new Map<string, number>();
|
|
for (const l of letters) {
|
|
if (!l.eventDate) continue;
|
|
const month = l.eventDate.slice(0, 7); // YYYY-MM
|
|
counts.set(month, (counts.get(month) ?? 0) + 1);
|
|
}
|
|
const buckets = Array.from(counts.entries()).map(([month, count]) => ({ month, count }));
|
|
return fillDensityGaps(buckets, `${year}-01-01`, `${year}-12-31`);
|
|
}
|