@synapxlab/ladder-timeline 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ladder-timeline.umd.cjs","sources":["../src/components/ladder-timeline/LadderTimeline.utils.ts","../src/components/ladder-timeline/LadderTimeline.events.ts","../src/components/ladder-timeline/Scale.ts","../src/components/ladder-timeline/LadderTimeline.ts","../src/adapters/FullCalendarAdapter.ts"],"sourcesContent":["import type { WeekItem, WeekRange } from './LadderTimeline.types';\n\n// ─── Basic date operations ────────────────────────────────────────────────────\n\n/** Return a new Date stripped to midnight UTC-local */\nexport function normalizeDate(d: Date): Date {\n const n = new Date(d.getFullYear(), d.getMonth(), d.getDate());\n return n;\n}\n\n/** Clone a Date instance */\nexport function cloneDate(d: Date): Date {\n return new Date(d.getTime());\n}\n\n/** Add (or subtract) N days */\nexport function addDays(d: Date, days: number): Date {\n const result = cloneDate(d);\n result.setDate(result.getDate() + days);\n return result;\n}\n\n/** Add (or subtract) N weeks */\nexport function addWeeks(d: Date, weeks: number): Date {\n return addDays(d, weeks * 7);\n}\n\n// ─── Week boundary calculation ────────────────────────────────────────────────\n\n/**\n * Return Monday (firstDayOfWeek=1) or Sunday (firstDayOfWeek=0)\n * of the week containing `d`.\n */\nexport function getWeekStart(d: Date, firstDayOfWeek: 0 | 1 = 1): Date {\n const normalized = normalizeDate(d);\n const day = normalized.getDay(); // 0=Sun … 6=Sat\n const diff = (day - firstDayOfWeek + 7) % 7;\n return addDays(normalized, -diff);\n}\n\n/** Return the last day of the week (6 days after start) */\nexport function getWeekEnd(weekStart: Date): Date {\n return addDays(weekStart, 6);\n}\n\n/** ISO-8601 week number */\nexport function getISOWeekNumber(d: Date): number {\n const date = normalizeDate(d);\n // Thursday of current week → always in the same ISO year\n date.setDate(date.getDate() + 4 - (date.getDay() || 7));\n const yearStart = new Date(date.getFullYear(), 0, 1);\n return Math.ceil(((date.getTime() - yearStart.getTime()) / 86_400_000 + 1) / 7);\n}\n\n// ─── Comparison helpers ───────────────────────────────────────────────────────\n\n/** True if two dates fall on the same calendar day */\nexport function isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\n/** True if two dates fall in the same ISO week */\nexport function isSameWeek(a: Date, b: Date, firstDayOfWeek: 0 | 1 = 1): boolean {\n const startA = getWeekStart(a, firstDayOfWeek);\n const startB = getWeekStart(b, firstDayOfWeek);\n return isSameDay(startA, startB);\n}\n\n/** Numeric comparison: –1, 0, 1 */\nexport function compareDate(a: Date, b: Date): -1 | 0 | 1 {\n const na = normalizeDate(a).getTime();\n const nb = normalizeDate(b).getTime();\n if (na < nb) return -1;\n if (na > nb) return 1;\n return 0;\n}\n\n// ─── Formatting ───────────────────────────────────────────────────────────────\n\n/**\n * Format a week date range using Intl.DateTimeFormat.formatRange when available.\n * Same month → \"18 – 24 août\" / \"Aug 18 – 24\" (month shown once)\n * Cross-month → \"28 mars – 3 avr.\" / \"Mar 28 – Apr 3\"\n */\nexport function formatWeekLabel(range: WeekRange, locale: string): string {\n const opts: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'short' };\n const fmt = new Intl.DateTimeFormat(locale, opts);\n\n // formatRange est supporté par tous les navigateurs evergreen (Chrome 76+, FF 91+, Safari 14.1+)\n // Les types TS de lib DOM n'incluent pas encore formatRange → cast explicite\n const fmtAny = fmt as unknown as { formatRange?: (s: Date, e: Date) => string };\n if (typeof fmtAny.formatRange === 'function') {\n return fmtAny.formatRange(range.start, range.end);\n }\n\n // Fallback : afficher les deux dates complètes\n return `${fmt.format(range.start)} – ${fmt.format(range.end)}`;\n}\n\n/** \"W14 · 2025\" */\nexport function formatWeekSublabel(weekNumber: number, year: number): string {\n return `S${weekNumber} · ${year}`;\n}\n\n// ─── Week list builder ────────────────────────────────────────────────────────\n\n/**\n * Generate an array of WeekItems centred on `referenceDate`.\n *\n * @param referenceDate Centre of the window\n * @param selectedDate Currently selected date\n * @param weeksVisible Total weeks to generate (should be odd for centring)\n * @param firstDayOfWeek 0 = Sun, 1 = Mon\n * @param locale BCP-47 locale\n */\nexport function buildWeekList(\n referenceDate: Date,\n selectedDate: Date,\n weeksVisible: number,\n firstDayOfWeek: 0 | 1,\n locale: string,\n): WeekItem[] {\n const today = normalizeDate(new Date());\n const refStart = getWeekStart(referenceDate, firstDayOfWeek);\n const half = Math.floor(weeksVisible / 2);\n\n const items: WeekItem[] = [];\n\n for (let offset = -half; offset <= half; offset++) {\n const start = addWeeks(refStart, offset);\n const end = getWeekEnd(start);\n const range: WeekRange = { start, end };\n const weekNumber = getISOWeekNumber(start);\n const year = start.getFullYear();\n\n items.push({\n weekNumber,\n year,\n range,\n label: formatWeekLabel(range, locale),\n sublabel: formatWeekSublabel(weekNumber, year),\n isCurrent: isSameWeek(today, start, firstDayOfWeek),\n isSelected: isSameWeek(selectedDate, start, firstDayOfWeek),\n });\n }\n\n return items;\n}\n\n// ─── Validation ───────────────────────────────────────────────────────────────\n\nexport function isValidDate(d: unknown): d is Date {\n return d instanceof Date && !isNaN(d.getTime());\n}\n","import type {\n LadderTimelineSelectEventDetail,\n LadderTimelineNavigateEventDetail,\n} from './LadderTimeline.types';\n\n// ─── Typed custom events ──────────────────────────────────────────────────────\n\n/** Fired when the user selects a week (click, keyboard) */\nexport type WeekChangeEvent = CustomEvent<LadderTimelineSelectEventDetail>;\n\n/** Fired when prev / next / today navigation occurs */\nexport type WeekNavigateEvent = CustomEvent<LadderTimelineNavigateEventDetail>;\n\n// ─── Event name constants ─────────────────────────────────────────────────────\n\nexport const EVENT_WEEK_CHANGE = 'weekchange' as const;\nexport const EVENT_NAVIGATE = 'navigate' as const;\nexport const EVENT_SELECT = 'select' as const;\n\n// ─── Factory helpers ──────────────────────────────────────────────────────────\n\nexport function createWeekChangeEvent(\n detail: LadderTimelineSelectEventDetail,\n): WeekChangeEvent {\n return new CustomEvent<LadderTimelineSelectEventDetail>(EVENT_WEEK_CHANGE, {\n detail,\n bubbles: true,\n composed: true,\n });\n}\n\nexport function createSelectEvent(\n detail: LadderTimelineSelectEventDetail,\n): CustomEvent<LadderTimelineSelectEventDetail> {\n return new CustomEvent<LadderTimelineSelectEventDetail>(EVENT_SELECT, {\n detail,\n bubbles: true,\n composed: true,\n });\n}\n\nexport function createNavigateEvent(\n detail: LadderTimelineNavigateEventDetail,\n): WeekNavigateEvent {\n return new CustomEvent<LadderTimelineNavigateEventDetail>(EVENT_NAVIGATE, {\n detail,\n bubbles: true,\n composed: true,\n });\n}\n\n// ─── Typed addEventListener helper ────────────────────────────────────────────\n\nexport function onWeekChange(\n target: EventTarget,\n handler: (e: WeekChangeEvent) => void,\n): () => void {\n const listener = (e: Event) => handler(e as WeekChangeEvent);\n target.addEventListener(EVENT_WEEK_CHANGE, listener);\n return () => target.removeEventListener(EVENT_WEEK_CHANGE, listener);\n}\n\nexport function onNavigate(\n target: EventTarget,\n handler: (e: WeekNavigateEvent) => void,\n): () => void {\n const listener = (e: Event) => handler(e as WeekNavigateEvent);\n target.addEventListener(EVENT_NAVIGATE, listener);\n return () => target.removeEventListener(EVENT_NAVIGATE, listener);\n}\n","/**\n * Scale system — 18 échelles temporelles, du milliard d'années à la nanoseconde.\n *\n * Cursor interne unifié = `decimal year` (float64). Chaque Scale fournit :\n * - step : pas entre deux items (en années décimales)\n * - snap(year) : arrondit à la frontière d'unité de cette échelle\n * - build(...) : produit N items autour du curseur\n *\n * Pour les très grandes échelles (>= ka), on travaille uniquement sur le `year`\n * numérique (Date ne supporte pas au-delà de ±271 821 ans depuis 1970).\n * Pour les échelles fines (< seconde), la précision float64 ne suffit pas pour\n * un timestamp absolu — les labels deviennent décoratifs.\n */\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type ScaleId =\n | 'Ga' | '100Ma' | 'Ma' | '100ka' | '10ka'\n | 'millennium' | 'century' | 'decade'\n | 'year' | 'month' | 'week' | 'day'\n | 'hour' | 'minute' | 'second'\n | 'ms';\n\nexport interface ScaleItem {\n /** Identité canonique de l'item — décimal year de son début */\n year: number;\n /** Label principal affiché sur l'item */\n label: string;\n /** Label secondaire (S14 · 2025, 14:00…) */\n sublabel: string;\n /** Header optionnel affiché sur l'item sélectionné ou aux transitions */\n header: string;\n /** Label court pour le mode d'affichage `compact` (ex: \"·10\" pour 1810 à l'échelle décennie) */\n compactLabel: string;\n /** Marqueur \"aujourd'hui\" (ou présent pour macro) */\n isCurrent: boolean;\n /** Marqueur \"sélectionné\" */\n isSelected: boolean;\n}\n\nexport interface Scale {\n id: ScaleId;\n label: string;\n /** Pas (en années décimales) entre deux items adjacents */\n step: number;\n /** Snap d'un year libre à la frontière d'unité de l'échelle */\n snap(year: number): number;\n /** Décale le year de n unités */\n add(year: number, n: number): number;\n /** Construit `count` items autour de `centerYear` */\n build(centerYear: number, count: number, selectedYear: number, locale: string): ScaleItem[];\n}\n\n// ─── Helpers Date <-> decimal year ────────────────────────────────────────────\n\nconst DATE_MIN_YEAR = -271820;\nconst DATE_MAX_YEAR = 275759;\n\nfunction inDateRange(year: number): boolean {\n return year >= DATE_MIN_YEAR && year <= DATE_MAX_YEAR;\n}\n\nfunction yearToDate(year: number): Date {\n const y = Math.floor(year);\n const frac = year - y;\n const startOfY = new Date(y, 0, 1).getTime();\n const startOfNextY = new Date(y + 1, 0, 1).getTime();\n // Math.round évite la dérive sub-ms qui peut basculer sur le jour précédent\n // après un roundtrip dateToYear → yearToDate.\n return new Date(Math.round(startOfY + frac * (startOfNextY - startOfY)));\n}\n\nfunction dateToYear(d: Date): number {\n const y = d.getFullYear();\n const startOfY = new Date(y, 0, 1).getTime();\n const startOfNextY = new Date(y + 1, 0, 1).getTime();\n return y + (d.getTime() - startOfY) / (startOfNextY - startOfY);\n}\n\nfunction todayYear(): number {\n return dateToYear(new Date());\n}\n\n// ─── Helpers formatage scientifique grandes années ────────────────────────────\n\nfunction fmtBigYear(year: number): string {\n const abs = Math.abs(year);\n if (abs >= 1e9) return `${(year / 1e9).toFixed(2)} Ga`;\n if (abs >= 1e6) return `${(year / 1e6).toFixed(2)} Ma`;\n if (abs >= 1000) return `${(year / 1000).toFixed(1)} ka`;\n return year < 0 ? `${Math.round(year)}` : `+${Math.round(year)}`;\n}\n\nfunction fmtYearShort(year: number): string {\n const y = Math.round(year);\n if (y < 0) return `${y} av.`;\n return `${y}`;\n}\n\n// ─── Macro scales (Ga, 100Ma, Ma, 100ka, 10ka) ────────────────────────────────\n\nfunction macroScale(id: ScaleId, label: string, step: number): Scale {\n return {\n id, label, step,\n snap(year) { return Math.round(year / step) * step; },\n add(year, n) { return year + n * step; },\n build(centerYear, count, selectedYear, _locale) {\n const half = Math.floor(count / 2);\n const start = this.snap(centerYear) - half * step;\n const today = todayYear();\n const items: ScaleItem[] = [];\n for (let i = 0; i < count; i++) {\n const y = start + i * step;\n items.push({\n year: y,\n label: fmtBigYear(y),\n sublabel: fmtBigYear(y + step) + ' →',\n header: '',\n compactLabel: fmtBigYear(y),\n isCurrent: Math.abs(today - y) < step / 2,\n isSelected: Math.abs(this.snap(selectedYear) - y) < step / 2,\n });\n }\n return items;\n },\n };\n}\n\n// ─── Mid-large scales (millennium, century, decade) ───────────────────────────\n\nfunction calendarYearBucketScale(id: ScaleId, label: string, step: number): Scale {\n return {\n id, label, step,\n snap(year) { return Math.floor(year / step) * step; },\n add(year, n) { return year + n * step; },\n build(centerYear, count, selectedYear, _locale) {\n const half = Math.floor(count / 2);\n const start = this.snap(centerYear) - half * step;\n const today = todayYear();\n const items: ScaleItem[] = [];\n for (let i = 0; i < count; i++) {\n const y = start + i * step;\n const yi = Math.round(y);\n const next = yi + step;\n const lbl =\n step === 1000 ? `${fmtYearShort(yi)} → ${fmtYearShort(next)}`\n : step === 100 ? `${fmtYearShort(yi)}s`\n : step === 10 ? `${fmtYearShort(yi)}s`\n : `${fmtYearShort(yi)}`;\n const sub =\n step === 1000 ? 'millénaire'\n : step === 100 ? 'siècle'\n : step === 10 ? 'décennie'\n : '';\n // compactLabel : derniers chiffres dans la \"tranche\" coarser\n // decade (step=10) → \"10\", \"20\", …, \"90\"\n // century (step=100) → \"100\", \"200\", …, \"900\"\n // millennium (step=1000) → \"1000\", …, \"9000\"\n const compact = String(((Math.abs(yi) % (step * 10)) | 0));\n items.push({\n year: y,\n label: lbl,\n sublabel: sub,\n header: '',\n compactLabel: compact,\n isCurrent: today >= y && today < y + step,\n isSelected: selectedYear >= y && selectedYear < y + step,\n });\n }\n return items;\n },\n };\n}\n\n// ─── Year scale ──────────────────────────────────────────────────────────────\n\nconst yearScale: Scale = {\n id: 'year',\n label: 'Année',\n step: 1,\n snap(year) { return Math.floor(year); },\n add(year, n) { return year + n; },\n build(centerYear, count, selectedYear, _locale) {\n const half = Math.floor(count / 2);\n const start = Math.floor(centerYear) - half;\n const today = todayYear();\n const items: ScaleItem[] = [];\n for (let i = 0; i < count; i++) {\n const y = start + i;\n const isNewDecade = y % 10 === 0;\n items.push({\n year: y,\n label: fmtYearShort(y),\n sublabel: isNewDecade ? `${y}s` : '',\n header: isNewDecade ? `${y}s` : '',\n compactLabel: String(Math.abs(y) % 100), // \"0\"..\"99\"\n isCurrent: Math.floor(today) === y,\n isSelected: Math.floor(selectedYear) === y,\n });\n }\n return items;\n },\n};\n\n// ─── Month scale ─────────────────────────────────────────────────────────────\n\nconst monthScale: Scale = {\n id: 'month',\n label: 'Mois',\n step: 1 / 12,\n snap(year) {\n if (!inDateRange(year)) return Math.floor(year * 12) / 12;\n const d = yearToDate(year);\n return dateToYear(new Date(d.getFullYear(), d.getMonth(), 1));\n },\n add(year, n) {\n if (!inDateRange(year)) return year + n / 12;\n const d = yearToDate(year);\n return dateToYear(new Date(d.getFullYear(), d.getMonth() + n, 1));\n },\n build(centerYear, count, selectedYear, locale) {\n const half = Math.floor(count / 2);\n const center = inDateRange(centerYear) ? yearToDate(this.snap(centerYear)) : null;\n const todayD = new Date();\n const todayM = new Date(todayD.getFullYear(), todayD.getMonth(), 1).getTime();\n const selM = inDateRange(selectedYear) ? new Date(yearToDate(this.snap(selectedYear)).getFullYear(), yearToDate(this.snap(selectedYear)).getMonth(), 1).getTime() : null;\n const fmtMonth = new Intl.DateTimeFormat(locale, { month: 'long' });\n const fmtShort = new Intl.DateTimeFormat(locale, { month: 'short' });\n const items: ScaleItem[] = [];\n\n for (let i = -half; i < count - half; i++) {\n if (!center) {\n const y = this.snap(centerYear) + i / 12;\n items.push({ year: y, label: '—', sublabel: '', header: '', compactLabel: '—', isCurrent: false, isSelected: false });\n continue;\n }\n const d = new Date(center.getFullYear(), center.getMonth() + i, 1);\n const long = fmtMonth.format(d);\n const short = fmtShort.format(d);\n const labelFull = long.charAt(0).toUpperCase() + long.slice(1);\n const labelAbbr = short.charAt(0).toUpperCase() + short.slice(1);\n const isJan = d.getMonth() === 0;\n items.push({\n year: dateToYear(d),\n label: isJan ? `${labelAbbr} ${d.getFullYear()}` : labelAbbr,\n sublabel: String(d.getFullYear()),\n header: labelFull,\n compactLabel: String(d.getMonth() + 1), // 1..12\n isCurrent: d.getTime() === todayM,\n isSelected: selM !== null && d.getTime() === selM,\n });\n }\n return items;\n },\n};\n\n// ─── Week scale ──────────────────────────────────────────────────────────────\n\nconst ONE_WEEK_YEARS = 7 / 365.25;\n\nfunction startOfWeek(d: Date, firstDayOfWeek: 0 | 1 = 1): Date {\n const day = d.getDay();\n const diff = (day - firstDayOfWeek + 7) % 7;\n return new Date(d.getFullYear(), d.getMonth(), d.getDate() - diff);\n}\n\nfunction isoWeekNumber(d: Date): number {\n const date = new Date(d.getFullYear(), d.getMonth(), d.getDate());\n date.setDate(date.getDate() + 4 - (date.getDay() || 7));\n const yearStart = new Date(date.getFullYear(), 0, 1);\n return Math.ceil(((date.getTime() - yearStart.getTime()) / 86_400_000 + 1) / 7);\n}\n\nconst weekScale: Scale = {\n id: 'week',\n label: 'Semaine',\n step: ONE_WEEK_YEARS,\n snap(year) {\n if (!inDateRange(year)) return year;\n return dateToYear(startOfWeek(yearToDate(year)));\n },\n add(year, n) {\n if (!inDateRange(year)) return year + n * ONE_WEEK_YEARS;\n const d = yearToDate(year);\n return dateToYear(new Date(d.getFullYear(), d.getMonth(), d.getDate() + n * 7));\n },\n build(centerYear, count, selectedYear, locale) {\n const half = Math.floor(count / 2);\n const todayWeekStart = startOfWeek(new Date()).getTime();\n const selStart = inDateRange(selectedYear) ? startOfWeek(yearToDate(selectedYear)).getTime() : null;\n const fmtRange = new Intl.DateTimeFormat(locale, { day: 'numeric', month: 'short' });\n const fmtMonthLong = new Intl.DateTimeFormat(locale, { month: 'long' });\n const items: ScaleItem[] = [];\n\n if (!inDateRange(centerYear)) {\n // Hors plage Date — fallback approximatif\n for (let i = -half; i < count - half; i++) {\n const y = this.snap(centerYear) + i * ONE_WEEK_YEARS;\n items.push({ year: y, label: '—', sublabel: '', header: '', compactLabel: '—', isCurrent: false, isSelected: false });\n }\n return items;\n }\n\n const center = startOfWeek(yearToDate(centerYear));\n for (let i = -half; i < count - half; i++) {\n const start = new Date(center.getFullYear(), center.getMonth(), center.getDate() + i * 7);\n const end = new Date(start.getFullYear(), start.getMonth(), start.getDate() + 6);\n const range = (fmtRange as unknown as { formatRange?: (a: Date, b: Date) => string }).formatRange?.(start, end)\n ?? `${fmtRange.format(start)} – ${fmtRange.format(end)}`;\n const monthName = fmtMonthLong.format(start);\n items.push({\n year: dateToYear(start),\n label: range,\n sublabel: `S${isoWeekNumber(start)} · ${start.getFullYear()}`,\n header: monthName.charAt(0).toUpperCase() + monthName.slice(1),\n compactLabel: String(start.getDate()), // jour du début de semaine\n isCurrent: start.getTime() === todayWeekStart,\n isSelected: selStart !== null && start.getTime() === selStart,\n });\n }\n return items;\n },\n};\n\n// ─── Day scale ───────────────────────────────────────────────────────────────\n\nconst ONE_DAY_YEARS = 1 / 365.25;\n\nconst dayScale: Scale = {\n id: 'day',\n label: 'Jour',\n step: ONE_DAY_YEARS,\n snap(year) {\n if (!inDateRange(year)) return year;\n const d = yearToDate(year);\n return dateToYear(new Date(d.getFullYear(), d.getMonth(), d.getDate()));\n },\n add(year, n) {\n if (!inDateRange(year)) return year + n * ONE_DAY_YEARS;\n const d = yearToDate(year);\n return dateToYear(new Date(d.getFullYear(), d.getMonth(), d.getDate() + n));\n },\n build(centerYear, count, selectedYear, locale) {\n const half = Math.floor(count / 2);\n const todayD = new Date();\n const todayKey = new Date(todayD.getFullYear(), todayD.getMonth(), todayD.getDate()).getTime();\n const selKey = inDateRange(selectedYear)\n ? new Date(yearToDate(selectedYear).getFullYear(), yearToDate(selectedYear).getMonth(), yearToDate(selectedYear).getDate()).getTime()\n : null;\n const fmtWD = new Intl.DateTimeFormat(locale, { weekday: 'short' });\n const fmtMonthLong = new Intl.DateTimeFormat(locale, { month: 'long' });\n const items: ScaleItem[] = [];\n\n if (!inDateRange(centerYear)) {\n for (let i = -half; i < count - half; i++) {\n items.push({ year: this.snap(centerYear) + i * ONE_DAY_YEARS, label: '—', sublabel: '', header: '', compactLabel: '—', isCurrent: false, isSelected: false });\n }\n return items;\n }\n const center = yearToDate(this.snap(centerYear));\n for (let i = -half; i < count - half; i++) {\n const d = new Date(center.getFullYear(), center.getMonth(), center.getDate() + i);\n const wd = fmtWD.format(d);\n const monthName = fmtMonthLong.format(d);\n items.push({\n year: dateToYear(d),\n label: `${wd.charAt(0).toUpperCase()}${wd.slice(1)} ${d.getDate()}`,\n sublabel: `${monthName.slice(0, 3)} ${d.getFullYear()}`,\n header: monthName.charAt(0).toUpperCase() + monthName.slice(1),\n compactLabel: String(d.getDate()), // jour du mois\n isCurrent: d.getTime() === todayKey,\n isSelected: selKey !== null && d.getTime() === selKey,\n });\n }\n return items;\n },\n};\n\n// ─── Sub-day scales (hour / minute / second) ─────────────────────────────────\n\nconst MS_PER_YEAR = 365.25 * 24 * 3600 * 1000;\n\nfunction subDayScale(\n id: ScaleId, label: string, msPerStep: number,\n fmtFn: (d: Date) => string,\n compactFn: (d: Date) => string,\n): Scale {\n const stepYears = msPerStep / MS_PER_YEAR;\n return {\n id, label, step: stepYears,\n snap(year) {\n if (!inDateRange(year)) return year;\n const ms = yearToDate(year).getTime();\n return dateToYear(new Date(Math.floor(ms / msPerStep) * msPerStep));\n },\n add(year, n) {\n if (!inDateRange(year)) return year + n * stepYears;\n const ms = yearToDate(year).getTime();\n return dateToYear(new Date(ms + n * msPerStep));\n },\n build(centerYear, count, selectedYear, locale) {\n const half = Math.floor(count / 2);\n const todayMs = Date.now();\n const todayBucket = Math.floor(todayMs / msPerStep) * msPerStep;\n const selBucket = inDateRange(selectedYear)\n ? Math.floor(yearToDate(selectedYear).getTime() / msPerStep) * msPerStep\n : null;\n const fmtDay = new Intl.DateTimeFormat(locale, { day: 'numeric', month: 'short', year: 'numeric' });\n const items: ScaleItem[] = [];\n\n if (!inDateRange(centerYear)) {\n for (let i = -half; i < count - half; i++) {\n items.push({ year: this.snap(centerYear) + i * stepYears, label: '—', sublabel: '', header: '', compactLabel: '—', isCurrent: false, isSelected: false });\n }\n return items;\n }\n const centerMs = Math.floor(yearToDate(this.snap(centerYear)).getTime() / msPerStep) * msPerStep;\n for (let i = -half; i < count - half; i++) {\n const ms = centerMs + i * msPerStep;\n const d = new Date(ms);\n items.push({\n year: dateToYear(d),\n label: fmtFn(d),\n sublabel: fmtDay.format(d),\n header: fmtFn(d),\n compactLabel: compactFn(d),\n isCurrent: ms === todayBucket,\n isSelected: selBucket !== null && ms === selBucket,\n });\n }\n return items;\n },\n };\n}\n\nconst hourScale = subDayScale('hour', 'Heure', 3600 * 1000,\n d => `${String(d.getHours()).padStart(2,'0')}:00`,\n d => String(d.getHours()));\nconst minuteScale = subDayScale('minute', 'Minute', 60 * 1000,\n d => `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`,\n d => `:${String(d.getMinutes()).padStart(2,'0')}`);\nconst secondScale = subDayScale('second', 'Seconde', 1000,\n d => `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}`,\n d => `:${String(d.getSeconds()).padStart(2,'0')}`);\n\n// ─── Sub-second scales (ms / μs / ns) — décoratif ─────────────────────────────\n\nfunction subSecondScale(id: ScaleId, label: string, unit: 'ms', divisor: number): Scale {\n // step en années : 1 unit = (1 / divisor) seconde / secondes-par-an\n const stepYears = (1 / divisor) / (365.25 * 24 * 3600);\n return {\n id, label, step: stepYears,\n snap(year) { return year; },\n add(year, n) { return year + n * stepYears; },\n build(centerYear, count, selectedYear, _locale) {\n const half = Math.floor(count / 2);\n const sel = Math.round((selectedYear - centerYear) / stepYears);\n const items: ScaleItem[] = [];\n for (let i = -half; i < count - half; i++) {\n items.push({\n year: centerYear + i * stepYears,\n label: `${i >= 0 ? '+' : ''}${i} ${unit}`,\n sublabel: '',\n header: `Δ${unit}`,\n compactLabel: `${i >= 0 ? '+' : ''}${i}`,\n isCurrent: i === 0,\n isSelected: i === sel,\n });\n }\n return items;\n },\n };\n}\n\nconst msScale = subSecondScale('ms', 'Milliseconde', 'ms', 1000);\n\n// ─── Registry ────────────────────────────────────────────────────────────────\n\nexport const SCALES: Scale[] = [\n macroScale('Ga', \"Milliard d'années\", 1e9),\n macroScale('100Ma', 'Centaine de millions d\\'années', 1e8),\n macroScale('Ma', \"Million d'années\", 1e6),\n macroScale('100ka', 'Centaine de milliers d\\'années', 1e5),\n macroScale('10ka', '10 000 ans', 1e4),\n calendarYearBucketScale('millennium', 'Millénaire', 1000),\n calendarYearBucketScale('century', 'Siècle', 100),\n calendarYearBucketScale('decade', 'Décennie', 10),\n yearScale,\n monthScale,\n weekScale,\n dayScale,\n hourScale,\n minuteScale,\n secondScale,\n msScale,\n];\n\nconst SCALE_BY_ID: Record<ScaleId, Scale> = SCALES.reduce((acc, s) => {\n acc[s.id] = s;\n return acc;\n}, {} as Record<ScaleId, Scale>);\n\nexport function getScale(id: ScaleId): Scale {\n const s = SCALE_BY_ID[id];\n if (!s) throw new Error(`[Scale] unknown id: ${id}`);\n return s;\n}\n\n/** Ordre canonique : index 0 = Ga (le plus grossier) → 17 = ns (le plus fin). */\nconst SCALE_ORDER_BY_ID: Record<ScaleId, number> = (() => {\n const m: Partial<Record<ScaleId, number>> = {};\n SCALES.forEach((s, i) => { m[s.id] = i; });\n return m as Record<ScaleId, number>;\n})();\n\nexport function scaleIndex(id: ScaleId): number {\n return SCALE_ORDER_BY_ID[id];\n}\n\nexport function scaleAt(index: number): Scale | undefined {\n return SCALES[index];\n}\n\nexport const SCALE_COUNT = SCALES.length;\n\nexport { dateToYear, yearToDate, todayYear, inDateRange };\n","/**\n * LadderTimeline — Carousel multi-échelles\n *\n * Cursor interne unifié = decimal year (float64). L'échelle active fournit\n * via Scale.build() les items à afficher. Le drag/momentum/scroll/visuals\n * ne dépendent pas de l'échelle.\n */\n\nimport type {\n LadderTimelineOptions,\n LadderTimelineSelectEventDetail,\n CalendarAdapter,\n TimelineItemInfo,\n TimelineMarker,\n} from './LadderTimeline.types';\n\nimport {\n normalizeDate,\n cloneDate,\n getWeekStart,\n getWeekEnd,\n isValidDate,\n} from './LadderTimeline.utils';\n\nimport {\n createWeekChangeEvent,\n createSelectEvent,\n createNavigateEvent,\n EVENT_WEEK_CHANGE,\n EVENT_SELECT,\n EVENT_NAVIGATE,\n} from './LadderTimeline.events';\n\nimport {\n type Scale,\n type ScaleId,\n getScale,\n scaleIndex,\n scaleAt,\n SCALE_COUNT,\n dateToYear,\n yearToDate,\n todayYear,\n inDateRange,\n} from './Scale';\n\n// ─── Constantes ────────────────────────────────────────────────────────────────\n\nconst CAROUSEL_WEEKS = 51;\nconst EXTEND_THRESHOLD = 8;\nconst MOMENTUM_FRICTION = 0.90;\nconst MOMENTUM_MIN_VEL = 0.08;\nconst SCALE_MIN = 0.82;\nconst OPACITY_MIN = 0.40;\n/** Délai (ms) entre deux navigations molette */\nconst WHEEL_DEBOUNCE_MS = 250;\n\nconst DEFAULTS = {\n firstDayOfWeek: 1 as const,\n // Suit la langue du navigateur ; fallback 'fr-FR' en SSR / environnement sans navigator\n locale: typeof navigator !== 'undefined' ? navigator.language : 'fr-FR',\n compact: false,\n scale: 'week' as ScaleId,\n};\n\n// ─── LadderTimeline ─────────────────────────────────────────────────────────────\n\nexport class LadderTimeline {\n // Config\n private readonly container: HTMLElement;\n private readonly firstDayOfWeek: 0 | 1;\n private readonly locale: string;\n private readonly compact: boolean;\n private readonly theme: 'light' | 'dark' | 'auto';\n private readonly storageKey: string | undefined;\n private readonly onWeekChangeCb: ((d: LadderTimelineSelectEventDetail) => void) | undefined;\n private readonly onWeekPreviewCb: ((d: LadderTimelineSelectEventDetail) => void) | undefined;\n private readonly onItemChangeCb: ((i: TimelineItemInfo) => void) | undefined;\n private readonly onItemPreviewCb: ((i: TimelineItemInfo) => void) | undefined;\n private readonly onMarkerClickCb: ((m: TimelineMarker) => void) | undefined;\n\n // Markers\n private markers: TimelineMarker[] = [];\n\n // Dark mode auto\n private _darkMq: MediaQueryList | undefined;\n private _darkMqHandler: ((e: MediaQueryListEvent) => void) | undefined;\n\n // State — cursor en decimal year\n private selectedYear: number;\n private referenceYear: number;\n private scale: Scale;\n private displayMode: 'expanded' | 'compact';\n\n // Bornes (immutable après construction)\n private readonly minYear: number;\n private readonly maxYear: number;\n private readonly coarsestScaleIdx: number; // index dans SCALES — petit = grossier (Ga=0)\n private readonly finestScaleIdx: number; // index dans SCALES — grand = fin (ns=17)\n\n // DOM\n private root!: HTMLElement;\n private listWrapper!: HTMLElement;\n private listEl!: HTMLElement;\n private centerMark!: HTMLElement;\n private liveRegion!: HTMLElement;\n\n // Drag\n private isDragging = false;\n private dragStartX = 0;\n private dragStartScroll = 0;\n\n // Momentum\n private velX = 0;\n private lastMoveX = 0;\n private lastMoveT = 0;\n\n // RAF\n private rafId: number | undefined;\n\n // Molette — #4\n private wheelDebounceId: number | undefined;\n\n // Extension proactive\n private _isExtending = false;\n\n // Spring snap\n private springRafId: number | undefined;\n\n // Pinch (touch multi-fingers)\n private _isPinching = false;\n private _pinchInitialDist = 0;\n\n // Misc\n private cleanupFns: Array<() => void> = [];\n private resizeObserver?: ResizeObserver;\n private calendarAdapter?: CalendarAdapter;\n\n // ─── Constructor ────────────────────────────────────────────────────────────\n\n constructor(options: LadderTimelineOptions) {\n if (!options.container) throw new Error('[LadderTimeline] container is required');\n\n this.container = options.container;\n this.firstDayOfWeek = options.firstDayOfWeek ?? DEFAULTS.firstDayOfWeek;\n this.locale = options.locale ?? DEFAULTS.locale;\n this.compact = options.compact ?? DEFAULTS.compact;\n this.theme = options.theme ?? 'light';\n this.storageKey = options.storageKey;\n this.onWeekChangeCb = options.onWeekChange;\n this.onWeekPreviewCb = options.onWeekPreview;\n this.onItemChangeCb = options.onItemChange;\n this.onItemPreviewCb = options.onItemPreview;\n this.onMarkerClickCb = options.onMarkerClick;\n if (options.markers) this.markers = options.markers.slice();\n\n // ── Bornes year ──────────────────────────────────────────────────────────\n this.minYear =\n typeof options.minYear === 'number' ? options.minYear\n : isValidDate(options.minDate) ? dateToYear(options.minDate)\n : -Infinity;\n this.maxYear =\n typeof options.maxYear === 'number' ? options.maxYear\n : isValidDate(options.maxDate) ? dateToYear(options.maxDate)\n : Infinity;\n\n // ── Bornes échelle ───────────────────────────────────────────────────────\n // coarsestScaleIdx = plus grossier autorisé (= plus petit index → maxScale)\n // finestScaleIdx = plus fin autorisé (= plus grand index → minScale)\n this.coarsestScaleIdx = options.maxScale ? scaleIndex(options.maxScale) : 0;\n this.finestScaleIdx = options.minScale ? scaleIndex(options.minScale) : SCALE_COUNT - 1;\n\n // ── Échelle initiale (clampée) ──────────────────────────────────────────\n const requestedScale = options.scale ?? DEFAULTS.scale;\n this.scale = getScale(this._clampScaleId(requestedScale));\n this.displayMode = options.displayMode ?? 'expanded';\n\n const today = todayYear();\n this.selectedYear = isValidDate(options.selectedDate) ? dateToYear(normalizeDate(options.selectedDate)) : today;\n this.referenceYear = isValidDate(options.referenceDate) ? dateToYear(normalizeDate(options.referenceDate)) : this.selectedYear;\n\n // Persistence : la date stockée prend priorité si aucune selectedDate explicite\n if (this.storageKey && !isValidDate(options.selectedDate)) {\n const stored = this._readStorage();\n if (stored !== null) { this.selectedYear = stored; this.referenceYear = stored; }\n }\n\n this.selectedYear = this._snapAndClamp(this.selectedYear);\n this.referenceYear = this._snapAndClamp(this.referenceYear);\n\n // Si l'échelle initiale est calendaire et le year chargé est hors plage Date\n // (storage rempli depuis une échelle macro), retour aujourd'hui.\n if (this.scale.step < 1 && !inDateRange(this.selectedYear)) {\n const t = this._snapAndClamp(todayYear());\n this.selectedYear = t;\n this.referenceYear = t;\n }\n\n this.render();\n }\n\n // ─── Public API ─────────────────────────────────────────────────────────────\n\n setDate(date: Date): void {\n if (!isValidDate(date)) return;\n const y = dateToYear(normalizeDate(date));\n this.selectedYear = this._snapAndClamp(y);\n this.referenceYear = this.selectedYear;\n this._updateListDOM(false);\n this._emitItemChange(this.selectedYear);\n }\n\n getDate(): Date {\n return inDateRange(this.selectedYear) ? yearToDate(this.selectedYear) : new Date();\n }\n\n goToToday(): void {\n const y = this._snapAndClamp(todayYear());\n this.selectedYear = y;\n this.referenceYear = y;\n this._updateListDOM(true);\n this._emitNavigate('today');\n this._emitItemChange(this.selectedYear);\n }\n\n goToPrevious(): void {\n const target = this._clampYear(this.scale.add(this.referenceYear, -1));\n if (target === this.referenceYear) return; // bloqué à la borne\n this.referenceYear = target;\n this.selectedYear = target;\n this._updateListDOM(true);\n this._emitNavigate('prev');\n this._emitItemChange(this.selectedYear);\n }\n\n goToNext(): void {\n const target = this._clampYear(this.scale.add(this.referenceYear, 1));\n if (target === this.referenceYear) return; // bloqué à la borne\n this.referenceYear = target;\n this.selectedYear = target;\n this._updateListDOM(true);\n this._emitNavigate('next');\n this._emitItemChange(this.selectedYear);\n }\n\n /** Change l'échelle courante et re-rend la barre alignée dessus */\n setScale(id: ScaleId): void {\n const clampedId = this._clampScaleId(id);\n if (this.scale.id === clampedId) return;\n this.scale = getScale(clampedId);\n let y = this.selectedYear;\n if (this.scale.step < 1 && !inDateRange(y)) y = todayYear();\n y = this._snapAndClamp(y);\n this.selectedYear = y;\n this.referenceYear = y;\n this._updateListDOM(false);\n this._emitItemChange(this.selectedYear);\n }\n\n getScale(): ScaleId { return this.scale.id; }\n\n setDisplayMode(mode: 'expanded' | 'compact'): void {\n if (this.displayMode === mode) return;\n this.displayMode = mode;\n this._renderItems();\n this._syncCenterMark();\n const sel = this.listEl.querySelector<HTMLElement>('.lt__item--selected');\n if (sel) { this._scrollToItem(sel, false); this._applyScaleEffect(); }\n }\n\n getDisplayMode(): 'expanded' | 'compact' { return this.displayMode; }\n\n // ─── Markers ───────────────────────────────────────────────────────────────\n\n /** Remplace tous les markers. */\n setMarkers(markers: TimelineMarker[]): void {\n this.markers = markers.slice();\n this._renderItems();\n }\n\n /** Ajoute un marker (ne dédoublonne pas sur id — c'est à l'appelant). */\n addMarker(marker: TimelineMarker): void {\n this.markers.push(marker);\n this._renderItems();\n }\n\n /** Supprime tous les markers ayant cet id. No-op si id absent ou inconnu. */\n removeMarker(id: string): void {\n const before = this.markers.length;\n this.markers = this.markers.filter(m => m.id !== id);\n if (this.markers.length !== before) this._renderItems();\n }\n\n /** Renvoie une copie de la liste courante. */\n getMarkers(): TimelineMarker[] { return this.markers.slice(); }\n\n /** Renvoie les bornes effectives configurées sur cette instance. */\n getBounds(): { minYear: number; maxYear: number; minScale: ScaleId; maxScale: ScaleId } {\n const finest = scaleAt(this.finestScaleIdx);\n const coarsest = scaleAt(this.coarsestScaleIdx);\n return {\n minYear: this.minYear,\n maxYear: this.maxYear,\n minScale: finest?.id ?? 'ms',\n maxScale: coarsest?.id ?? 'Ga',\n };\n }\n\n // ─── Clamp helpers ──────────────────────────────────────────────────────────\n\n private _clampYear(year: number): number {\n if (year < this.minYear) return this.minYear;\n if (year > this.maxYear) return this.maxYear;\n return year;\n }\n\n /**\n * Snap puis clamp un year en garantissant qu'on reste sur une frontière\n * d'échelle (sauf bornes pathologiques sans aucune frontière dans la plage).\n * Cas géré : snap (souvent Math.floor) tombe juste sous minYear → on monte\n * d'une unité d'échelle pour retomber dans les bornes.\n */\n private _snapAndClamp(year: number): number {\n const snapped = this.scale.snap(this._clampYear(year));\n if (snapped < this.minYear) {\n const next = this.scale.add(snapped, 1);\n if (next <= this.maxYear) return next;\n }\n if (snapped > this.maxYear) {\n const prev = this.scale.add(snapped, -1);\n if (prev >= this.minYear) return prev;\n }\n return this._clampYear(snapped);\n }\n\n private _clampScaleId(id: ScaleId): ScaleId {\n const idx = scaleIndex(id);\n const clampedIdx = Math.max(this.coarsestScaleIdx, Math.min(this.finestScaleIdx, idx));\n return scaleAt(clampedIdx)?.id ?? id;\n }\n\n getEventTarget(): EventTarget { return this.container; }\n\n connectAdapter(adapter: CalendarAdapter): void { this.calendarAdapter = adapter; }\n\n render(): void {\n this._cleanup();\n this.container.innerHTML = '';\n this._buildDOM();\n this._setupTheme();\n this._bindDragEvents();\n this._bindWheelEvents();\n this._bindKeyboardEvents();\n this._bindPinchEvents();\n this._updateListDOM(false);\n this._initResizeObserver();\n }\n\n destroy(): void {\n this._cleanup();\n this.resizeObserver?.disconnect();\n if (this.rafId !== void 0) cancelAnimationFrame(this.rafId);\n if (this.springRafId !== void 0) cancelAnimationFrame(this.springRafId);\n if (this.wheelDebounceId !== void 0) clearTimeout(this.wheelDebounceId);\n if (this._darkMq && this._darkMqHandler) {\n this._darkMq.removeEventListener('change', this._darkMqHandler);\n }\n this.container.innerHTML = '';\n }\n\n on(\n event: typeof EVENT_WEEK_CHANGE | typeof EVENT_SELECT | typeof EVENT_NAVIGATE,\n handler: EventListener,\n ): () => void {\n this.container.addEventListener(event, handler);\n return () => this.container.removeEventListener(event, handler);\n }\n\n // ─── DOM ────────────────────────────────────────────────────────────────────\n\n private _buildDOM(): void {\n this.root = document.createElement('div');\n this.root.className = ['lt', this.compact ? 'lt--compact' : ''].filter(Boolean).join(' ');\n this.root.setAttribute('role', 'toolbar');\n this.root.setAttribute('aria-label', 'Navigation temporelle');\n\n const header = document.createElement('div');\n header.className = 'lt__header';\n\n this.listWrapper = document.createElement('div');\n this.listWrapper.className = 'lt__list-wrapper';\n this.listWrapper.tabIndex = 0;\n this.listWrapper.setAttribute('aria-label', 'Glisser ou utiliser la molette pour naviguer');\n\n this.centerMark = document.createElement('div');\n this.centerMark.className = 'lt__center-mark';\n this.centerMark.setAttribute('aria-hidden', 'true');\n this.listWrapper.appendChild(this.centerMark);\n\n this.listEl = document.createElement('ol');\n this.listEl.className = 'lt__list';\n this.listEl.setAttribute('role', 'listbox');\n this.listEl.setAttribute('aria-orientation', 'horizontal');\n\n this.listWrapper.appendChild(this.listEl);\n header.appendChild(this.listWrapper);\n this.root.appendChild(header);\n\n // ARIA live region — annonce le label de l'item courant aux lecteurs d'écran\n this.liveRegion = document.createElement('div');\n this.liveRegion.className = 'lt__live';\n this.liveRegion.setAttribute('aria-live', 'polite');\n this.liveRegion.setAttribute('aria-atomic', 'true');\n this.root.appendChild(this.liveRegion);\n\n this.container.appendChild(this.root);\n }\n\n // ─── List DOM ──────────────────────────────────────────────────────────────\n\n /**\n * Rend tous les <li> dans listEl à partir de referenceYear / selectedYear\n * et de l'échelle courante. Applique le padding carousel immédiatement.\n */\n private _renderItems(): void {\n const items = this.scale.build(\n this.referenceYear, CAROUSEL_WEEKS, this.selectedYear, this.locale,\n );\n\n // Échelle plus grossière (pour détecter les items aux frontières)\n const coarser = scaleAt(scaleIndex(this.scale.id) - 1);\n const tol = this.scale.step * 0.5;\n\n this.listEl.innerHTML = '';\n let prevHeader = '__init__';\n\n for (const it of items) {\n const isNewHeader = it.header !== '' && it.header !== prevHeader;\n prevHeader = it.header;\n\n // Détection major : l'item est-il à la frontière de l'échelle supérieure ?\n const isMajor = !coarser || Math.abs(coarser.snap(it.year) - it.year) < tol;\n const isCompact = this.displayMode === 'compact';\n const minimised = isCompact && !isMajor && !it.isSelected;\n\n const li = document.createElement('li');\n li.className =\n 'lt__item' +\n (it.isSelected ? ' lt__item--selected' : '') +\n (it.isCurrent ? ' lt__item--current' : '') +\n (isCompact ? (isMajor ? ' lt__item--major' : ' lt__item--minor') : '');\n li.setAttribute('role', 'option');\n li.setAttribute('aria-selected', String(it.isSelected));\n li.setAttribute('tabindex', it.isSelected ? '0' : '-1');\n li.setAttribute('data-year', String(it.year));\n\n li.setAttribute('data-month-text', it.header);\n li.setAttribute('data-orig-label', isNewHeader ? it.header : '');\n\n const monthEl = document.createElement('span');\n monthEl.className = 'lt__month-label';\n monthEl.setAttribute('aria-hidden', 'true');\n if (!minimised) {\n if (it.isSelected) {\n monthEl.textContent = it.header;\n } else if (isNewHeader) {\n monthEl.textContent = it.header;\n }\n }\n\n const lbl = document.createElement('span');\n lbl.className = 'lt__item-label';\n lbl.textContent = minimised ? it.compactLabel : it.label;\n\n const sub = document.createElement('span');\n sub.className = 'lt__item-sublabel';\n if (!minimised) {\n sub.textContent = it.isCurrent && it.sublabel ? `${it.sublabel} · Auj.` : it.sublabel;\n }\n\n li.append(monthEl, lbl, sub);\n\n if (it.isCurrent) {\n const dot = document.createElement('span');\n dot.className = 'lt__item-dot';\n dot.setAttribute('aria-hidden', 'true');\n li.appendChild(dot);\n }\n\n // Markers : ceux dont year ∈ [item.year, item.year + step)\n const itemMarkers = this._markersForItem(it.year);\n if (itemMarkers.length > 0) {\n const stack = document.createElement('span');\n stack.className = 'lt__marker-stack';\n stack.setAttribute('aria-hidden', 'true');\n for (const m of itemMarkers) {\n const dot = document.createElement('button');\n dot.type = 'button';\n dot.className = 'lt__marker';\n if (m.color) dot.style.background = m.color;\n dot.title = m.label;\n dot.setAttribute('aria-label', m.label);\n if (m.id) dot.setAttribute('data-marker-id', m.id);\n dot.addEventListener('click', (ev) => {\n ev.stopPropagation();\n this._handleMarkerClick(m);\n });\n stack.appendChild(dot);\n }\n li.appendChild(stack);\n }\n\n this.listEl.appendChild(li);\n }\n\n this._applyCarouselPadding(); // lit offsetWidth → reflow synchrone\n }\n\n /** Renvoie les markers dont year tombe dans [itemYear, itemYear + step). */\n private _markersForItem(itemYear: number): TimelineMarker[] {\n const end = itemYear + this.scale.step;\n return this.markers.filter(m => m.year >= itemYear && m.year < end);\n }\n\n private _handleMarkerClick(marker: TimelineMarker): void {\n this.onMarkerClickCb?.(marker);\n this.container.dispatchEvent(new CustomEvent('markerclick', {\n detail: marker, bubbles: true, composed: true,\n }));\n }\n\n private _updateListDOM(animate: boolean): void {\n this._renderItems();\n this._syncCenterMark();\n\n const sel = this.listEl.querySelector<HTMLElement>('.lt__item--selected');\n if (sel) {\n this._scrollToItem(sel, animate);\n requestAnimationFrame(() => this._applyScaleEffect());\n }\n }\n\n // ─── Extension proactive ─────────────────────────────────────────────────────\n\n /**\n * Vérifie pendant le drag si le centre approche du bord (seuil × 2).\n * Si oui, reconstruit la liste autour de l'item central visible en\n * conservant la position de scroll — le drag continue sans saut.\n */\n private _checkAndExtend(): void {\n if (this._isExtending) return;\n\n const items = Array.from(this.listEl.querySelectorAll<HTMLElement>('.lt__item'));\n if (items.length === 0) return;\n\n const wl = this.listWrapper;\n const viewCenter = wl.scrollLeft + wl.clientWidth / 2;\n const itemStride = items.length > 1 && items[1] && items[0]\n ? items[1].offsetLeft - items[0].offsetLeft : 100;\n\n // Trouver l'item le plus proche du centre\n let centerIdx = -1;\n let minDist = Infinity;\n items.forEach((item, i) => {\n const d = Math.abs(item.offsetLeft + item.offsetWidth / 2 - viewCenter);\n if (d < minDist) { minDist = d; centerIdx = i; }\n });\n if (centerIdx === -1) return;\n\n // Seuil proactif = 2 × EXTEND_THRESHOLD (bien avant le bord)\n const PROACTIVE = EXTEND_THRESHOLD * 2;\n const scrollRemRight = wl.scrollWidth - wl.clientWidth - wl.scrollLeft;\n const scrollRemLeft = wl.scrollLeft;\n\n const nearEdge =\n centerIdx < PROACTIVE ||\n centerIdx >= items.length - PROACTIVE ||\n scrollRemRight < itemStride * PROACTIVE ||\n scrollRemLeft < itemStride * PROACTIVE;\n\n if (!nearEdge) return;\n\n const centerEl = items[centerIdx];\n if (!centerEl) return;\n\n const centerYearStr = centerEl.getAttribute('data-year');\n if (!centerYearStr) return;\n\n this._isExtending = true;\n\n // Décalage entre le scroll actuel et le scroll \"centré sur cet item\"\n const scrollOffsetFromCenter =\n wl.scrollLeft - (centerEl.offsetLeft + centerEl.offsetWidth / 2 - wl.clientWidth / 2);\n\n // Reconstruction autour de l'item central\n this.referenceYear = parseFloat(centerYearStr);\n this._renderItems();\n this._syncCenterMark();\n\n // Restaurer la position de scroll — même item, même décalage\n const newCenterEl = this.listEl.querySelector<HTMLElement>(`[data-year=\"${centerYearStr}\"]`);\n if (newCenterEl) {\n const newScroll = newCenterEl.offsetLeft + newCenterEl.offsetWidth / 2\n - wl.clientWidth / 2 + scrollOffsetFromCenter;\n wl.scrollLeft = newScroll;\n\n // Recaler dragStartScroll pour que le prochain pointermove soit cohérent\n if (this.isDragging) {\n this.dragStartScroll = newScroll - (this.dragStartX - this.lastMoveX);\n }\n }\n\n this._applyScaleEffect();\n this._isExtending = false;\n }\n\n // ─── Carousel helpers ────────────────────────────────────────────────────────\n\n private _applyCarouselPadding(): void {\n const ww = this.listWrapper.clientWidth;\n const items = this.listEl.querySelectorAll<HTMLElement>('.lt__item');\n const first = items[0];\n const last = items[items.length - 1];\n if (!first || ww === 0) return;\n\n const padLeft = Math.max(0, ww / 2 - first.offsetWidth / 2);\n const padRight = Math.max(0, ww / 2 - (last ? last.offsetWidth / 2 : first.offsetWidth / 2));\n\n this.listEl.style.paddingLeft = `${padLeft}px`;\n this.listEl.style.paddingRight = `${padRight}px`;\n }\n\n private _syncCenterMark(): void {\n const item = this.listEl.querySelector<HTMLElement>('.lt__item');\n if (!item) return;\n this.centerMark.style.width = `${item.offsetWidth}px`;\n this.centerMark.style.height = `${item.offsetHeight}px`;\n }\n\n private _scrollToItem(el: HTMLElement, animate: boolean, onComplete?: () => void): void {\n const target = el.offsetLeft + el.offsetWidth / 2 - this.listWrapper.clientWidth / 2;\n\n if (this.rafId !== void 0) { cancelAnimationFrame(this.rafId); this.rafId = void 0; }\n\n if (animate) {\n this._smoothScrollTo(target, onComplete);\n } else {\n this.listWrapper.scrollLeft = target;\n onComplete?.();\n }\n }\n\n private _smoothScrollTo(target: number, onComplete?: () => void): void {\n const DURATION = 280;\n const start = this.listWrapper.scrollLeft;\n const diff = target - start;\n\n if (Math.abs(diff) < 1) { onComplete?.(); return; }\n\n const t0 = performance.now();\n const easeOut = (t: number) => 1 - (1 - t) ** 3;\n\n const tick = (now: number) => {\n const t = Math.min((now - t0) / DURATION, 1);\n this.listWrapper.scrollLeft = start + diff * easeOut(t);\n\n if (t < 1) {\n this.rafId = requestAnimationFrame(tick);\n } else {\n this.listWrapper.scrollLeft = target;\n this.rafId = void 0;\n onComplete?.();\n }\n };\n\n this.rafId = requestAnimationFrame(tick);\n }\n\n private _getCenterItem(): HTMLElement | null {\n const viewCenter = this.listWrapper.scrollLeft + this.listWrapper.clientWidth / 2;\n const items = Array.from(this.listEl.querySelectorAll<HTMLElement>('.lt__item'));\n let closest: HTMLElement | null = null;\n let minDist = Infinity;\n for (const item of items) {\n const d = Math.abs(item.offsetLeft + item.offsetWidth / 2 - viewCenter);\n if (d < minDist) { minDist = d; closest = item; }\n }\n return closest;\n }\n\n // ─── Scale + opacité (#3) ─────────────────────────────────────────────────\n\n private _applyScaleEffect(): void {\n const viewCenter = this.listWrapper.scrollLeft + this.listWrapper.clientWidth / 2;\n const maxDist = this.listWrapper.clientWidth * 0.55;\n this.listEl.querySelectorAll<HTMLElement>('.lt__item').forEach(item => {\n const ratio = Math.min(Math.abs(item.offsetLeft + item.offsetWidth / 2 - viewCenter) / maxDist, 1);\n const scale = 1 - ratio * (1 - SCALE_MIN);\n const opacity = 1 - ratio * (1 - OPACITY_MIN);\n item.style.transform = `scale(${scale.toFixed(3)})`;\n item.style.opacity = opacity.toFixed(3);\n });\n }\n\n // ─── Spring snap ─────────────────────────────────────────────────────────────\n\n private _springSnap(el: HTMLElement): void {\n if (this.springRafId !== void 0) { cancelAnimationFrame(this.springRafId); this.springRafId = void 0; }\n\n const DURATION = 420;\n const A = 0.10;\n const DECAY = 7;\n const FREQ = 2.2;\n const t0 = performance.now();\n\n const spring = (t: number) => 1 + A * Math.exp(-DECAY * t) * Math.sin(FREQ * Math.PI * t);\n\n const tick = (now: number) => {\n const t = Math.min((now - t0) / DURATION, 1);\n el.style.transform = `scale(${spring(t).toFixed(4)})`;\n el.style.opacity = '1';\n\n if (t < 1) {\n this.springRafId = requestAnimationFrame(tick);\n } else {\n this.springRafId = void 0;\n el.style.transform = 'scale(1)';\n }\n };\n\n this.springRafId = requestAnimationFrame(tick);\n }\n\n // ─── Visuals unifiés ─────────────────────────────────────────────────────────\n\n private _updateVisuals(): void {\n this._updateCenterHighlight();\n this._applyScaleEffect();\n }\n\n private _scheduleVisuals(): void {\n if (this.rafId !== void 0) return;\n this.rafId = requestAnimationFrame(() => {\n this.rafId = void 0;\n this._checkAndExtend();\n this._updateVisuals();\n });\n }\n\n private _updateCenterHighlight(): void {\n const closest = this._getCenterItem();\n if (!closest) return;\n\n this.listEl.querySelectorAll<HTMLElement>('.lt__item').forEach(item => {\n const sel = item === closest;\n const wasSelected = item.classList.contains('lt__item--selected');\n item.classList.toggle('lt__item--selected', sel);\n item.setAttribute('aria-selected', String(sel));\n item.tabIndex = sel ? 0 : -1;\n\n if (sel === wasSelected) return;\n const monthEl = item.querySelector<HTMLElement>('.lt__month-label');\n if (!monthEl) return;\n\n if (sel) {\n monthEl.textContent = item.getAttribute('data-month-text') ?? '';\n } else {\n monthEl.textContent = item.getAttribute('data-orig-label') ?? '';\n }\n });\n\n const centerYearStr = closest.getAttribute('data-year');\n if (centerYearStr) {\n const y = parseFloat(centerYearStr);\n this._emitItemPreview(y);\n if (this.onWeekPreviewCb && inDateRange(y)) {\n const date = normalizeDate(yearToDate(y));\n const start = getWeekStart(date, this.firstDayOfWeek);\n this.onWeekPreviewCb({ date: cloneDate(date), week: { start, end: getWeekEnd(start) } });\n }\n }\n }\n\n // ─── Drag (Pointer Events) ───────────────────────────────────────────────────\n\n private _bindDragEvents(): void {\n const onPointerDown = (e: PointerEvent) => {\n if (e.button !== 0 && e.pointerType === 'mouse') return;\n if (this._isPinching) return;\n if (this.rafId !== void 0) { cancelAnimationFrame(this.rafId); this.rafId = void 0; }\n if (this.springRafId !== void 0) { cancelAnimationFrame(this.springRafId); this.springRafId = void 0; }\n\n this.listWrapper.setPointerCapture(e.pointerId);\n this.isDragging = true;\n this.dragStartX = e.clientX;\n this.dragStartScroll = this.listWrapper.scrollLeft;\n this.velX = 0;\n this.lastMoveX = e.clientX;\n this.lastMoveT = performance.now();\n this.listWrapper.classList.add('is-dragging');\n };\n\n const onPointerMove = (e: PointerEvent) => {\n if (!this.isDragging) return;\n this.listWrapper.scrollLeft = this.dragStartScroll + (this.dragStartX - e.clientX);\n\n const now = performance.now();\n const dt = now - this.lastMoveT;\n if (dt > 0 && dt < 100) this.velX = (this.lastMoveX - e.clientX) / dt;\n this.lastMoveX = e.clientX;\n this.lastMoveT = now;\n\n this._scheduleVisuals();\n };\n\n const onPointerUp = () => {\n if (!this.isDragging) return;\n this.isDragging = false;\n this.listWrapper.classList.remove('is-dragging');\n this._startMomentum();\n };\n\n this.listWrapper.addEventListener('pointerdown', onPointerDown);\n this.listWrapper.addEventListener('pointermove', onPointerMove);\n this.listWrapper.addEventListener('pointerup', onPointerUp);\n this.listWrapper.addEventListener('pointercancel', onPointerUp);\n\n this.cleanupFns.push(\n () => this.listWrapper.removeEventListener('pointerdown', onPointerDown),\n () => this.listWrapper.removeEventListener('pointermove', onPointerMove),\n () => this.listWrapper.removeEventListener('pointerup', onPointerUp),\n () => this.listWrapper.removeEventListener('pointercancel', onPointerUp),\n );\n }\n\n // ─── Molette souris ─────────────────────────────────────────────────────────\n\n private _bindWheelEvents(): void {\n const onWheel = (e: WheelEvent) => {\n e.preventDefault();\n if (this.wheelDebounceId !== void 0) return;\n\n this.wheelDebounceId = window.setTimeout(() => {\n this.wheelDebounceId = void 0;\n }, WHEEL_DEBOUNCE_MS);\n\n if (e.deltaY > 0 || e.deltaX > 0) this.goToNext();\n else this.goToPrevious();\n };\n\n this.listWrapper.addEventListener('wheel', onWheel, { passive: false });\n this.cleanupFns.push(() => this.listWrapper.removeEventListener('wheel', onWheel));\n }\n\n // ─── Momentum ──────────────────────────────────────────────────────────────\n\n private _startMomentum(): void {\n if (Math.abs(this.velX) < MOMENTUM_MIN_VEL) { this._snapToCenter(); return; }\n\n const tick = () => {\n this.velX *= MOMENTUM_FRICTION;\n this.listWrapper.scrollLeft += this.velX * 16;\n this._updateVisuals();\n\n if (Math.abs(this.velX) < MOMENTUM_MIN_VEL) {\n this.rafId = void 0;\n this._snapToCenter();\n return;\n }\n this.rafId = requestAnimationFrame(tick);\n };\n this.rafId = requestAnimationFrame(tick);\n }\n\n // ─── Snap ───────────────────────────────────────────────────────────────────\n\n private _snapToCenter(): void {\n const closest = this._getCenterItem();\n if (!closest) return;\n\n const yearStr = closest.getAttribute('data-year');\n if (!yearStr) return;\n\n const year = parseFloat(yearStr);\n const items = Array.from(this.listEl.querySelectorAll<HTMLElement>('.lt__item'));\n const idx = items.indexOf(closest);\n\n this._commitSelection(year);\n this._hapticFeedback();\n\n // Fallback : si l'extension proactive n'a pas pu se déclencher\n const itemStride = items.length > 1 && items[1] && items[0]\n ? items[1].offsetLeft - items[0].offsetLeft : 100;\n const wl = this.listWrapper;\n const scrollRemRight = wl.scrollWidth - wl.clientWidth - wl.scrollLeft;\n const scrollRemLeft = wl.scrollLeft;\n\n const atEdge =\n idx < EXTEND_THRESHOLD ||\n idx >= items.length - EXTEND_THRESHOLD ||\n scrollRemRight < itemStride * 2 ||\n scrollRemLeft < itemStride * 2;\n\n if (atEdge) {\n this.referenceYear = this.selectedYear;\n this._updateListDOM(false);\n const newSel = this.listEl.querySelector<HTMLElement>('.lt__item--selected');\n if (newSel) this._springSnap(newSel);\n return;\n }\n\n this._scrollToItem(closest, true, () => {\n this._applyScaleEffect();\n this._springSnap(closest);\n });\n }\n\n // ─── Keyboard ───────────────────────────────────────────────────────────────\n\n private _bindKeyboardEvents(): void {\n const onKeyDown = (e: KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowLeft': e.preventDefault(); this.goToPrevious(); break;\n case 'ArrowRight': e.preventDefault(); this.goToNext(); break;\n case 'Home': e.preventDefault(); this.goToToday(); break;\n }\n };\n this.listWrapper.addEventListener('keydown', onKeyDown);\n this.cleanupFns.push(() => this.listWrapper.removeEventListener('keydown', onKeyDown));\n }\n\n // ─── Pinch-to-zoom (touch) ───────────────────────────────────────────────────\n\n private _bindPinchEvents(): void {\n // Seuils : ratio > 1.3 → pinch out (zoom in) ; ratio < 0.77 → pinch in (zoom out)\n const PINCH_OUT_RATIO = 1.3;\n const PINCH_IN_RATIO = 0.77;\n\n const dist = (a: Touch, b: Touch): number =>\n Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY);\n\n const onTouchStart = (e: TouchEvent) => {\n if (e.touches.length !== 2) return;\n const a = e.touches[0], b = e.touches[1];\n if (!a || !b) return;\n this._isPinching = true;\n this._pinchInitialDist = dist(a, b);\n // Annuler tout drag en cours\n this.isDragging = false;\n this.listWrapper.classList.remove('is-dragging');\n };\n\n const onTouchMove = (e: TouchEvent) => {\n if (!this._isPinching || e.touches.length !== 2) return;\n e.preventDefault();\n const a = e.touches[0], b = e.touches[1];\n if (!a || !b || this._pinchInitialDist === 0) return;\n const ratio = dist(a, b) / this._pinchInitialDist;\n if (ratio > PINCH_OUT_RATIO) {\n this._stepScale(1); // pinch out = échelle plus fine\n this._pinchInitialDist = dist(a, b); // reset pour le prochain step\n } else if (ratio < PINCH_IN_RATIO) {\n this._stepScale(-1); // pinch in = échelle plus grossière\n this._pinchInitialDist = dist(a, b);\n }\n };\n\n const onTouchEnd = (e: TouchEvent) => {\n if (e.touches.length < 2) {\n this._isPinching = false;\n this._pinchInitialDist = 0;\n }\n };\n\n this.listWrapper.addEventListener('touchstart', onTouchStart, { passive: true });\n this.listWrapper.addEventListener('touchmove', onTouchMove, { passive: false });\n this.listWrapper.addEventListener('touchend', onTouchEnd);\n this.listWrapper.addEventListener('touchcancel',onTouchEnd);\n\n this.cleanupFns.push(\n () => this.listWrapper.removeEventListener('touchstart', onTouchStart),\n () => this.listWrapper.removeEventListener('touchmove', onTouchMove),\n () => this.listWrapper.removeEventListener('touchend', onTouchEnd),\n () => this.listWrapper.removeEventListener('touchcancel',onTouchEnd),\n );\n }\n\n /** Décale l'échelle de `delta` positions (+1 = plus fine, -1 = plus grossière). */\n private _stepScale(delta: number): void {\n const idx = scaleIndex(this.scale.id) + delta;\n const next = scaleAt(idx);\n if (next) this.setScale(next.id); // setScale applique le clamp min/maxScale\n }\n\n // ─── Selection ──────────────────────────────────────────────────────────────\n\n /** Construit un TimelineItemInfo pour un year donné, à l'échelle courante. */\n private _buildItemInfo(year: number): TimelineItemInfo | null {\n const items = this.scale.build(year, 1, year, this.locale);\n const item = items[0];\n if (!item) return null;\n return {\n scale: this.scale.id,\n year: item.year,\n label: item.label,\n sublabel: item.sublabel,\n header: item.header,\n date: inDateRange(item.year) ? yearToDate(item.year) : null,\n };\n }\n\n private _emitItemChange(year: number): void {\n const info = this._buildItemInfo(year);\n if (!info) return;\n // ARIA : annonce l'item courant au lecteur d'écran\n if (this.liveRegion) {\n const parts = [info.label, info.sublabel].filter(s => s && s.trim().length > 0);\n this.liveRegion.textContent = parts.join(', ');\n }\n this.onItemChangeCb?.(info);\n }\n\n private _emitItemPreview(year: number): void {\n if (!this.onItemPreviewCb) return;\n const info = this._buildItemInfo(year);\n if (info) this.onItemPreviewCb(info);\n }\n\n private _commitSelection(year: number): void {\n const clamped = this._clampYear(year);\n this.selectedYear = clamped;\n this.referenceYear = clamped;\n this._writeStorage();\n this._emitItemChange(clamped);\n\n // Reassign 'year' var so the rest du flow utilise la valeur clampée\n year = clamped;\n\n // Callbacks Date-based : seulement quand le year est dans la plage Date\n if (!inDateRange(year)) return;\n\n const date = normalizeDate(yearToDate(year));\n const start = getWeekStart(date, this.firstDayOfWeek);\n const detail: LadderTimelineSelectEventDetail = {\n date: cloneDate(date),\n week: { start, end: getWeekEnd(start) },\n };\n\n this.container.dispatchEvent(createWeekChangeEvent(detail));\n this.container.dispatchEvent(createSelectEvent(detail));\n this.onWeekChangeCb?.(detail);\n this.calendarAdapter?.gotoDate(detail.week.start);\n }\n\n private _emitNavigate(direction: 'prev' | 'next' | 'today'): void {\n if (!inDateRange(this.selectedYear)) return;\n const date = normalizeDate(yearToDate(this.selectedYear));\n const start = getWeekStart(date, this.firstDayOfWeek);\n this.container.dispatchEvent(\n createNavigateEvent({ direction, date: cloneDate(date), week: { start, end: getWeekEnd(start) } }),\n );\n }\n\n // ─── Feedback haptique mobile ───────────────────────────────────────────────\n\n private _hapticFeedback(): void {\n if ('vibrate' in navigator) navigator.vibrate(8);\n }\n\n // ─── Thème ──────────────────────────────────────────────────────────────────\n\n private _setupTheme(): void {\n if (this._darkMq && this._darkMqHandler) {\n this._darkMq.removeEventListener('change', this._darkMqHandler);\n this._darkMqHandler = void 0;\n this._darkMq = void 0;\n }\n\n if (this.theme === 'dark') {\n this.root.classList.add('lt--dark');\n } else if (this.theme === 'auto' && 'matchMedia' in window) {\n this._darkMq = window.matchMedia('(prefers-color-scheme: dark)');\n this.root.classList.toggle('lt--dark', this._darkMq.matches);\n this._darkMqHandler = (e: MediaQueryListEvent) => {\n this.root.classList.toggle('lt--dark', e.matches);\n };\n this._darkMq.addEventListener('change', this._darkMqHandler);\n }\n }\n\n // ─── Persistence ────────────────────────────────────────────────────────────\n\n /**\n * Format storage : `v1:{year}`. Toute autre forme (ancien format, version\n * future inconnue) est ignorée silencieusement — permet de faire évoluer\n * la sérialisation sans casser les sessions existantes.\n */\n private static readonly STORAGE_VERSION = 'v1';\n\n private _readStorage(): number | null {\n if (!this.storageKey) return null;\n try {\n const raw = localStorage.getItem(this.storageKey);\n if (!raw) return null;\n const prefix = `${LadderTimeline.STORAGE_VERSION}:`;\n if (!raw.startsWith(prefix)) return null; // version inconnue → skip\n const n = parseFloat(raw.slice(prefix.length));\n return Number.isFinite(n) ? n : null;\n } catch {\n return null;\n }\n }\n\n private _writeStorage(): void {\n if (!this.storageKey) return;\n try {\n localStorage.setItem(this.storageKey, `${LadderTimeline.STORAGE_VERSION}:${this.selectedYear}`);\n } catch {\n // quota dépassé ou mode privé\n }\n }\n\n // ─── Resize ─────────────────────────────────────────────────────────────────\n\n private _initResizeObserver(): void {\n if (!('ResizeObserver' in window)) return;\n this.resizeObserver = new ResizeObserver(() => {\n this._applyCarouselPadding();\n this._syncCenterMark();\n const sel = this.listEl.querySelector<HTMLElement>('.lt__item--selected');\n if (sel) { this._scrollToItem(sel, false); this._applyScaleEffect(); }\n });\n this.resizeObserver.observe(this.listWrapper);\n }\n\n // ─── Debug ──────────────────────────────────────────────────────────────────\n\n debugState(): Record<string, unknown> {\n const items = Array.from(this.listEl?.querySelectorAll<HTMLElement>('.lt__item') ?? []);\n const wl = this.listWrapper;\n const scroll = wl?.scrollLeft ?? 0;\n const cw = wl?.clientWidth ?? 0;\n const sw = wl?.scrollWidth ?? 0;\n const viewCenter = scroll + cw / 2;\n\n let centerIdx = -1;\n let minDist = Infinity;\n items.forEach((item, i) => {\n const d = Math.abs(item.offsetLeft + item.offsetWidth / 2 - viewCenter);\n if (d < minDist) { minDist = d; centerIdx = i; }\n });\n\n const selIdx = items.findIndex(el => el.classList.contains('lt__item--selected'));\n const firstY = items[0]?.getAttribute('data-year') ?? '—';\n const lastY = items[items.length - 1]?.getAttribute('data-year') ?? '—';\n const maxScroll = sw - cw;\n\n const centerItem = items[centerIdx];\n const centerY = centerItem?.getAttribute('data-year') ?? '—';\n\n const selItem = items[selIdx];\n const selY = selItem?.getAttribute('data-year') ?? '—';\n\n const distToMax = Math.round(maxScroll - scroll);\n const nearEdgeRight = centerIdx > items.length - 1 - EXTEND_THRESHOLD;\n const nearEdgeLeft = centerIdx < EXTEND_THRESHOLD;\n\n const itemW = (items[1] && items[0])\n ? items[1].offsetLeft - items[0].offsetLeft\n : 0;\n\n const weeksToRight = items.length - 1 - centerIdx;\n const weeksToLeft = centerIdx;\n\n return {\n scale: this.scale.id,\n selectedYear: this.selectedYear,\n referenceYear: this.referenceYear,\n isDragging: this.isDragging,\n rafActive: this.rafId !== void 0,\n velX: +this.velX.toFixed(3),\n\n scrollLeft: Math.round(scroll),\n maxScrollLeft: Math.round(maxScroll),\n distToRight: distToMax,\n scrollWidth: Math.round(sw),\n clientWidth: Math.round(cw),\n paddingLeft: this.listEl?.style.paddingLeft ?? '?',\n paddingRight: this.listEl?.style.paddingRight ?? '?',\n itemWidth: Math.round(itemW),\n\n totalItems: items.length,\n weeksToLeft,\n weeksToRight,\n\n centerIdx,\n centerYear: centerY,\n selIdx,\n selYear: selY,\n centerMatchSel: centerIdx === selIdx,\n\n EXTEND_THRESHOLD,\n nearEdgeLeft,\n nearEdgeRight,\n extendWillFire: nearEdgeLeft || nearEdgeRight,\n\n firstYear: firstY,\n lastYear: lastY,\n };\n }\n\n // ─── Cleanup ────────────────────────────────────────────────────────────────\n\n private _cleanup(): void {\n this.cleanupFns.forEach(fn => fn());\n this.cleanupFns = [];\n }\n}\n","/**\n * FullCalendarAdapter\n *\n * Bridges LadderTimeline with a FullCalendar instance without creating\n * a direct import dependency on FullCalendar inside the component.\n *\n * The component only depends on the `CalendarAdapter` interface:\n * interface CalendarAdapter { gotoDate(date: Date): void; }\n *\n * Usage example (see also main.ts):\n *\n * import { Calendar } from '@fullcalendar/core';\n * import { createFullCalendarAdapter, bindTimelineToCalendar } from './adapters/FullCalendarAdapter';\n *\n * const calendar = new Calendar(calendarEl, { ... });\n * calendar.render();\n *\n * const timeline = new LadderTimeline({ container: timelineEl });\n * timeline.connectAdapter(createFullCalendarAdapter(calendar));\n *\n * // Or use the convenience helper:\n * const unbind = bindTimelineToCalendar(timeline, calendar);\n */\n\nimport type { CalendarAdapter } from '../components/ladder-timeline/LadderTimeline.types';\nimport { onWeekChange } from '../components/ladder-timeline/LadderTimeline.events';\n\n// ─── Minimal FullCalendar type surface ────────────────────────────────────────\n// We only declare what we use, so no FullCalendar import is needed at compile time.\n\nexport interface FullCalendarInstance {\n gotoDate(date: Date | string): void;\n getDate(): Date;\n setOption?(option: string, value: unknown): void;\n}\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\n/**\n * Wrap a FullCalendar instance in the CalendarAdapter interface.\n * The adapter forwards `gotoDate` calls to FullCalendar.\n */\nexport function createFullCalendarAdapter(calendar: FullCalendarInstance): CalendarAdapter {\n return {\n gotoDate(date: Date): void {\n calendar.gotoDate(date);\n },\n };\n}\n\n// ─── Convenience binder ───────────────────────────────────────────────────────\n\nexport interface BindableTimeline {\n connectAdapter(adapter: CalendarAdapter): void;\n getEventTarget(): EventTarget;\n}\n\n/**\n * Bind a LadderTimeline (class variant) to a FullCalendar instance bidirectionally.\n *\n * - When the user picks a week in the timeline → calendar.gotoDate() is called.\n * - Returns an `unbind` function to clean up listeners.\n */\nexport function bindTimelineToCalendar(\n timeline: BindableTimeline,\n calendar: FullCalendarInstance,\n): () => void {\n const adapter = createFullCalendarAdapter(calendar);\n timeline.connectAdapter(adapter);\n\n // Listen for weekchange events on the container and forward to calendar\n const unsubscribe = onWeekChange(timeline.getEventTarget(), (e) => {\n calendar.gotoDate(e.detail.week.start);\n });\n\n return unsubscribe;\n}\n\n// ─── EventTarget variant binder ───────────────────────────────────────────────\n\nimport type { LadderTimelineSelectEventDetail } from '../components/ladder-timeline/LadderTimeline.types';\nimport { EVENT_WEEK_CHANGE } from '../components/ladder-timeline/LadderTimeline.events';\n\n/**\n * Bind a LadderTimelineEventTarget instance to a FullCalendar instance.\n * Use this variant when using the EventTarget-based LadderTimeline.\n */\nexport function bindEventTargetTimelineToCalendar(\n timeline: EventTarget & { connectAdapter(a: CalendarAdapter): void },\n calendar: FullCalendarInstance,\n): () => void {\n const adapter = createFullCalendarAdapter(calendar);\n timeline.connectAdapter(adapter);\n\n const handler = (e: Event) => {\n const detail = (e as CustomEvent<LadderTimelineSelectEventDetail>).detail;\n calendar.gotoDate(detail.week.start);\n };\n timeline.addEventListener(EVENT_WEEK_CHANGE, handler);\n return () => timeline.removeEventListener(EVENT_WEEK_CHANGE, handler);\n}\n\n// ─── Web Component binder ─────────────────────────────────────────────────────\n\n/**\n * Bind a <ladder-timeline> Web Component to a FullCalendar instance.\n *\n * Example:\n * const el = document.querySelector('ladder-timeline')!;\n * bindWebComponentToCalendar(el, calendar);\n */\nexport function bindWebComponentToCalendar(\n el: HTMLElement & { connectAdapter?(a: CalendarAdapter): void },\n calendar: FullCalendarInstance,\n): () => void {\n const adapter = createFullCalendarAdapter(calendar);\n el.connectAdapter?.(adapter);\n\n const handler = (e: Event) => {\n const detail = (e as CustomEvent<LadderTimelineSelectEventDetail>).detail;\n calendar.gotoDate(detail.week.start);\n };\n el.addEventListener(EVENT_WEEK_CHANGE, handler);\n return () => el.removeEventListener(EVENT_WEEK_CHANGE, handler);\n}\n"],"names":["normalizeDate","d","cloneDate","addDays","days","result","getWeekStart","firstDayOfWeek","normalized","diff","getWeekEnd","weekStart","isValidDate","EVENT_WEEK_CHANGE","EVENT_NAVIGATE","EVENT_SELECT","createWeekChangeEvent","detail","createSelectEvent","createNavigateEvent","onWeekChange","target","handler","listener","e","DATE_MIN_YEAR","DATE_MAX_YEAR","inDateRange","year","yearToDate","y","frac","startOfY","startOfNextY","dateToYear","todayYear","fmtBigYear","abs","fmtYearShort","macroScale","id","label","step","n","centerYear","count","selectedYear","_locale","half","start","today","items","i","calendarYearBucketScale","yi","next","lbl","sub","compact","yearScale","isNewDecade","monthScale","locale","center","todayD","todayM","selM","fmtMonth","fmtShort","long","short","labelFull","labelAbbr","isJan","ONE_WEEK_YEARS","startOfWeek","isoWeekNumber","date","yearStart","weekScale","todayWeekStart","selStart","fmtRange","fmtMonthLong","end","range","monthName","ONE_DAY_YEARS","dayScale","todayKey","selKey","fmtWD","wd","MS_PER_YEAR","subDayScale","msPerStep","fmtFn","compactFn","stepYears","ms","todayMs","todayBucket","selBucket","fmtDay","centerMs","hourScale","minuteScale","secondScale","subSecondScale","unit","divisor","sel","msScale","SCALES","SCALE_BY_ID","acc","s","getScale","SCALE_ORDER_BY_ID","m","scaleIndex","scaleAt","index","SCALE_COUNT","CAROUSEL_WEEKS","EXTEND_THRESHOLD","MOMENTUM_FRICTION","MOMENTUM_MIN_VEL","SCALE_MIN","OPACITY_MIN","WHEEL_DEBOUNCE_MS","DEFAULTS","_LadderTimeline","options","__publicField","requestedScale","stored","t","clampedId","mode","markers","marker","before","finest","coarsest","snapped","prev","idx","clampedIdx","adapter","event","header","coarser","tol","prevHeader","it","isNewHeader","isMajor","isCompact","minimised","li","monthEl","dot","itemMarkers","stack","ev","itemYear","animate","wl","viewCenter","itemStride","centerIdx","minDist","item","PROACTIVE","scrollRemRight","scrollRemLeft","centerEl","centerYearStr","scrollOffsetFromCenter","newCenterEl","newScroll","ww","first","last","padLeft","padRight","el","onComplete","t0","easeOut","tick","now","closest","maxDist","ratio","scale","opacity","DURATION","A","FREQ","spring","wasSelected","onPointerDown","onPointerMove","dt","onPointerUp","onWheel","yearStr","newSel","onKeyDown","dist","a","b","onTouchStart","onTouchMove","onTouchEnd","delta","info","parts","clamped","direction","raw","prefix","scroll","cw","sw","selIdx","firstY","lastY","maxScroll","centerY","selY","distToMax","nearEdgeRight","nearEdgeLeft","itemW","weeksToRight","weeksToLeft","fn","LadderTimeline","createFullCalendarAdapter","calendar","bindTimelineToCalendar","timeline"],"mappings":"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"}
package/dist/main.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import './components/ladder-timeline/LadderTimeline.scss';
2
+ import { LadderTimeline } from './components/ladder-timeline/LadderTimeline';
3
+ declare global {
4
+ interface Window {
5
+ timelineA?: LadderTimeline;
6
+ }
7
+ }
8
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,kDAAkD,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAK7E,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QAAG,SAAS,CAAC,EAAE,cAAc,CAAC;KAAE;CACjD"}
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ @charset "UTF-8";:root{--wt-accent: #033249;--wt-accent-light: #e0eaf0;--wt-accent-hover: #022538;--wt-bg: #ffffff;--wt-border: #e8eaed;--wt-text-primary: #1a1d23;--wt-text-secondary: #6b7280;--wt-text-muted: #9ca3af;--wt-hover-bg: #f3f4f6;--wt-current-dot: #10b981}.lt--dark,[data-theme=dark]{--wt-bg: #0f1117;--wt-border: #2a2d36;--wt-text-primary: #f1f3f7;--wt-text-secondary: #9ca3af;--wt-text-muted: #6b7280;--wt-hover-bg: #1c1f29;--wt-accent-light: #0a2233}.lt{display:flex;flex-direction:column;gap:8px;background:var(--wt-bg);border:1px solid var(--wt-border);border-radius:12px;padding:12px 16px;box-shadow:0 1px 3px #00000014;font-family:system-ui,-apple-system,Segoe UI,sans-serif;color:var(--wt-text-primary)}.lt *,.lt *:before,.lt *:after{box-sizing:border-box}.lt--compact{padding:8px 12px;gap:6px}.lt__header{display:flex;align-items:center;gap:6px;min-width:0}.lt__list-wrapper{position:relative;flex:1;min-width:0;overflow:hidden;cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none}.lt__list-wrapper:before,.lt__list-wrapper:after{content:"";position:absolute;top:0;bottom:0;width:72px;pointer-events:none;z-index:2;transition:opacity .2s cubic-bezier(.4,0,.2,1)}.lt__list-wrapper:before{left:0;background:linear-gradient(to right,var(--wt-bg) 10%,transparent)}.lt__list-wrapper:after{right:0;background:linear-gradient(to left,var(--wt-bg) 10%,transparent)}.lt__list-wrapper.is-dragging{cursor:grabbing}.lt__list-wrapper.is-dragging:before,.lt__list-wrapper.is-dragging:after{opacity:.6}.lt__center-mark{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);border-radius:999px;border:1.5px solid var(--wt-accent);opacity:.18;pointer-events:none;z-index:1}.lt__list{display:flex;gap:4px;list-style:none;margin:0;padding:4px 0}.lt__item{flex-shrink:0;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;padding:6px 14px 8px;border-radius:999px;border:1.5px solid transparent;cursor:inherit;transition:background .2s cubic-bezier(.4,0,.2,1),border-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1),box-shadow .2s cubic-bezier(.4,0,.2,1)}.is-dragging .lt__item{transition:none}.lt__item--current:not(.lt__item--selected){border-color:var(--wt-accent);background:var(--wt-accent-light)}.lt__item--current:not(.lt__item--selected) .lt__item-label{color:var(--wt-accent)}.lt__item--current:not(.lt__item--selected) .lt__item-sublabel{color:var(--wt-accent);opacity:.75}.lt__item--selected{background:var(--wt-accent);border-color:var(--wt-accent-hover);box-shadow:0 2px 12px #0332494d}.lt__item--selected .lt__item-label,.lt__item--selected .lt__item-sublabel{color:#fff}.lt__item--minor{padding:6px 6px 8px;opacity:.55}.lt__item--minor .lt__item-label{font-size:11px;font-weight:400;letter-spacing:0}.lt__item--minor .lt__item-label:before{content:"· ";color:#00000059}.lt__item--minor .lt__month-label,.lt__item--minor .lt__item-sublabel{display:none}.lt__item--major:not(.lt__item--selected) .lt__item-label{font-weight:700}.lt__item:focus-visible{outline:2px solid var(--wt-accent);outline-offset:3px}.lt--compact .lt__item{padding:5px 10px}.lt__month-label{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:transparent;white-space:nowrap;pointer-events:none;height:11px;line-height:11px}.lt__month-label:not(:empty){color:var(--wt-text-muted)}.lt__month-label--year:not(:empty){color:var(--wt-accent);font-weight:800;font-size:8px;letter-spacing:.12em;opacity:.85}.lt__item--selected .lt__month-label--year:not(:empty){color:#fffc;opacity:1}.lt__item--selected .lt__month-label:not(:empty){color:#ffffff80}.lt__item--current:not(.lt__item--selected) .lt__month-label:not(:empty){color:var(--wt-accent);opacity:.65}.lt__item-dot{width:5px;height:5px;border-radius:50%;background:var(--wt-current-dot);flex-shrink:0;margin-top:2px}.lt__item--selected .lt__item-dot{background:#ffffffbf}.lt__item-label{font-size:13px;font-weight:600;line-height:1.2;color:var(--wt-text-primary);white-space:nowrap;pointer-events:none}.lt--compact .lt__item-label{font-size:12px}.lt__item-sublabel{font-size:10px;font-weight:400;line-height:1;color:var(--wt-text-muted);white-space:nowrap;pointer-events:none}.lt--compact .lt__item-sublabel{font-size:9px}@media (max-width: 768px){.lt{padding:10px 12px}.lt__item{padding:7px 11px}.lt__item-label{font-size:12px}.lt__item-sublabel{display:none}.lt__list-wrapper:before,.lt__list-wrapper:after{width:48px}}@media (max-width: 480px){.lt{padding:8px 10px;gap:6px;border-radius:10px}.lt__item{padding:6px 10px}.lt__item-label{font-size:11px}.lt__list-wrapper:before,.lt__list-wrapper:after{width:32px}}.lt__marker-stack{position:absolute;top:-4px;left:50%;transform:translate(-50%);display:flex;gap:2px;pointer-events:auto}.lt__marker{width:8px;height:8px;border-radius:50%;border:1.5px solid #fff;background:var(--wt-accent);padding:0;cursor:pointer;box-shadow:0 1px 2px #00000040;transition:transform .15s}.lt__marker:hover{transform:scale(1.4)}.lt__marker:focus-visible{outline:2px solid var(--wt-accent);outline-offset:2px}.lt__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synapxlab/ladder-timeline",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Multi-scale temporal carousel — drag/zoom across 18 scales from billion-years to nanoseconds. Zero runtime dependency, TypeScript + SCSS.",
5
5
  "type": "module",
6
6
  "main": "./dist/ladder-timeline.umd.cjs",
@@ -16,7 +16,8 @@
16
16
  },
17
17
  "files": [
18
18
  "dist",
19
- "README.md"
19
+ "README.md",
20
+ "README.fr.md"
20
21
  ],
21
22
  "sideEffects": [
22
23
  "**/*.css"