refactor(frontend): share formatDatePart between life dates and relationships

formatLifeDate and the relationship formatEnd were the same nullable-date →
formatDocumentDate delegation (YEAR fallback, '' on null). Hoist that core into
formatDatePart in documentDate.ts; formatLifeDate becomes a thin glyph-free
alias and formatRelationshipDateRange calls the shared helper directly. The two
range composers stay separate — they genuinely differ (*/† glyphs vs leading
dash). relationshipDates, personLifeDates and documentDate specs (60) stay green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-14 20:21:21 +02:00
parent a3fd886711
commit 063d1aac55
3 changed files with 30 additions and 32 deletions

View File

@@ -1,23 +1,17 @@
import { formatDocumentDate, type DatePrecision } from '$lib/shared/utils/documentDate'; import { formatDatePart, type DatePrecision } from '$lib/shared/utils/documentDate';
/** /**
* Formats one life date (birth or death) at the precision the data claims, * Formats one life date (birth or death) at the precision the data claims.
* delegating all rendering to {@link formatDocumentDate}. Returns '' for a * Thin domain alias over the shared {@link formatDatePart}: carries no * / †
* missing date. Carries no * / † glyph — components that need the glyphs wrap * glyph — components that need the glyphs wrap them in their own `aria-hidden`
* them in their own `aria-hidden` markup so screen readers only hear the date. * markup so screen readers only hear the date.
*
* A missing precision falls back to YEAR: pre-V76 rows only knew a year, and
* a bare year is the only safe rendering for a date without precision metadata.
*/ */
export function formatLifeDate( export function formatLifeDate(
date: string | null | undefined, date: string | null | undefined,
precision: DatePrecision | null | undefined, precision: DatePrecision | null | undefined,
locale?: string locale?: string
): string { ): string {
if (!date) { return formatDatePart(date, precision, locale);
return '';
}
return formatDocumentDate(date, precision ?? 'YEAR', null, null, locale);
} }
/** /**

View File

@@ -1,21 +1,4 @@
import { formatDocumentDate, type DatePrecision } from '$lib/shared/utils/documentDate'; import { formatDatePart, type DatePrecision } from '$lib/shared/utils/documentDate';
/**
* Formats one relationship endpoint (start or end) at the precision the data
* claims, delegating all rendering to {@link formatDocumentDate}. Returns '' for
* a missing date. A missing precision falls back to YEAR — pre-V78 rows only knew
* a year. Mirrors {@link formatLifeDate} for the person life-date pattern.
*/
function formatEnd(
date: string | null | undefined,
precision: DatePrecision | null | undefined,
locale?: string
): string {
if (!date) {
return '';
}
return formatDocumentDate(date, precision ?? 'YEAR', null, null, locale);
}
/** /**
* Formats a relationship's startend range as plain text, e.g. for a marriage row. * Formats a relationship's startend range as plain text, e.g. for a marriage row.
@@ -32,8 +15,8 @@ export function formatRelationshipDateRange(
toDatePrecision: DatePrecision | null | undefined, toDatePrecision: DatePrecision | null | undefined,
locale?: string locale?: string
): string { ): string {
const from = formatEnd(fromDate, fromDatePrecision, locale); const from = formatDatePart(fromDate, fromDatePrecision, locale);
const to = formatEnd(toDate, toDatePrecision, locale); const to = formatDatePart(toDate, toDatePrecision, locale);
if (from && to) { if (from && to) {
return `${from} ${to}`; return `${from} ${to}`;
} }

View File

@@ -66,6 +66,27 @@ export function formatDocumentDate(
} }
} }
/**
* Formats one nullable date at the precision the data claims, delegating all
* rendering to {@link formatDocumentDate}. Returns '' for a missing date; a
* missing precision falls back to YEAR — pre-precision rows knew only a year,
* and a bare year is the only safe rendering without precision metadata.
*
* This is the shared core of {@link formatLifeDate} (person birth/death) and the
* relationship from/to formatter. Range-level glyphs and dashes belong in those
* domain wrappers, never here.
*/
export function formatDatePart(
date: string | null | undefined,
precision: DatePrecision | null | undefined,
locale?: string
): string {
if (!date) {
return '';
}
return formatDocumentDate(date, precision ?? 'YEAR', null, null, locale);
}
// ─── precision branches ────────────────────────────────────────────────────── // ─── precision branches ──────────────────────────────────────────────────────
function longDate(iso: string, locale: string): string { function longDate(iso: string, locale: string): string {