histropediajs 1.4.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.
package/index.d.ts ADDED
@@ -0,0 +1,1464 @@
1
+ // Type definitions for HistropediaJS – public API only
2
+
3
+ // ------------------------------------------------------------------------------------
4
+ // Helpers
5
+ // ------------------------------------------------------------------------------------
6
+
7
+ /**
8
+ * Deeply partial helper for ergonomic option updates.
9
+ *
10
+ * Notes:
11
+ * - Functions are left as-is (not recursed).
12
+ * - Arrays/tuples are treated as replace-whole values (not recursed).
13
+ */
14
+ export type DeepPartial<T> = T extends (...args: any) => any
15
+ ? T
16
+ : T extends readonly any[]
17
+ ? T
18
+ : T extends object
19
+ ? { [K in keyof T]?: DeepPartial<T[K]> }
20
+ : T;
21
+
22
+ /** Built-in card layouts stay in autocomplete while custom registered names still type-check. */
23
+ export type CardLayoutName = 'portrait' | 'landscape' | (string & {});
24
+
25
+ /** Runtime visibility state used by lanes and time bands. */
26
+ export type VisibilityState = 'visible' | 'hidden';
27
+
28
+ /** Canvas-relative 2D position used by rendered articles. */
29
+ export interface CanvasPosition {
30
+ left: number;
31
+ top: number;
32
+ }
33
+
34
+ /** Main-line indicator coordinates for an article currently in range. */
35
+ export interface ArticleIndicator {
36
+ fromX: number;
37
+ toX: number;
38
+ }
39
+
40
+ /** Computed article time span after precision expansion. */
41
+ export interface ArticlePeriod {
42
+ from: DmyInstance;
43
+ to: DmyInstance;
44
+ isToPresent: boolean;
45
+ /** Approximate duration in decimal years, used for sorting/layout decisions. */
46
+ years: number;
47
+ }
48
+
49
+ /** Computed time-band span after precision expansion. */
50
+ export interface TimeBandPeriod {
51
+ from: DmyInstance;
52
+ to: DmyInstance;
53
+ isToPresent: boolean;
54
+ }
55
+
56
+ /** Hit area metadata returned by card layouts when the star icon is visible. */
57
+ export interface CardLayoutIconBox extends CanvasPosition {
58
+ width: number;
59
+ height: number;
60
+ centreX: number;
61
+ centreY: number;
62
+ innerRadius: number;
63
+ outerRadius: number;
64
+ }
65
+
66
+ // ------------------------------------------------------------------------------------
67
+ // Date and precision
68
+ // ------------------------------------------------------------------------------------
69
+
70
+ /** Numeric precision constants (public). */
71
+ export const PRECISION_DAY: 11;
72
+ export const PRECISION_MONTH: 10;
73
+ export const PRECISION_YEAR: 9;
74
+ export const PRECISION_DECADE: 8;
75
+ export const PRECISION_CENTURY: 7;
76
+ export const PRECISION_MILLENNIUM: 6;
77
+ export const PRECISION_MILLION_YEARS: 3;
78
+ export const PRECISION_BILLION_YEARS: 1;
79
+
80
+ /**
81
+ * Date precision used by Article/TimeBand ranges.
82
+ * Matches the numeric constants exported above.
83
+ */
84
+ export type Precision =
85
+ | typeof PRECISION_DAY
86
+ | typeof PRECISION_MONTH
87
+ | typeof PRECISION_YEAR
88
+ | typeof PRECISION_DECADE
89
+ | typeof PRECISION_CENTURY
90
+ | typeof PRECISION_MILLENNIUM
91
+ | typeof PRECISION_MILLION_YEARS
92
+ | typeof PRECISION_BILLION_YEARS;
93
+
94
+ /** Plain YMD date parts (no precision). */
95
+ export type DateParts = {
96
+ year: number;
97
+ month?: number;
98
+ day?: number;
99
+ };
100
+
101
+ /** General date input that can include precision. */
102
+ export type DateInput = DateParts & { precision?: Precision };
103
+
104
+ /** Internal date input shape (consumer-friendly). */
105
+ export interface Dmy {
106
+ year: number;
107
+ month?: number;
108
+ day?: number;
109
+ precision?: Precision;
110
+ }
111
+
112
+ /** Formatting options for Dmy.format. */
113
+ export interface DmyFormatOptions {
114
+ yearPrefix?: { value?: number; label?: string };
115
+ bceText?: string;
116
+ thousandsSeparator?: string;
117
+ }
118
+
119
+ /** Histropedia date helper instance. */
120
+ export interface DmyInstance extends Dmy {
121
+ getDaysTo(other: Dmy): number;
122
+ getMonthsTo(other: Dmy): number;
123
+ getYearsTo(other: Dmy): number;
124
+ getLeapYearsSinceYearZero(): number;
125
+ getDaysSinceYearZero(): number;
126
+ getDayOfYear(): number;
127
+ getNextDay(): DmyInstance;
128
+ addDays(amount: number): DmyInstance;
129
+ addMonths(amount: number): DmyInstance;
130
+ addYears(amount: number): DmyInstance;
131
+ getPreviousDay(): DmyInstance;
132
+ getNextMonth(): DmyInstance;
133
+ getPreviousMonth(): DmyInstance;
134
+ format(format?: string, options?: DmyFormatOptions): string;
135
+ isSame(other: Dmy): boolean;
136
+ isAfter(other: Dmy): boolean;
137
+ isBetween(from: Dmy, to: Dmy): boolean;
138
+ compare(other: Dmy): number;
139
+ toKeyString(): string;
140
+ asFloat(): number;
141
+ isInFuture(): boolean;
142
+ convertToIso(): DmyInstance;
143
+ }
144
+
145
+ export type FitDateRangeInput = DmyInstance | DateParts;
146
+
147
+ export interface DmyConstructor {
148
+ new (year: number, month?: number, day?: number): DmyInstance;
149
+ new (value: Dmy): DmyInstance;
150
+ readonly prototype: DmyInstance;
151
+ getByDayOfYear(year: number, dayOfYear: number): DmyInstance;
152
+ CreateAsStartOfPeriod(date: Dmy, precision?: Precision): DmyInstance;
153
+ CreateAsEndOfPeriod(date: Dmy, precision?: Precision): DmyInstance;
154
+ now(): DmyInstance;
155
+ fromString(dateString: string): DmyInstance;
156
+ }
157
+
158
+ export const Dmy: DmyConstructor;
159
+
160
+ // ------------------------------------------------------------------------------------
161
+ // Public constants (used in options)
162
+ // ------------------------------------------------------------------------------------
163
+
164
+ export const DENSITY_ALL: 0;
165
+ export const DENSITY_LOW: 1;
166
+ export const DENSITY_MEDIUM: 2;
167
+ export const DENSITY_HIGH: 3;
168
+ export type Density =
169
+ | typeof DENSITY_ALL
170
+ | typeof DENSITY_LOW
171
+ | typeof DENSITY_MEDIUM
172
+ | typeof DENSITY_HIGH;
173
+
174
+ export const RANGE_ALL: 0;
175
+ export const RANGE_SCREEN: 1;
176
+ export type AutoStackRange = typeof RANGE_ALL | typeof RANGE_SCREEN;
177
+
178
+ export const MINOR_MARKER: 0;
179
+ export const MAJOR_MARKER: 1;
180
+ export const ERA_MARKER: 2;
181
+ export type MarkerType = typeof MINOR_MARKER | typeof MAJOR_MARKER | typeof ERA_MARKER;
182
+
183
+ export const ZOOM_DAY: 0;
184
+ export const ZOOM_MONTH: 1;
185
+ export const ZOOM_YEAR: 2;
186
+ export const ZOOM_DECADE: 3;
187
+ export const ZOOM_CENTURY: 4;
188
+ export const ZOOM_MILLENNIUM: 5;
189
+ export const ZOOM_10_THOUSAND_YEARS: 6;
190
+ export const ZOOM_100_THOUSAND_YEARS: 7;
191
+ export const ZOOM_MILLION_YEARS: 8;
192
+ export const ZOOM_10_MILLION_YEARS: 9;
193
+ export const ZOOM_100_MILLION_YEARS: 10;
194
+ export const ZOOM_BILLION_YEARS: 11;
195
+ export type ZoomLevel =
196
+ | typeof ZOOM_DAY
197
+ | typeof ZOOM_MONTH
198
+ | typeof ZOOM_YEAR
199
+ | typeof ZOOM_DECADE
200
+ | typeof ZOOM_CENTURY
201
+ | typeof ZOOM_MILLENNIUM
202
+ | typeof ZOOM_10_THOUSAND_YEARS
203
+ | typeof ZOOM_100_THOUSAND_YEARS
204
+ | typeof ZOOM_MILLION_YEARS
205
+ | typeof ZOOM_10_MILLION_YEARS
206
+ | typeof ZOOM_100_MILLION_YEARS
207
+ | typeof ZOOM_BILLION_YEARS;
208
+
209
+ export interface YearPrefixSettings {
210
+ label: string;
211
+ value: number;
212
+ }
213
+
214
+ export const YEAR_PREFIXES: Readonly<Record<number, YearPrefixSettings>>;
215
+
216
+ export interface DensitySetting {
217
+ region: number;
218
+ zoom: {
219
+ from: number;
220
+ to: number;
221
+ };
222
+ low: number;
223
+ medium: number;
224
+ high: number;
225
+ all: number;
226
+ step: {
227
+ months: number;
228
+ days: number;
229
+ };
230
+ }
231
+
232
+ export const DENSITY_SETTINGS: readonly DensitySetting[];
233
+
234
+ export interface LoggerInstance {
235
+ setEnabled(enabled: boolean): void;
236
+ isEnabled(): boolean;
237
+ setPrefix(prefix: string): void;
238
+ getPrefix(): string;
239
+ log(...args: unknown[]): void;
240
+ warn(...args: unknown[]): void;
241
+ error(...args: unknown[]): void;
242
+ info(...args: unknown[]): void;
243
+ debug(...args: unknown[]): void;
244
+ group(...args: unknown[]): void;
245
+ groupCollapsed(...args: unknown[]): void;
246
+ groupEnd(): void;
247
+ }
248
+
249
+ export const Logger: LoggerInstance;
250
+
251
+ // ------------------------------------------------------------------------------------
252
+ // Animation types
253
+ // ------------------------------------------------------------------------------------
254
+
255
+ /** Allowed easing names used by animations. */
256
+ export type EasingName =
257
+ | 'linear'
258
+ | 'swing'
259
+ | 'easeInQuad'
260
+ | 'easeOutQuad'
261
+ | 'easeInOutQuad'
262
+ | 'easeInCubic'
263
+ | 'easeOutCubic'
264
+ | 'easeInOutCubic';
265
+
266
+ // ------------------------------------------------------------------------------------
267
+ // Style types
268
+ // ------------------------------------------------------------------------------------
269
+
270
+ export interface ArticleStyleText {
271
+ font?: string;
272
+ color?: string;
273
+ align?: 'left' | 'right' | 'center' | 'start' | 'end';
274
+ baseline?: 'top' | 'middle' | 'alphabetic' | 'bottom';
275
+ margin?: number;
276
+ lineHeight?: number;
277
+ numberOfLines?: number;
278
+ offsetY?: number;
279
+ }
280
+
281
+ export interface ArticleStyle {
282
+ /** @defaultValue 150 */
283
+ width?: number;
284
+ /** Landscape card layout only. @defaultValue 70 */
285
+ height?: number;
286
+ /** Card background fill color. @defaultValue '#e9e9e9' */
287
+ color?: string;
288
+ /** Inner card background. @defaultValue '#fff' */
289
+ backgroundColor?: string;
290
+ /** Portrait card layout only. @defaultValue 3 */
291
+ topRadius?: number;
292
+ /** Landscape card layout only. @defaultValue 4 */
293
+ borderRadius?: number;
294
+ /**
295
+ * Shorthand for the article image maximum height.
296
+ *
297
+ * @deprecated Deprecated since 1.3.0 — Use `image.maxHeight` instead.
298
+ * @defaultValue 400
299
+ */
300
+ maxImageHeight?: number;
301
+ image?: {
302
+ /** @defaultValue 'natural' */
303
+ shape?: 'natural' | 'square' | 'circle';
304
+ /** @defaultValue 0 */
305
+ margin?: number;
306
+ /** @defaultValue 400 */
307
+ maxHeight?: number;
308
+ /** @defaultValue 0 */
309
+ borderRadius?: number;
310
+ };
311
+ header?: {
312
+ /** Portrait card layout only. @defaultValue 50 */
313
+ height?: number;
314
+ text?: ArticleStyleText;
315
+ };
316
+ subheader?: {
317
+ /** Portrait card layout only. @defaultValue 30 */
318
+ height?: number;
319
+ /** Portrait card layout only. @defaultValue '#555' */
320
+ color?: string;
321
+ text?: ArticleStyleText;
322
+ };
323
+ shadow?: {
324
+ /** @defaultValue 0 */ x?: number;
325
+ /** @defaultValue 0 */ y?: number;
326
+ /** @defaultValue 0 */ amount?: number;
327
+ /** @defaultValue '#000' */ color?: string;
328
+ };
329
+ border?: {
330
+ /** @defaultValue '#ddd' */ color?: string;
331
+ /** @defaultValue 1 */ width?: number;
332
+ };
333
+ connectorLine?: {
334
+ /** @defaultValue true */ visible?: boolean;
335
+ /** @defaultValue 18 */ offsetX?: number;
336
+ /** @defaultValue -20 */ offsetY?: number;
337
+ /** @defaultValue 1 */ thickness?: number;
338
+ arrow?: {
339
+ /** @defaultValue 16 */ width?: number;
340
+ /** @defaultValue 45 */ height?: number;
341
+ };
342
+ };
343
+ star?: {
344
+ /** @defaultValue 16 */ width?: number;
345
+ /** @defaultValue 3 */ margin?: number;
346
+ };
347
+ }
348
+
349
+ export interface TimeBandStyleText {
350
+ font?: string;
351
+ color?: string;
352
+ align?: 'left' | 'right' | 'center' | 'start' | 'end';
353
+ baseline?: 'top' | 'middle' | 'alphabetic' | 'bottom';
354
+ /** Vertical align inside band area. */
355
+ verticalAlign?: 'top' | 'middle' | 'bottom';
356
+ /** Horizontal/vertical padding in px. @defaultValue 4 */
357
+ margin?: number;
358
+ /** Extra vertical offset in px. @defaultValue 0 */
359
+ offsetY?: number;
360
+ }
361
+
362
+ export interface TimeBandStyle {
363
+ /** @defaultValue 'rgba(200, 200, 200, 0.25)' */
364
+ backgroundColor?: string;
365
+ border?: { color?: string; width?: number };
366
+ text?: TimeBandStyleText;
367
+ }
368
+
369
+ export interface TimelineStyleMarkerKind {
370
+ /** @defaultValue 12 (minor) / 30 (major) */ height?: number;
371
+ /** Extend marker to canvas top/bottom. @defaultValue false */ extend?: boolean;
372
+ color?: string;
373
+ futureColor?: string;
374
+ }
375
+
376
+ export interface TimelineStyleOptions {
377
+ mainLine?: {
378
+ /** @defaultValue true */ visible?: boolean;
379
+ /** @defaultValue 'bottom' */ position?: 'top' | 'bottom';
380
+ /** thickness in px. @defaultValue 8 */ size?: number;
381
+ /** Draw small article position ticks. @defaultValue true */
382
+ showDateIndicators?: boolean;
383
+ };
384
+ draggingHighlight?: {
385
+ /** @defaultValue true */ visible?: boolean;
386
+ /** @defaultValue 'rgba(237, 247, 255, 0.5)' */ color?: string;
387
+ /** Up/down extents relative to the main line. Accepts px, `'edge'`, or `{ value: 'edge', padding?: number }`.
388
+ * @defaultValue {up: 0, down: 'edge'}
389
+ */
390
+ area?: { up?: AreaValue; down?: AreaValue };
391
+ };
392
+ marker?: {
393
+ minor?: TimelineStyleMarkerKind;
394
+ major?: TimelineStyleMarkerKind;
395
+ };
396
+ dateLabel?: {
397
+ minor?: {
398
+ font?: string;
399
+ color?: string;
400
+ futureColor?: string;
401
+ textAlign?: CanvasTextAlign;
402
+ offset?: { x?: number; y?: number };
403
+ /** Text appended for BCE labeling. */
404
+ bceText?: string;
405
+ /** Thousands separator for large year labels. */
406
+ thousandsSeparator?: string;
407
+ /** Map of year-scale prefixes to display (e.g. ka, Ma, Ga). */
408
+ yearPrefixes?: Record<string, { label: string; value: number; minDivision: number }>;
409
+ };
410
+ major?: {
411
+ font?: string;
412
+ color?: string;
413
+ futureColor?: string;
414
+ textAlign?: CanvasTextAlign;
415
+ offset?: { x?: number; y?: number };
416
+ bceText?: string;
417
+ thousandsSeparator?: string;
418
+ yearPrefixes?: Record<string, { label: string; value: number; minDivision: number }>;
419
+ };
420
+ };
421
+ cursor?: {
422
+ clickable?: string;
423
+ timeline?: { hover?: string; dragging?: string };
424
+ article?: { hoverDraggable?: string; dragging?: string };
425
+ };
426
+ }
427
+
428
+ // ------------------------------------------------------------------------------------
429
+ // Data schemas for loading
430
+ // ------------------------------------------------------------------------------------
431
+
432
+ export interface ArticleData {
433
+ id: string | number;
434
+ title: string;
435
+ subtitle?: string;
436
+ lane?: string | number;
437
+ from: DateInput;
438
+ to?: DateInput;
439
+ /** If true, the period ends at the present day. */
440
+ isToPresent?: boolean;
441
+ imageUrl?: string;
442
+ rank?: number;
443
+ starred?: boolean;
444
+ hiddenByFilter?: boolean | ((article: Article) => boolean);
445
+ hidePeriodLine?: boolean | ((article: Article) => boolean);
446
+ /** Initial offset from computed position (px). */
447
+ offsetLeft?: number;
448
+ /** Initial offset from computed position (px). */
449
+ offsetTop?: number;
450
+ /** Override default card layout for this article. */
451
+ cardLayout?: CardLayoutName;
452
+ style?: ArticleStyle;
453
+ hoverStyle?: ArticleStyle;
454
+ activeStyle?: ArticleStyle;
455
+ }
456
+
457
+ export interface ArticleCardLayoutBreakpoint {
458
+ maxHeight: number;
459
+ layout: CardLayoutName;
460
+ }
461
+
462
+ export interface TimeBandData {
463
+ id: string | number;
464
+ title: string;
465
+ from: DateInput;
466
+ to?: DateInput;
467
+ /** If true, the band ends at the present day. */
468
+ isToPresent?: boolean;
469
+ /** Visibility of this band: 'visible' | 'hidden' or a function returning one. Defaults to 'visible'. */
470
+ visibility?: VisibilityState | ((band: TimeBand) => VisibilityState);
471
+ style?: TimeBandStyle;
472
+ }
473
+
474
+ export interface LaneLayout {
475
+ height?: number;
476
+ /**
477
+ * Relative share of the remaining automatic lane height for lanes without a fixed `height`.
478
+ * Hidden lanes are excluded from the distribution.
479
+ *
480
+ * @defaultValue 1
481
+ */
482
+ heightWeight?: number;
483
+ header?: {
484
+ visible?: boolean;
485
+ height?: number;
486
+ padding?: { left?: number; right?: number };
487
+ };
488
+ }
489
+
490
+ /** Decorative lane chrome styles. Only explicitly provided values are written inline. */
491
+ export interface LaneStyle {
492
+ header?: {
493
+ backgroundColor?: string;
494
+ };
495
+ body?: {
496
+ backgroundColor?: string;
497
+ borderColor?: string;
498
+ borderWidth?: number;
499
+ borderRadius?: number;
500
+ };
501
+ title?: {
502
+ color?: string;
503
+ font?: string;
504
+ textAlign?: 'left' | 'center' | 'right';
505
+ };
506
+ }
507
+
508
+ export interface DistanceToBaselineResponsiveCardLayoutOptions {
509
+ /** Fraction of the available content height used to calculate the baseline distance. */
510
+ ratio?: number;
511
+ /** Minimum responsive baseline distance in px. */
512
+ min?: number;
513
+ /** Maximum responsive baseline distance in px. */
514
+ max?: number;
515
+ }
516
+
517
+ export interface DistanceToBaselineResponsiveOptions {
518
+ /** Enable responsive baseline distance calculations. @defaultValue true */
519
+ active?: boolean;
520
+ /**
521
+ * Restrict responsive baseline calculations to lanes only.
522
+ * When true, timelines without active lanes use the static `distanceToBaseline.value`.
523
+ *
524
+ * @defaultValue true
525
+ */
526
+ lanesOnly?: boolean;
527
+ /**
528
+ * Responsive baseline settings keyed by the resolved default card layout name.
529
+ *
530
+ * @defaultValue `{ portrait: { ratio: 0.75, min: 140, max: 350 }, landscape: { ratio: 0.45, min: 50, max: 160 } }`
531
+ */
532
+ byCardLayout?: Partial<Record<CardLayoutName, DistanceToBaselineResponsiveCardLayoutOptions>>;
533
+ }
534
+
535
+ export interface DistanceToBaselineOptions {
536
+ /**
537
+ * Distance of cards from their baseline in px:
538
+ * the shared timeline axis without lanes, or the lane bottom with lanes.
539
+ *
540
+ * @defaultValue 350
541
+ */
542
+ value?: number;
543
+ /** Responsive baseline distance settings. */
544
+ responsive?: DistanceToBaselineResponsiveOptions;
545
+ }
546
+
547
+ export interface LaneArticleOverrides {
548
+ defaultData?: Partial<ArticleData>;
549
+ /** Lane-local density grouping/filtering input. */
550
+ density?: ArticleOptions['density'];
551
+ defaultCardLayout?: CardLayoutName;
552
+ defaultStyle?: ArticleStyle;
553
+ defaultHoverStyle?: ArticleStyle;
554
+ defaultActiveStyle?: ArticleStyle;
555
+ layoutStyles?: ArticleOptions['layoutStyles'];
556
+ /** Distance of cards from this lane's baseline in px, plus responsive settings for this lane. */
557
+ distanceToBaseline?: DistanceToBaselineOptions;
558
+ /**
559
+ * @deprecated Deprecated since 1.3.4 — Use `distanceToBaseline.value` instead.
560
+ */
561
+ distanceToMainLine?: number;
562
+ autoStacking?: {
563
+ /** Enable or disable automatic row assignment for this lane. */
564
+ active?: boolean;
565
+ rowSpacing?: number;
566
+ fitToHeight?: boolean;
567
+ topGap?: number | ((timeline: Timeline) => number);
568
+ };
569
+ periodLine?: {
570
+ /** Additional gap from the active baseline in px. */
571
+ baselineGap?: number;
572
+ /** Gap between stacked period lines in px. */
573
+ spacing?: number;
574
+ /** Line thickness in px. */
575
+ thickness?: number;
576
+ };
577
+ }
578
+
579
+ export interface LaneData {
580
+ id: string | number;
581
+ title?: string;
582
+ visibility?: VisibilityState | ((lane: Lane) => VisibilityState);
583
+ /** Additional CSS class for the lane root beside `histropedia-lane` and `histropedia-lane-index-N`. */
584
+ className?: string;
585
+ /** Additional CSS class for the lane header beside `histropedia-lane-header`. */
586
+ headerClassName?: string;
587
+ /** Additional CSS class for the lane body beside `histropedia-lane-body`. */
588
+ bodyClassName?: string;
589
+ /** Additional CSS class for the lane title beside `histropedia-lane-title`. */
590
+ titleClassName?: string;
591
+ layout?: LaneLayout;
592
+ style?: LaneStyle;
593
+ article?: LaneArticleOverrides;
594
+ }
595
+
596
+ // ------------------------------------------------------------------------------------
597
+ // Timeline options
598
+ // ------------------------------------------------------------------------------------
599
+
600
+ export interface TimelineZoomOptions {
601
+ /** @defaultValue 34 */ initial?: number;
602
+ /** @defaultValue 0 */ minimum?: number;
603
+ /** @defaultValue 123 */ maximum?: number;
604
+ /** Zoom step multiplier per wheel unit. @defaultValue 0.1 */
605
+ wheelStep?: number;
606
+ /** Overall wheel speed. @defaultValue 3 */
607
+ wheelSpeed?: number;
608
+ /** Allow Ctrl+wheel zoom. @defaultValue false */
609
+ allowCtrlWheel?: boolean;
610
+ /** 'auto' picks smooth vs discrete from device; 'discrete' forces stepped. @defaultValue 'auto' */
611
+ wheelMode?: 'auto' | 'discrete';
612
+ /** Animate discrete/notched wheel zoom steps. */
613
+ discreteWheelAnimation?: {
614
+ /** Enable animated wheel zoom. @defaultValue true */
615
+ active?: boolean;
616
+ /** Animation duration in ms. @defaultValue 250 */
617
+ duration?: number;
618
+ };
619
+ /** Gain for proportional (trackpad) mode. @defaultValue 2 */
620
+ proportionalGain?: number;
621
+ /** Exponent for proportional (trackpad) mode. @defaultValue 1.1 */
622
+ proportionalExponent?: number;
623
+ /** Zoom ratio between unit-size levels. @defaultValue 0.8 */
624
+ ratio?: number;
625
+ unitSize?: {
626
+ /** Size of a day at zoom=0 (and density region size). @defaultValue 200 */
627
+ initial?: number;
628
+ /** Unit-size threshold to show minor labels. @defaultValue 48 */
629
+ showMinorLabels?: number;
630
+ /** Minimum unit size in px. @defaultValue 8 */
631
+ minimum?: number;
632
+ };
633
+ }
634
+
635
+ export interface PanMomentumOptions {
636
+ /** Continue timeline panning after a drag release. @defaultValue true */
637
+ active?: boolean;
638
+ /** Pointer movement sample window in ms used to estimate release velocity. @defaultValue 100 */
639
+ sampleWindow?: number;
640
+ /** Minimum release velocity in px/ms before momentum starts. @defaultValue 0.01 */
641
+ minVelocity?: number;
642
+ /** Velocity in px/ms at which momentum stops. @defaultValue 0.005 */
643
+ stopVelocity?: number;
644
+ /** Exponential friction coefficient per ms. Lower values travel farther. @defaultValue 0.004 */
645
+ friction?: number;
646
+ /** Maximum momentum duration in ms. @defaultValue 1200 */
647
+ maxDuration?: number;
648
+ }
649
+
650
+ export interface TimelinePanOptions {
651
+ /** Momentum behaviour after a timeline drag release. */
652
+ momentum?: PanMomentumOptions;
653
+ }
654
+
655
+ export interface ArticleOptions {
656
+ /** Density filter for visible articles. @defaultValue DENSITY_ALL */
657
+ density?: Density;
658
+ /** Allow dragging event cards. @defaultValue true */
659
+ draggable?: boolean;
660
+ /**
661
+ * Distance of cards from their baseline in px, plus responsive settings.
662
+ *
663
+ * Responsive calculations use the resolved default card layout for the timeline or lane.
664
+ */
665
+ distanceToBaseline?: DistanceToBaselineOptions;
666
+ /**
667
+ * @deprecated Deprecated since 1.3.4 — Use `distanceToBaseline.value` instead.
668
+ */
669
+ distanceToMainLine?: number;
670
+ /** Collect ongoing events on main line. @defaultValue false */
671
+ collectOngoing?: boolean;
672
+ /** Auto stacking options. */
673
+ autoStacking?: {
674
+ /** @defaultValue true */
675
+ active?: boolean;
676
+ /** Distance between rows in px. @defaultValue 50 */
677
+ rowSpacing?: number;
678
+ /** Range considered for stacking. @defaultValue RANGE_ALL */
679
+ range?: AutoStackRange;
680
+ /** Fit to available height by adjusting rowSpacing. @defaultValue true */
681
+ fitToHeight?: boolean;
682
+ /** Top gap in px or a function returning px. @defaultValue 10 */
683
+ topGap?: number | ((timeline: Timeline) => number);
684
+ };
685
+ periodLine?: {
686
+ /** Anchor period lines to the shared axis or each lane bottom. @defaultValue 'lane' */
687
+ position?: 'axis' | 'lane';
688
+ /**
689
+ * Default value merged into `article.defaultData.hidePeriodLine`.
690
+ *
691
+ * @deprecated Deprecated since 1.3.0 — Use `defaultData.hidePeriodLine` instead.
692
+ */
693
+ defaultHide?: boolean;
694
+ /** Additional gap from the active baseline in px. @defaultValue 0 */
695
+ baselineGap?: number;
696
+ /** Gap between stacked period lines in px. @defaultValue 4 */
697
+ spacing?: number;
698
+ /** Line thickness in px. @defaultValue 10 */
699
+ thickness?: number;
700
+ stacking?: {
701
+ /** Comparator used when assigning period-line stacks. */
702
+ sorter?: ArticleSorter;
703
+ /** Reverse stacking order. @defaultValue false */
704
+ reverseOrder?: boolean;
705
+ };
706
+ };
707
+ animation?: {
708
+ fade?: { active?: boolean; duration?: number; easing?: EasingName };
709
+ move?: { active?: boolean; duration?: number; easing?: EasingName };
710
+ };
711
+ star?: { /** Show star icon. @defaultValue true */ visible?: boolean };
712
+ /** Default data merged into every loaded article. */
713
+ defaultData?: Partial<ArticleData>;
714
+ /** Fallback card layout name used when no breakpoint matches. @defaultValue 'portrait' */
715
+ defaultCardLayout?: CardLayoutName;
716
+ /** Ordered max-height overrides evaluated before falling back to `defaultCardLayout`. */
717
+ cardLayoutBreakpoints?: ArticleCardLayoutBreakpoint[];
718
+ /** Base styles. */
719
+ defaultStyle?: ArticleStyle;
720
+ defaultHoverStyle?: ArticleStyle;
721
+ defaultActiveStyle?: ArticleStyle;
722
+ /** Per-layout style overrides registered via Timeline.registerCardLayout. */
723
+ layoutStyles?: Record<
724
+ string,
725
+ Partial<{
726
+ style: ArticleStyle;
727
+ hoverStyle: ArticleStyle;
728
+ activeStyle: ArticleStyle;
729
+ }>
730
+ >;
731
+ }
732
+
733
+ /** Pixels from the main line, the `'edge'` keyword, or `{ value: 'edge', padding?: number }` for edge-stretching with padding. */
734
+ export type AreaValue = number | 'edge' | { value: 'edge'; padding?: number };
735
+
736
+ export interface TimeBandOptions {
737
+ /** Enable drawing of bands. @defaultValue true */
738
+ visible?: boolean;
739
+ /** Reserve extra space near main line when bands are present. @defaultValue true */
740
+ reserveSpace?: boolean;
741
+ /** Pixels to reserve when reserveSpace is true. @defaultValue 20 */
742
+ reserveSpacePixels?: number;
743
+ /** Up/down extents of the band area relative to the main line. Accepts px, `'edge'`, or `{ value: 'edge', padding?: number }`. @defaultValue {up: 0, down: 'edge'} */
744
+ area?: { up?: AreaValue; down?: AreaValue };
745
+ /** Global default style merged into each band. */
746
+ defaultStyle?: TimeBandStyle;
747
+ /** Bands to load initially. */
748
+ data?: TimeBandData[];
749
+ }
750
+
751
+ export interface LaneOptions {
752
+ /**
753
+ * Enable lane DOM chrome. Article layout still remains lane-aware when lane data is present.
754
+ * @defaultValue true
755
+ */
756
+ visible?: boolean;
757
+ /** Default lane id used when an article does not specify `lane`. @defaultValue 'default' */
758
+ defaultId?: string | number;
759
+ /** Gap between rendered lanes in px. @defaultValue 12 */
760
+ gap?: number;
761
+ /** Space reserved above the topmost lane in px. @defaultValue 0 */
762
+ topGap?: number;
763
+ /** Space between the painted edge of the shared main line and the lane area in px. @defaultValue 24 */
764
+ axisGap?: number;
765
+ /** Global default lane layout merged into each lane. */
766
+ defaultLayout?: LaneLayout;
767
+ /** Global default lane style merged into each lane. Only explicitly provided values are written inline. */
768
+ defaultStyle?: LaneStyle;
769
+ /** CSS class added to each lane root element beside `histropedia-lane` and `histropedia-lane-index-N`. */
770
+ defaultClassName?: string;
771
+ /** CSS class added to each lane header element beside `histropedia-lane-header`. */
772
+ defaultHeaderClassName?: string;
773
+ /** CSS class added to each lane body element beside `histropedia-lane-body`. */
774
+ defaultBodyClassName?: string;
775
+ /** CSS class added to each lane title element beside `histropedia-lane-title`. */
776
+ defaultTitleClassName?: string;
777
+ /** Lanes to load initially. */
778
+ data?: LaneData[];
779
+ }
780
+
781
+ /**
782
+ * Image loading and URL sanitization options.
783
+ *
784
+ * By default, the built-in sanitizer allows http, https, data:image/* and blob:.
785
+ * You can restrict schemes and/or remote origins, or provide a custom sanitizer.
786
+ */
787
+ export type ImageDecodeMode = 'auto' | 'prefer-bitmap' | 'bitmap' | 'prefer-img' | 'img';
788
+
789
+ export interface ImageSanitizerOptions {
790
+ /**
791
+ * Allowed URL schemes for images.
792
+ * @defaultValue ['http', 'https', 'data', 'blob']
793
+ */
794
+ allowedSchemes?: string[];
795
+
796
+ /**
797
+ * Allowed origins/hosts for remote images. Supports full origins, bare hostnames,
798
+ * leading-dot, and wildcard-prefixed (`*.example.com`) entries.
799
+ */
800
+ allowedOrigins?: string[];
801
+ }
802
+
803
+ export interface ImageOptions {
804
+ /**
805
+ * Built-in URL sanitization controls.
806
+ */
807
+ sanitizer?: ImageSanitizerOptions;
808
+
809
+ /**
810
+ * Optional override for image URL sanitization. Receives the raw URL and the
811
+ * current sanitizer configuration from `image.sanitizer`. Return a trusted URL
812
+ * string to allow the load or null to block it.
813
+ */
814
+ customSanitizer?: (url: string, options: ImageSanitizerOptions) => string | null;
815
+
816
+ /**
817
+ * Maximum number of concurrent image fetch/decode operations. Values < 1 are clamped to 1.
818
+ * @defaultValue 6
819
+ */
820
+ maxConcurrent?: number;
821
+
822
+ /**
823
+ * Maximum decoded raster memory (in bytes) retained by the image cache.
824
+ * Set to `Infinity` to disable eviction, or reduce to bound memory usage.
825
+ * @defaultValue 64 * 1024 * 1024 (64 MiB)
826
+ */
827
+ maxCacheBytes?: number;
828
+
829
+ /**
830
+ * Force cross-origin requests to opt-in via CORS (sets `crossOrigin = 'anonymous'`).
831
+ * When true, images served without CORS headers will fail to load but canvas readbacks
832
+ * remain untainted.
833
+ * @defaultValue false
834
+ */
835
+ requireCORS?: boolean;
836
+
837
+ /**
838
+ * Controls how raster data is produced once an image loads.
839
+ *
840
+ * - 'auto' (default) prefers ImageBitmap when supported, falling back to `<img>`.
841
+ * - 'prefer-bitmap' aggressively tries ImageBitmap but falls back to `<img>` if needed.
842
+ * - 'bitmap' requires ImageBitmap support and treats failures as errors.
843
+ * - 'prefer-img' keeps `<img>` unless decoding fails, then falls back to ImageBitmap.
844
+ * - 'img' forces `<img>` and never calls `createImageBitmap`.
845
+ */
846
+ decodeMode?: ImageDecodeMode;
847
+ }
848
+
849
+ export interface TimelineOptions {
850
+ /** Canvas width in px. @defaultValue 1000 */
851
+ width?: number;
852
+ /** Canvas height in px. @defaultValue 500 */
853
+ height?: number;
854
+ /** Canvas/backing-store configuration. */
855
+ canvas?: {
856
+ /** Device pixel ratio to use; 'auto' follows device. @defaultValue 'auto' */ dpr?:
857
+ | 'auto'
858
+ | number;
859
+ /**
860
+ * Behaviour for dpr:'auto'.
861
+ * - 'stable' (default) rounds devicePixelRatio up to the nearest integer.
862
+ * - 'native' uses the raw devicePixelRatio value.
863
+ * @defaultValue 'stable'
864
+ */
865
+ dprMode?: 'stable' | 'native';
866
+ /** Maximum DPR to apply for performance. @defaultValue 3 */ maxDpr?: number;
867
+ };
868
+ /** Vertical offset of the main line in px. @defaultValue 40 */
869
+ verticalOffset?: number;
870
+ /** Enable user interactions (wheel/drag/pinch). @defaultValue true */
871
+ enableUserControl?: boolean;
872
+ /** Show interactive cursors. @defaultValue true */
873
+ enableCursor?: boolean;
874
+ /** Zoom configuration. */
875
+ zoom?: TimelineZoomOptions;
876
+ /** Initial date (top-left when position not offset). @defaultValue {year:1990,month:1,day:1} */
877
+ initialDate?: DateParts;
878
+ /** Shift BCE input dates by +1 so 1BCE = year 0 internally. @defaultValue false */
879
+ shiftBceDates?: boolean;
880
+ /** Hide the built-in Histropedia branding/footer links. Defaults to false when omitted. */
881
+ disableBranding?: boolean;
882
+ /**
883
+ * Drag activation vicinity near the main line.
884
+ * - boolean: enable/disable dragging anywhere on the timeline
885
+ * - function: (event, relativePosition) => boolean; return true to allow dragging
886
+ * where relativePosition is { left: number; top: number }
887
+ * - object: { up?: number; down?: number } in pixels from the main line
888
+ * @defaultValue true
889
+ */
890
+ draggingVicinity?:
891
+ | boolean
892
+ | ((event: PointerEvent, relativePosition: { left: number; top: number }) => boolean)
893
+ | { up?: number; down?: number };
894
+ /** Timeline panning configuration. */
895
+ pan?: TimelinePanOptions;
896
+ /** Canvas/UI styling. */
897
+ style?: TimelineStyleOptions;
898
+ /** Time band configuration. */
899
+ timeBand?: TimeBandOptions;
900
+ /** Lane configuration. */
901
+ lane?: LaneOptions;
902
+ /** Article configuration. */
903
+ article?: ArticleOptions;
904
+ /** Configure image sanitization, caching, and loader behaviour. */
905
+ image?: ImageOptions;
906
+ /** Register event handlers via object literal: { 'article-click': fn, ... } */
907
+ on?: Partial<EventHandlerMap>;
908
+ /**
909
+ * Called after every draw pass.
910
+ *
911
+ * @deprecated Deprecated since 1.3.0 — Use `on['timeline-render-end']` instead.
912
+ */
913
+ onRedraw?: (ctx: RedrawContext) => void;
914
+ /**
915
+ * Article click handler configured via this option.
916
+ *
917
+ * @deprecated Deprecated since 1.3.0 — Use `on['article-click']` for click events or `on['article-select']` for selection by any means.
918
+ */
919
+ onArticleClick?: (article: Article, event?: Event) => void;
920
+ /**
921
+ * Article double click handler configured via this option.
922
+ *
923
+ * @deprecated Deprecated since 1.3.0 — Use `on['article-dblclick']` instead.
924
+ */
925
+ onArticleDoubleClick?: (article: Article, event?: Event) => void;
926
+ /**
927
+ * Timeline save handler configured via this option.
928
+ *
929
+ * @deprecated Deprecated since 1.3.0 — Use `on['timeline-state-change']` instead.
930
+ */
931
+ onSave?: (state: string) => void;
932
+ }
933
+
934
+ // ------------------------------------------------------------------------------------
935
+ // Events
936
+ // ------------------------------------------------------------------------------------
937
+
938
+ /** Token produced by the timescale manager for marker drawing and viewport drag payloads. */
939
+ export interface TimelineToken {
940
+ unit: ZoomLevel;
941
+ value: DmyInstance;
942
+ length: number;
943
+ label?: string;
944
+ type?: MarkerType;
945
+ drawX?: number;
946
+ }
947
+
948
+ export interface ZoomPayload {
949
+ zoom: number;
950
+ zoomDelta: number;
951
+ }
952
+ export interface WheelPayload {
953
+ centre: { x: number; y: number };
954
+ zoom: number;
955
+ zoomDelta: number;
956
+ }
957
+ export interface PinchPayload {
958
+ centre: { x: number; y: number } | null;
959
+ scale: number;
960
+ zoom: number;
961
+ distance: number;
962
+ zoomDelta: number;
963
+ dx?: number;
964
+ totalZoomDelta: number;
965
+ pointers: PointerEvent[];
966
+ }
967
+ export interface ViewportDragPayload {
968
+ startToken: TimelineToken;
969
+ offsetX: number;
970
+ dx?: number;
971
+ totalDx?: number;
972
+ }
973
+ export interface TimelineClickPayload {
974
+ viewport: { x: number; y: number };
975
+ }
976
+ export interface DragDelta {
977
+ dx: number;
978
+ dy: number;
979
+ totalDx: number;
980
+ totalDy: number;
981
+ }
982
+
983
+ /** Token cache exposed to redraw hooks for advanced positioning work. */
984
+ export interface RedrawCycleContext {
985
+ tokens: {
986
+ start: DmyInstance;
987
+ end: DmyInstance;
988
+ [tokenKey: string]: DmyInstance | number;
989
+ };
990
+ }
991
+
992
+ /** Data delivered to redraw lifecycle handlers. */
993
+ export interface RedrawContext {
994
+ canvasContext: CanvasRenderingContext2D;
995
+ /** Present for timeline-render-end; omitted for timeline-render-start. */
996
+ drawCycleContext?: RedrawCycleContext;
997
+ top: number;
998
+ }
999
+
1000
+ export interface EventPayloadMap {
1001
+ // zoom
1002
+ zoom: [ZoomPayload];
1003
+ 'zoom-wheel': [WheelPayload, WheelEvent];
1004
+ 'zoom-pinch-start': [PinchPayload, PointerEvent];
1005
+ 'zoom-pinch': [PinchPayload, PointerEvent];
1006
+ 'zoom-pinch-end': [PinchPayload, PointerEvent];
1007
+ // viewport
1008
+ 'viewport-drag-start': [ViewportDragPayload, PointerEvent];
1009
+ 'viewport-drag': [ViewportDragPayload, PointerEvent];
1010
+ 'viewport-drag-end': [ViewportDragPayload, PointerEvent];
1011
+ // articles
1012
+ 'article-pointerenter': [Article, PointerEvent];
1013
+ 'article-pointermove': [Article, PointerEvent];
1014
+ 'article-pointerleave': [Article, PointerEvent];
1015
+ 'article-click': [Article, PointerEvent];
1016
+ 'article-dblclick': [Article, PointerEvent];
1017
+ 'article-select': [Article];
1018
+ 'article-drag-start': [Article, PointerEvent];
1019
+ 'article-drag': [Article, DragDelta, PointerEvent];
1020
+ 'article-drag-end': [Article, PointerEvent];
1021
+ // timeline
1022
+ 'timeline-render-start': [RedrawContext];
1023
+ 'timeline-render-end': [RedrawContext];
1024
+ 'timeline-click': [TimelineClickPayload, PointerEvent];
1025
+ 'timeline-dblclick': [TimelineClickPayload, PointerEvent];
1026
+ 'timeline-state-change': [string];
1027
+ }
1028
+
1029
+ export type TimelineEvent = keyof EventPayloadMap;
1030
+ export type EventHandlerMap = { [K in TimelineEvent]: (...args: EventPayloadMap[K]) => void };
1031
+
1032
+ /** Backward-compatible alias for the article instance passed as `this` to card layout hooks. */
1033
+ export type CardLayoutArticleContext = Article;
1034
+
1035
+ /** Full card layout definition stored by the runtime registry. */
1036
+ export interface RegisteredCardLayout {
1037
+ name: string;
1038
+ draw(this: Article, ctx: CanvasRenderingContext2D): void;
1039
+ getWidth?(this: Article): number;
1040
+ getHeight?(this: Article): number;
1041
+ getIconBox?(this: Article): CardLayoutIconBox | false;
1042
+ defaultStyle?: ArticleStyle;
1043
+ defaultHoverStyle?: ArticleStyle;
1044
+ defaultActiveStyle?: ArticleStyle;
1045
+ }
1046
+
1047
+ /** Alias definition that points one layout name at another registered layout. */
1048
+ export interface CardLayoutAliasDefinition {
1049
+ name: string;
1050
+ aliasOf: CardLayoutName;
1051
+ }
1052
+
1053
+ /** Definition accepted by `Timeline.registerCardLayout(...)`. */
1054
+ export type CardLayoutDefinition = RegisteredCardLayout | CardLayoutAliasDefinition;
1055
+
1056
+ // ------------------------------------------------------------------------------------
1057
+ // Classes
1058
+ // ------------------------------------------------------------------------------------
1059
+
1060
+ export interface AnimOptions {
1061
+ /** Duration in ms. @defaultValue 2000 */ duration?: number;
1062
+ /** Easing name. @defaultValue 'swing' */ easing?: EasingName;
1063
+ /** Callback when animation completes. */ complete?: () => void;
1064
+ /** Additional horizontal shift, used by pinch. */ offsetX?: number;
1065
+ }
1066
+
1067
+ export type FitDateRangePadding = number | { left?: number; right?: number };
1068
+
1069
+ export interface FitDateRangeAnimationOptions {
1070
+ /** Enable simultaneous animated zooming and panning. @defaultValue false */
1071
+ active?: boolean;
1072
+ /** Duration in ms. Inherits the viewport animation default when omitted. */
1073
+ duration?: number;
1074
+ /** Easing name. Inherits the viewport animation default when omitted. */
1075
+ easing?: EasingName;
1076
+ }
1077
+
1078
+ export interface FitDateRangeOptions {
1079
+ /** Edge padding in px, or per-side padding for asymmetric fits. @defaultValue 0 */
1080
+ padding?: FitDateRangePadding;
1081
+ /** Optional animation settings for the fit. */
1082
+ animation?: FitDateRangeAnimationOptions;
1083
+ }
1084
+
1085
+ export interface FitArticleRangeOptions {
1086
+ /** Edge padding in px, or per-side padding for asymmetric fits. @defaultValue 0 */
1087
+ padding?: FitDateRangePadding;
1088
+ /** Optional animation settings for the fit. */
1089
+ animation?: FitDateRangeAnimationOptions;
1090
+ }
1091
+
1092
+ export interface FitArticlesOptions {
1093
+ /** Space in px between the outermost fitted article cards and the viewport edge. @defaultValue 40 */
1094
+ padding?: FitDateRangePadding;
1095
+ /** Optional article predicate. Defaults to fitting articles that are not `hiddenByFilter`. */
1096
+ articleFilter?: (article: Article) => boolean;
1097
+ /** Optional animation settings for the fit. */
1098
+ animation?: FitDateRangeAnimationOptions;
1099
+ }
1100
+
1101
+ export class Timeline {
1102
+ /**
1103
+ * Create a timeline bound to an existing canvas element or a container element (a canvas will be created).
1104
+ * @example
1105
+ * const tl = new Timeline(containerEl, { initialDate: { year: 1066, month: 10, day: 14 } });
1106
+ */
1107
+ constructor(container: HTMLCanvasElement | HTMLElement, options?: TimelineOptions);
1108
+
1109
+ /** Root canvas used for rendering. */
1110
+ readonly canvas: HTMLCanvasElement;
1111
+ /** 2D drawing context bound to `canvas`. */
1112
+ readonly canvasContext: CanvasRenderingContext2D;
1113
+ /** Current merged runtime options. */
1114
+ options: TimelineOptions;
1115
+ /** User-visible title used by serialized state helpers. */
1116
+ title: string;
1117
+ /** Current y-coordinate of the timeline baseline in canvas pixels. */
1118
+ top: number;
1119
+ readonly articles: Article[];
1120
+ readonly lanes: Lane[];
1121
+ readonly timeBands: TimeBand[];
1122
+
1123
+ /** Register a reusable card layout globally for all timelines. */
1124
+ static registerCardLayout(definition: CardLayoutDefinition): void;
1125
+ /** Resolve a previously registered card layout by name. */
1126
+ static getCardLayout(name: CardLayoutName): RegisteredCardLayout | null;
1127
+
1128
+ // Data loading
1129
+ /** Load articles and redraw. */
1130
+ load(articles: ArticleData[]): void;
1131
+ /** Load lanes and redraw. */
1132
+ loadLanes(lanes: LaneData[]): void;
1133
+ /** Load time bands and redraw. */
1134
+ loadTimeBands(bands: TimeBandData[]): void;
1135
+ /** Stamp a lane id onto a batch of articles, load them, and redraw. */
1136
+ loadLaneArticles(laneId: string | number, articles: ArticleData[]): void;
1137
+
1138
+ // Query
1139
+ getArticleById(id: string | number): Article | undefined;
1140
+ getActiveArticle(): Article | null;
1141
+ getLaneById(id: string | number): Lane | undefined;
1142
+ hasArticle(id: string | number): boolean;
1143
+
1144
+ // Selection and ordering
1145
+ bringFront(articleId: string | number): void;
1146
+ /** Selects and brings the article to the front; emits 'article-select'. */
1147
+ select(articleId: string | number): void;
1148
+ /** Reorder articles to match the array of ids (topmost last). */
1149
+ reorderArticles(ids: Array<string | number>): void;
1150
+ /** Move a lane to a public lane index where 0 is nearest the axis. */
1151
+ moveLaneToIndex(lane: Lane | string | number, index: number): Lane;
1152
+ /** Move lane so it ends up immediately before targetLane. */
1153
+ moveLaneBefore(lane: Lane | string | number, targetLane: Lane | string | number): Lane;
1154
+ /** Move lane so it ends up immediately after targetLane. */
1155
+ moveLaneAfter(lane: Lane | string | number, targetLane: Lane | string | number): Lane;
1156
+ /** Move lane one position farther from the axis. */
1157
+ moveLaneUp(lane: Lane | string | number): Lane;
1158
+ /** Move lane one position closer to the axis. */
1159
+ moveLaneDown(lane: Lane | string | number): Lane;
1160
+ /** Update the serializable title stored by `getStatusForSave()`. */
1161
+ setTitle(title: string): void;
1162
+
1163
+ // Navigation & view
1164
+ /** Set start date (left edge). String input accepted in "-YYYY-MM-DD" style. */
1165
+ setStartDate(date: Dmy | string, pixelOffset?: number): void;
1166
+ /** Set centre date using startDate + offset computation. */
1167
+ setCentreDate(date: Dmy | string, pixelOffset?: number): void;
1168
+ /** Current zoom value. */
1169
+ getZoom(): number;
1170
+ /** Set zoom (optionally pivoting around a pixel). */
1171
+ setZoom(zoom: number, pivotPixel?: number, offsetX?: number): void;
1172
+ /** Smooth pan by pixels (negative = right). */
1173
+ goToPixelAnim(pixels: number, options?: AnimOptions): void;
1174
+ /** Smoothly scroll so that the given date is centred/positioned. */
1175
+ goToDateAnim(dmy: Dmy, options?: AnimOptions): void;
1176
+ /** Fit a date range into the viewport with optional edge padding. Dates may be passed in either order; year-only and month-only inputs are treated as full periods. */
1177
+ fitDateRange(
1178
+ start: FitDateRangeInput,
1179
+ end: FitDateRangeInput,
1180
+ options?: FitDateRangeOptions,
1181
+ ): void;
1182
+ /** Fit a single article's full period into the viewport using `article.period.from` and `article.period.to`. */
1183
+ fitArticleRange(article: Article, options?: FitArticleRangeOptions): boolean;
1184
+ /** Fit currently unfiltered article cards into the viewport using each article's start date, card width, and connector offset. */
1185
+ fitArticles(options?: FitArticlesOptions): boolean;
1186
+ /** Animate to an absolute zoom value using the built-in tween. */
1187
+ zoomToAnim(newZoom: number, complete?: () => void): void;
1188
+
1189
+ // Drawing
1190
+ /** Request a redraw (coalesced if an animation is running). */
1191
+ requestRedraw(redrawFunction?: () => void): void;
1192
+ /** Full draw cycle. */
1193
+ redraw(): void;
1194
+ /** Reposition-only draw (for zoom/start-date changes). */
1195
+ repositionRedraw(): void;
1196
+ /** Default draw including reposition and stacking. */
1197
+ defaultRedraw(): void;
1198
+ /** Toggle auto fit-to-height for article stacking and return the new enabled state. */
1199
+ toggleAutoFit(): boolean;
1200
+ /** Recompute row spacing so visible rows fit in the available height. */
1201
+ fitToHeight(withRedraw?: boolean): number | undefined;
1202
+ /**
1203
+ * @deprecated Deprecated since 1.3.0 — Use `timeline.on('timeline-render-end', handler)` instead.
1204
+ */
1205
+ onRedraw(handler: (ctx: RedrawContext) => void): void;
1206
+
1207
+ // Interaction toggles
1208
+ enableZoomByWheel(): void;
1209
+ disableZoomByWheel(): void;
1210
+
1211
+ // Misc utilities
1212
+ /** Current canvas width in CSS pixels. */
1213
+ getWidth(): number;
1214
+ /** Current applied device pixel ratio for the canvas. */
1215
+ getCanvasDpr(): number;
1216
+ /** Get canvas-relative coordinates for a DOM event. */
1217
+ getRelativePosition(evt: MouseEvent | PointerEvent | TouchEvent): { left: number; top: number };
1218
+
1219
+ // Options
1220
+ /** Get or set options via dot-path or partial object. */
1221
+ setOption(path: string, value: unknown): void;
1222
+ setOption<T extends DeepPartial<TimelineOptions>>(patch: T): void;
1223
+ setOption(path: string): unknown;
1224
+ /** Resize the canvas. */
1225
+ setSize(width: number, height: number): void;
1226
+ /** Serialize the current timeline state to the legacy compact JSON string. */
1227
+ getStatusForSave(): string;
1228
+ /** Register a reusable card layout from an instance. Delegates to the static method. */
1229
+ registerCardLayout(definition: CardLayoutDefinition): void;
1230
+ /** Resolve a previously registered card layout by name. */
1231
+ getCardLayout(name: CardLayoutName): RegisteredCardLayout | null;
1232
+
1233
+ // Event bus
1234
+ on<T extends TimelineEvent>(event: T, handler: (...args: EventPayloadMap[T]) => void): void;
1235
+ on(event: string, handler: (...args: any[]) => void): void;
1236
+ off<T extends TimelineEvent>(event: T, handler: (...args: EventPayloadMap[T]) => void): void;
1237
+ off(event: string, handler: (...args: any[]) => void): void;
1238
+ /**
1239
+ * Trigger a built-in or custom event.
1240
+ * Custom event payloads are intentionally untyped because the runtime does not enforce a schema.
1241
+ */
1242
+ trigger<T extends TimelineEvent>(event: T, ...args: EventPayloadMap[T]): void;
1243
+ trigger(event: string, ...args: any[]): void;
1244
+ }
1245
+
1246
+ export class Article {
1247
+ /** Stable article identifier. */
1248
+ id: string | number;
1249
+ /** Owning timeline instance. */
1250
+ owner: Timeline;
1251
+ /** Latest runtime data backing this article. */
1252
+ data: ArticleData;
1253
+ title: string;
1254
+ subtitle: string;
1255
+ rank: number;
1256
+ laneId: string | number | null;
1257
+ cardLayout: CardLayoutName | null;
1258
+ period: ArticlePeriod;
1259
+ position: CanvasPosition;
1260
+ registeredPosition: CanvasPosition;
1261
+ offset: CanvasPosition;
1262
+ finalOffset: CanvasPosition;
1263
+ indicator: ArticleIndicator;
1264
+ row: number;
1265
+ image: CanvasImageSource | null;
1266
+ imageLoaded: boolean;
1267
+ style: ArticleStyle;
1268
+ hoverStyle: ArticleStyle;
1269
+ activeStyle: ArticleStyle;
1270
+ activeHoverStyle: ArticleStyle;
1271
+ opacity: number;
1272
+ isActive: boolean;
1273
+ isVisible: boolean;
1274
+ isDragging: boolean;
1275
+ isStarred: boolean;
1276
+ isMouseover: boolean;
1277
+ hiddenByFilter: boolean;
1278
+ isDataLoaded: boolean;
1279
+
1280
+ /** Update article data at runtime (dot-path or object). */
1281
+ setOption(path: string, value: unknown): void;
1282
+ setOption(patch: Partial<ArticleData>): void;
1283
+ setOption(path: string): unknown;
1284
+
1285
+ /** Update normal style (dot-path or object). */
1286
+ setStyle(path: string, value: unknown): void;
1287
+ setStyle(style: Partial<ArticleStyle>): void;
1288
+ setStyle(path: string): unknown;
1289
+
1290
+ /** Update hover style (dot-path or object). */
1291
+ setHoverStyle(path: string, value: unknown): void;
1292
+ setHoverStyle(style: Partial<ArticleStyle>): void;
1293
+ setHoverStyle(path: string): unknown;
1294
+
1295
+ /** Update active style (dot-path or object). */
1296
+ setActiveStyle(path: string, value: unknown): void;
1297
+ setActiveStyle(style: Partial<ArticleStyle>): void;
1298
+ setActiveStyle(path: string): unknown;
1299
+
1300
+ /** Move instantly to absolute canvas position. */
1301
+ moveTo(position: { left: number; top: number }): void;
1302
+ /** Move by delta from current position. */
1303
+ moveToOffset(offset: { left: number; top: number }): void;
1304
+ /** Override the card layout used by this article. */
1305
+ setCardLayout(cardLayout: CardLayoutName): void;
1306
+ /** Small per-layout memoization helper. */
1307
+ memo<T>(key: string, factory: () => T): T;
1308
+ /** Clear cached measurements/layout artifacts for this article. */
1309
+ invalidateCaches(): void;
1310
+ /** Resolve the clickable star hit area, or `false` when none is present. */
1311
+ getIconBox(): CardLayoutIconBox | false;
1312
+
1313
+ /** Measured card width. */
1314
+ getWidth(): number;
1315
+ /** Measured card height. */
1316
+ getHeight(): number;
1317
+ }
1318
+
1319
+ export class TimeBand {
1320
+ id: string | number;
1321
+ owner: Timeline;
1322
+ data: TimeBandData;
1323
+ title: string;
1324
+ style: TimeBandStyle;
1325
+ period: TimeBandPeriod;
1326
+ visibility: VisibilityState;
1327
+
1328
+ /** Update band data at runtime (dot-path or object). */
1329
+ setOption(path: string, value: unknown): void;
1330
+ setOption(patch: Partial<TimeBandData>): void;
1331
+ setOption(path: string): unknown;
1332
+
1333
+ /** Update band style (dot-path or object). */
1334
+ setStyle(path: string, value: unknown): void;
1335
+ setStyle(style: Partial<TimeBandStyle>): void;
1336
+ setStyle(path: string): unknown;
1337
+ }
1338
+
1339
+ export class Lane {
1340
+ id: string | number;
1341
+ owner: Timeline;
1342
+ data: LaneData;
1343
+ title: string;
1344
+ layout: LaneLayout;
1345
+ style: LaneStyle;
1346
+ order: number;
1347
+ isImplicit: boolean;
1348
+ visibility: VisibilityState;
1349
+
1350
+ /** Update lane data at runtime (dot-path or object). */
1351
+ setOption(path: string, value: unknown): void;
1352
+ setOption(patch: Partial<LaneData>): void;
1353
+ setOption(path: string): unknown;
1354
+
1355
+ /** Hide the lane by setting visibility to "hidden". */
1356
+ hide(): void;
1357
+
1358
+ /** Show the lane by setting visibility to "visible". */
1359
+ show(): void;
1360
+
1361
+ /** Move this lane to a public lane index where 0 is nearest the axis. */
1362
+ moveToIndex(index: number): Lane;
1363
+
1364
+ /** Move this lane so it ends up immediately before targetLane. */
1365
+ moveBefore(targetLane: Lane | string | number): Lane;
1366
+
1367
+ /** Move this lane so it ends up immediately after targetLane. */
1368
+ moveAfter(targetLane: Lane | string | number): Lane;
1369
+
1370
+ /** Move this lane one position farther from the axis. */
1371
+ moveUp(): Lane;
1372
+
1373
+ /** Move this lane one position closer to the axis. */
1374
+ moveDown(): Lane;
1375
+
1376
+ /** Update lane style (dot-path or object). */
1377
+ setStyle(path: string, value: unknown): void;
1378
+ setStyle(style: Partial<LaneStyle>): void;
1379
+ setStyle(path: string): unknown;
1380
+ }
1381
+
1382
+ /** Built-in sorter signature used by stacking and layout helpers. */
1383
+ export type ArticleSorter = (articleA: any, articleB: any) => number;
1384
+
1385
+ export const ARTICLE_DURATION_SORTER: ArticleSorter;
1386
+ export const ARTICLE_DURATION_YEARS_SORTER: ArticleSorter;
1387
+ export const ARTICLE_IMAGE_QUEUE_SORTER: ArticleSorter;
1388
+ export const ARTICLE_FROM_SORTER: ArticleSorter;
1389
+ export const ARTICLE_TO_SORTER: ArticleSorter;
1390
+ export const ARTICLE_TOP_SORTER: ArticleSorter;
1391
+ export const ARTICLE_ROW_SORTER: ArticleSorter;
1392
+ export const ARTICLE_RANK_SORTER: ArticleSorter;
1393
+ export const ARTICLE_EFFECTIVE_RANK_SORTER: ArticleSorter;
1394
+ export const ARTICLE_POSITION_SORTER: ArticleSorter;
1395
+
1396
+ // ------------------------------------------------------------------------------------
1397
+ // Default export for UMD / ESM default
1398
+ // ------------------------------------------------------------------------------------
1399
+
1400
+ export interface HistropediaApi {
1401
+ Timeline: typeof Timeline;
1402
+ Article: typeof Article;
1403
+ Lane: typeof Lane;
1404
+ TimeBand: typeof TimeBand;
1405
+ Dmy: DmyConstructor;
1406
+ Logger: LoggerInstance;
1407
+ setDebug(enabled: boolean): void;
1408
+ isDebugEnabled(): boolean;
1409
+ enableDebug(): void;
1410
+ disableDebug(): void;
1411
+ ARTICLE_DURATION_SORTER: typeof ARTICLE_DURATION_SORTER;
1412
+ ARTICLE_DURATION_YEARS_SORTER: typeof ARTICLE_DURATION_YEARS_SORTER;
1413
+ ARTICLE_IMAGE_QUEUE_SORTER: typeof ARTICLE_IMAGE_QUEUE_SORTER;
1414
+ ARTICLE_FROM_SORTER: typeof ARTICLE_FROM_SORTER;
1415
+ ARTICLE_TO_SORTER: typeof ARTICLE_TO_SORTER;
1416
+ ARTICLE_TOP_SORTER: typeof ARTICLE_TOP_SORTER;
1417
+ ARTICLE_ROW_SORTER: typeof ARTICLE_ROW_SORTER;
1418
+ ARTICLE_RANK_SORTER: typeof ARTICLE_RANK_SORTER;
1419
+ ARTICLE_EFFECTIVE_RANK_SORTER: typeof ARTICLE_EFFECTIVE_RANK_SORTER;
1420
+ ARTICLE_POSITION_SORTER: typeof ARTICLE_POSITION_SORTER;
1421
+ MINOR_MARKER: typeof MINOR_MARKER;
1422
+ MAJOR_MARKER: typeof MAJOR_MARKER;
1423
+ ERA_MARKER: typeof ERA_MARKER;
1424
+ ZOOM_DAY: typeof ZOOM_DAY;
1425
+ ZOOM_MONTH: typeof ZOOM_MONTH;
1426
+ ZOOM_YEAR: typeof ZOOM_YEAR;
1427
+ ZOOM_DECADE: typeof ZOOM_DECADE;
1428
+ ZOOM_CENTURY: typeof ZOOM_CENTURY;
1429
+ ZOOM_MILLENNIUM: typeof ZOOM_MILLENNIUM;
1430
+ ZOOM_10_THOUSAND_YEARS: typeof ZOOM_10_THOUSAND_YEARS;
1431
+ ZOOM_100_THOUSAND_YEARS: typeof ZOOM_100_THOUSAND_YEARS;
1432
+ ZOOM_MILLION_YEARS: typeof ZOOM_MILLION_YEARS;
1433
+ ZOOM_10_MILLION_YEARS: typeof ZOOM_10_MILLION_YEARS;
1434
+ ZOOM_100_MILLION_YEARS: typeof ZOOM_100_MILLION_YEARS;
1435
+ ZOOM_BILLION_YEARS: typeof ZOOM_BILLION_YEARS;
1436
+ YEAR_PREFIXES: typeof YEAR_PREFIXES;
1437
+ DENSITY_SETTINGS: typeof DENSITY_SETTINGS;
1438
+ PRECISION_DAY: typeof PRECISION_DAY;
1439
+ PRECISION_MONTH: typeof PRECISION_MONTH;
1440
+ PRECISION_YEAR: typeof PRECISION_YEAR;
1441
+ PRECISION_DECADE: typeof PRECISION_DECADE;
1442
+ PRECISION_CENTURY: typeof PRECISION_CENTURY;
1443
+ PRECISION_MILLENNIUM: typeof PRECISION_MILLENNIUM;
1444
+ PRECISION_MILLION_YEARS: typeof PRECISION_MILLION_YEARS;
1445
+ PRECISION_BILLION_YEARS: typeof PRECISION_BILLION_YEARS;
1446
+ DENSITY_ALL: typeof DENSITY_ALL;
1447
+ DENSITY_LOW: typeof DENSITY_LOW;
1448
+ DENSITY_MEDIUM: typeof DENSITY_MEDIUM;
1449
+ DENSITY_HIGH: typeof DENSITY_HIGH;
1450
+ RANGE_ALL: typeof RANGE_ALL;
1451
+ RANGE_SCREEN: typeof RANGE_SCREEN;
1452
+ }
1453
+
1454
+ declare const HistropediaModule: HistropediaApi;
1455
+
1456
+ export default HistropediaModule;
1457
+
1458
+ declare global {
1459
+ const Histropedia: HistropediaApi;
1460
+
1461
+ interface Window {
1462
+ Histropedia: HistropediaApi;
1463
+ }
1464
+ }