histropediajs 1.5.0 → 1.6.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 CHANGED
@@ -1,1718 +1,1883 @@
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
- /** Date precision constants (public). */
71
- export const PRECISION_DAY: 'day';
72
- export const PRECISION_MONTH: 'month';
73
- export const PRECISION_YEAR: 'year';
74
- export const PRECISION_DECADE: 'decade';
75
- export const PRECISION_CENTURY: 'century';
76
- export const PRECISION_MILLENNIUM: 'millennium';
77
- export const PRECISION_MILLION_YEARS: 'million-years';
78
- export const PRECISION_BILLION_YEARS: 'billion-years';
79
-
80
- /** @deprecated Use the equivalent string precision instead. */
81
- export type LegacyPrecision = 11 | 10 | 9 | 8 | 7 | 6 | 3 | 1;
82
-
83
- /**
84
- * Date precision used by Article/TimeBand ranges.
85
- * Legacy numeric precision values remain supported for backwards compatibility.
86
- */
87
- export type Precision =
88
- | typeof PRECISION_DAY
89
- | typeof PRECISION_MONTH
90
- | typeof PRECISION_YEAR
91
- | typeof PRECISION_DECADE
92
- | typeof PRECISION_CENTURY
93
- | typeof PRECISION_MILLENNIUM
94
- | typeof PRECISION_MILLION_YEARS
95
- | typeof PRECISION_BILLION_YEARS
96
- | LegacyPrecision;
97
-
98
- /** Plain YMD date parts (no precision). */
99
- export type DateParts = {
100
- year: number;
101
- month?: number;
102
- day?: number;
103
- };
104
-
105
- /** General date input that can include precision. */
106
- export type DateInput = DateParts & { precision?: Precision };
107
-
108
- /** Internal date input shape (consumer-friendly). */
109
- export interface Dmy {
110
- year: number;
111
- month?: number;
112
- day?: number;
113
- precision?: Precision;
114
- }
115
-
116
- /** Formatting options for Dmy.format. */
117
- export interface DmyFormatOptions {
118
- yearPrefix?: { value?: number; label?: string };
119
- bceText?: string;
120
- thousandsSeparator?: string;
121
- }
122
-
123
- /** Histropedia date helper instance. */
124
- export interface DmyInstance extends Dmy {
125
- getDaysTo(other: Dmy): number;
126
- getMonthsTo(other: Dmy): number;
127
- getYearsTo(other: Dmy): number;
128
- getLeapYearsSinceYearZero(): number;
129
- getDaysSinceYearZero(): number;
130
- getDayOfYear(): number;
131
- getNextDay(): DmyInstance;
132
- addDays(amount: number): DmyInstance;
133
- addMonths(amount: number): DmyInstance;
134
- addYears(amount: number): DmyInstance;
135
- getPreviousDay(): DmyInstance;
136
- getNextMonth(): DmyInstance;
137
- getPreviousMonth(): DmyInstance;
138
- format(format?: string, options?: DmyFormatOptions): string;
139
- isSame(other: Dmy): boolean;
140
- isAfter(other: Dmy): boolean;
141
- isBetween(from: Dmy, to: Dmy): boolean;
142
- compare(other: Dmy): number;
143
- toKeyString(): string;
144
- asFloat(): number;
145
- isInFuture(): boolean;
146
- convertToIso(): DmyInstance;
147
- }
148
-
149
- export type FitDateRangeInput = DmyInstance | DateParts;
150
-
151
- export interface DmyConstructor {
152
- new (year: number, month?: number, day?: number): DmyInstance;
153
- new (value: Dmy): DmyInstance;
154
- readonly prototype: DmyInstance;
155
- getByDayOfYear(year: number, dayOfYear: number): DmyInstance;
156
- CreateAsStartOfPeriod(date: Dmy, precision?: Precision): DmyInstance;
157
- CreateAsEndOfPeriod(date: Dmy, precision?: Precision): DmyInstance;
158
- now(): DmyInstance;
159
- fromString(dateString: string): DmyInstance;
160
- }
161
-
162
- export const Dmy: DmyConstructor;
163
-
164
- // ------------------------------------------------------------------------------------
165
- // Public constants (used in options)
166
- // ------------------------------------------------------------------------------------
167
-
168
- export const DENSITY_ALL: 'all';
169
- export const DENSITY_LOW: 'low';
170
- export const DENSITY_MEDIUM: 'medium';
171
- export const DENSITY_HIGH: 'high';
172
- /** @deprecated Use the equivalent string density instead. */
173
- export type LegacyDensity = 0 | 1 | 2 | 3;
174
- export type Density =
175
- | typeof DENSITY_ALL
176
- | typeof DENSITY_LOW
177
- | typeof DENSITY_MEDIUM
178
- | typeof DENSITY_HIGH
179
- | LegacyDensity;
180
-
181
- export const RANGE_ALL: 'all';
182
- export const RANGE_SCREEN: 'screen';
183
- /** @deprecated Use the equivalent string auto-stacking range instead. */
184
- export type LegacyAutoStackRange = 0 | 1;
185
- export type AutoStackRange = typeof RANGE_ALL | typeof RANGE_SCREEN | LegacyAutoStackRange;
186
-
187
- export const MINOR_MARKER: 0;
188
- export const MAJOR_MARKER: 1;
189
- export const ERA_MARKER: 2;
190
- export type MarkerType = typeof MINOR_MARKER | typeof MAJOR_MARKER | typeof ERA_MARKER;
191
-
192
- export const ZOOM_DAY: 0;
193
- export const ZOOM_MONTH: 1;
194
- export const ZOOM_YEAR: 2;
195
- export const ZOOM_DECADE: 3;
196
- export const ZOOM_CENTURY: 4;
197
- export const ZOOM_MILLENNIUM: 5;
198
- export const ZOOM_10_THOUSAND_YEARS: 6;
199
- export const ZOOM_100_THOUSAND_YEARS: 7;
200
- export const ZOOM_MILLION_YEARS: 8;
201
- export const ZOOM_10_MILLION_YEARS: 9;
202
- export const ZOOM_100_MILLION_YEARS: 10;
203
- export const ZOOM_BILLION_YEARS: 11;
204
- export type ZoomLevel =
205
- | typeof ZOOM_DAY
206
- | typeof ZOOM_MONTH
207
- | typeof ZOOM_YEAR
208
- | typeof ZOOM_DECADE
209
- | typeof ZOOM_CENTURY
210
- | typeof ZOOM_MILLENNIUM
211
- | typeof ZOOM_10_THOUSAND_YEARS
212
- | typeof ZOOM_100_THOUSAND_YEARS
213
- | typeof ZOOM_MILLION_YEARS
214
- | typeof ZOOM_10_MILLION_YEARS
215
- | typeof ZOOM_100_MILLION_YEARS
216
- | typeof ZOOM_BILLION_YEARS;
217
-
218
- export interface YearPrefixSettings {
219
- label: string;
220
- value: number;
221
- }
222
-
223
- export const YEAR_PREFIXES: Readonly<Record<number, YearPrefixSettings>>;
224
-
225
- export interface DensitySetting {
226
- region: number;
227
- zoom: {
228
- from: number;
229
- to: number;
230
- };
231
- low: number;
232
- medium: number;
233
- high: number;
234
- all: number;
235
- step: {
236
- months: number;
237
- days: number;
238
- };
239
- }
240
-
241
- export const DENSITY_SETTINGS: readonly DensitySetting[];
242
-
243
- export interface LoggerInstance {
244
- setEnabled(enabled: boolean): void;
245
- isEnabled(): boolean;
246
- setPrefix(prefix: string): void;
247
- getPrefix(): string;
248
- log(...args: unknown[]): void;
249
- warn(...args: unknown[]): void;
250
- error(...args: unknown[]): void;
251
- info(...args: unknown[]): void;
252
- debug(...args: unknown[]): void;
253
- group(...args: unknown[]): void;
254
- groupCollapsed(...args: unknown[]): void;
255
- groupEnd(): void;
256
- }
257
-
258
- export const Logger: LoggerInstance;
259
-
260
- // ------------------------------------------------------------------------------------
261
- // Animation types
262
- // ------------------------------------------------------------------------------------
263
-
264
- /** Allowed easing names used by animations. */
265
- export type EasingName =
266
- | 'linear'
267
- | 'swing'
268
- | 'easeInQuad'
269
- | 'easeOutQuad'
270
- | 'easeInOutQuad'
271
- | 'easeInCubic'
272
- | 'easeOutCubic'
273
- | 'easeInOutCubic';
274
-
275
- // ------------------------------------------------------------------------------------
276
- // Style types
277
- // ------------------------------------------------------------------------------------
278
-
279
- export interface ArticleStyleText {
280
- font?: string;
281
- color?: string;
282
- align?: 'left' | 'right' | 'center' | 'start' | 'end';
283
- baseline?: 'top' | 'middle' | 'alphabetic' | 'bottom';
284
- margin?: number;
285
- lineHeight?: number;
286
- numberOfLines?: number;
287
- offsetY?: number;
288
- }
289
-
290
- export interface ArticleStyle {
291
- /** @defaultValue 150 */
292
- width?: number;
293
- /** Landscape card layout only. @defaultValue 70 */
294
- height?: number;
295
- /** Card background fill color. @defaultValue '#e9e9e9' */
296
- color?: string;
297
- /** Inner card background. @defaultValue '#fff' */
298
- backgroundColor?: string;
299
- /** Portrait card layout only. @defaultValue 3 */
300
- topRadius?: number;
301
- /** Landscape card layout only. @defaultValue 4 */
302
- borderRadius?: number;
303
- /**
304
- * Shorthand for the article image maximum height.
305
- *
306
- * @deprecated Deprecated since 1.3.0 — Use `image.maxHeight` instead.
307
- * @defaultValue 400
308
- */
309
- maxImageHeight?: number;
310
- image?: {
311
- /** @defaultValue 'natural' */
312
- shape?: 'natural' | 'square' | 'circle';
313
- /** @defaultValue 0 */
314
- margin?: number;
315
- /** @defaultValue 400 */
316
- maxHeight?: number;
317
- /** @defaultValue 0 */
318
- borderRadius?: number;
319
- };
320
- header?: {
321
- /** Portrait card layout only. @defaultValue 50 */
322
- height?: number;
323
- text?: ArticleStyleText;
324
- };
325
- subheader?: {
326
- /** Portrait card layout only. @defaultValue 30 */
327
- height?: number;
328
- /** Portrait card layout only. @defaultValue '#555' */
329
- color?: string;
330
- text?: ArticleStyleText;
331
- };
332
- shadow?: {
333
- /** @defaultValue 0 */ x?: number;
334
- /** @defaultValue 0 */ y?: number;
335
- /** @defaultValue 0 */ amount?: number;
336
- /** @defaultValue '#000' */ color?: string;
337
- };
338
- border?: {
339
- /** @defaultValue '#ddd' */ color?: string;
340
- /** @defaultValue 1 */ width?: number;
341
- };
342
- connectorLine?: {
343
- /** @defaultValue true */ visible?: boolean;
344
- /** @defaultValue 18 */ offsetX?: number;
345
- /** @defaultValue -20 */ offsetY?: number;
346
- /** @defaultValue 1 */ thickness?: number;
347
- arrow?: {
348
- /** @defaultValue 16 */ width?: number;
349
- /** @defaultValue 45 */ height?: number;
350
- };
351
- };
352
- star?: {
353
- /** @defaultValue 16 */ width?: number;
354
- /** @defaultValue 3 */ margin?: number;
355
- };
356
- }
357
-
358
- export interface TimeBandStyleText {
359
- font?: string;
360
- color?: string;
361
- align?: 'left' | 'right' | 'center' | 'start' | 'end';
362
- baseline?: 'top' | 'middle' | 'alphabetic' | 'bottom';
363
- /** Vertical align inside band area. */
364
- verticalAlign?: 'top' | 'middle' | 'bottom';
365
- /** Horizontal/vertical padding in px. @defaultValue 4 */
366
- margin?: number;
367
- /** Extra vertical offset in px. @defaultValue 0 */
368
- offsetY?: number;
369
- }
370
-
371
- export interface TimeBandStyle {
372
- /** @defaultValue 'rgba(200, 200, 200, 0.25)' */
373
- backgroundColor?: string;
374
- border?: { color?: string; width?: number };
375
- text?: TimeBandStyleText;
376
- }
377
-
378
- export interface TimelineStyleMarkerKind {
379
- /** @defaultValue 12 (minor) / 30 (major) */ height?: number;
380
- /** Extend marker to canvas top/bottom. @defaultValue false */ extend?: boolean;
381
- color?: string;
382
- futureColor?: string;
383
- }
384
-
385
- export interface TimelineStyleOptions {
386
- mainLine?: {
387
- /** @defaultValue true */ visible?: boolean;
388
- /** @defaultValue 'bottom' */ position?: 'top' | 'bottom';
389
- /** thickness in px. @defaultValue 8 */ size?: number;
390
- /** Draw small article position ticks. @defaultValue true */
391
- showDateIndicators?: boolean;
392
- };
393
- draggingHighlight?: {
394
- /** @defaultValue true */ visible?: boolean;
395
- /** @defaultValue 'rgba(237, 247, 255, 0.5)' */ color?: string;
396
- /** Up/down extents relative to the main line. Accepts px, `'edge'`, or `{ value: 'edge', padding?: number }`.
397
- * @defaultValue {up: 0, down: 'edge'}
398
- */
399
- area?: { up?: AreaValue; down?: AreaValue };
400
- };
401
- marker?: {
402
- minor?: TimelineStyleMarkerKind;
403
- major?: TimelineStyleMarkerKind;
404
- };
405
- dateLabel?: {
406
- minor?: {
407
- font?: string;
408
- color?: string;
409
- futureColor?: string;
410
- textAlign?: CanvasTextAlign;
411
- offset?: { x?: number; y?: number };
412
- /** Text appended for BCE labeling. */
413
- bceText?: string;
414
- /** Thousands separator for large year labels. */
415
- thousandsSeparator?: string;
416
- /** Map of year-scale prefixes to display (e.g. ka, Ma, Ga). */
417
- yearPrefixes?: Record<string, { label: string; value: number; minDivision: number }>;
418
- };
419
- major?: {
420
- font?: string;
421
- color?: string;
422
- futureColor?: string;
423
- textAlign?: CanvasTextAlign;
424
- offset?: { x?: number; y?: number };
425
- bceText?: string;
426
- thousandsSeparator?: string;
427
- yearPrefixes?: Record<string, { label: string; value: number; minDivision: number }>;
428
- };
429
- };
430
- cursor?: {
431
- clickable?: string;
432
- timeline?: { hover?: string; dragging?: string };
433
- article?: { hoverDraggable?: string; dragging?: string };
434
- };
435
- }
436
-
437
- // ------------------------------------------------------------------------------------
438
- // Data schemas for loading
439
- // ------------------------------------------------------------------------------------
440
-
441
- export interface ArticleData {
442
- id: string | number;
443
- title: string;
444
- subtitle?: string;
445
- lane?: string | number;
446
- from: DateInput;
447
- to?: DateInput;
448
- /** If true, the period ends at the present day. */
449
- isToPresent?: boolean;
450
- imageUrl?: string;
451
- rank?: number;
452
- starred?: boolean;
453
- hiddenByFilter?: boolean | ((article: Article) => boolean);
454
- hidePeriodLine?: boolean | ((article: Article) => boolean);
455
- /** Initial offset from computed position (px). */
456
- offsetLeft?: number;
457
- /** Initial offset from computed position (px). */
458
- offsetTop?: number;
459
- /** Override default card layout for this article. */
460
- cardLayout?: CardLayoutName;
461
- style?: ArticleStyle;
462
- hoverStyle?: ArticleStyle;
463
- activeStyle?: ArticleStyle;
464
- }
465
-
466
- export interface ArticleCardLayoutBreakpoint {
467
- maxHeight: number;
468
- layout: CardLayoutName;
469
- }
470
-
471
- export interface TimeBandData {
472
- id: string | number;
473
- title: string;
474
- from: DateInput;
475
- to?: DateInput;
476
- /** If true, the band ends at the present day. */
477
- isToPresent?: boolean;
478
- /** Visibility of this band: 'visible' | 'hidden' or a function returning one. Defaults to 'visible'. */
479
- visibility?: VisibilityState | ((band: TimeBand) => VisibilityState);
480
- style?: TimeBandStyle;
481
- }
482
-
483
- export type ChartType = 'line' | 'area';
484
- export type ChartScaleSource = 'data' | 'viewport';
485
- export type ChartScaleTickMode = 'round' | 'exact';
486
- export type ChartAreaStackingInterpolation = 'linear' | 'step' | 'none';
487
-
488
- export interface ChartPointData {
489
- date: DateInput;
490
- value: number | null;
491
- }
492
-
493
- export interface ChartSeriesStyle {
494
- color?: string;
495
- lineWidth?: number;
496
- lineDash?: number[];
497
- area?: {
498
- fillColor?: string | null;
499
- opacity?: number;
500
- };
501
- point?: {
502
- visible?: boolean;
503
- radius?: number;
504
- fillColor?: string | null;
505
- strokeColor?: string | null;
506
- lineWidth?: number;
507
- };
508
- }
509
-
510
- export interface ChartGridLineStyle {
511
- visible?: boolean;
512
- color?: string;
513
- /** Stroke thickness in canvas pixels. */
514
- thickness?: number;
515
- }
516
-
517
- export interface ChartMinorGridLineStyle extends ChartGridLineStyle {
518
- /** Hide minor grid lines while the timeline minor unit size is below this many pixels. @defaultValue 40 */
519
- minimumSpacing?: number;
520
- }
521
-
522
- export interface ChartGridLineOptions {
523
- x?: {
524
- major?: ChartGridLineStyle;
525
- minor?: ChartMinorGridLineStyle;
526
- };
527
- y?: {
528
- major?: ChartGridLineStyle;
529
- };
530
- }
531
-
532
- export interface ChartStyle {
533
- padding?: {
534
- top?: number;
535
- right?: number;
536
- bottom?: number;
537
- left?: number;
538
- };
539
- backgroundColor?: string;
540
- gridLine?: ChartGridLineOptions;
541
- axis?: {
542
- visible?: boolean;
543
- color?: string;
544
- lineWidth?: number;
545
- };
546
- yAxis?: {
547
- visible?: boolean;
548
- font?: string;
549
- color?: string;
550
- margin?: number;
551
- /** Thousands separator for numeric y-axis labels. Set to null or an empty string to disable. @defaultValue ',' */
552
- thousandsSeparator?: string | null;
553
- formatter?: ((value: number) => string) | null;
554
- };
555
- legend?: {
556
- visible?: boolean | 'auto';
557
- position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
558
- font?: string;
559
- color?: string;
560
- markerSize?: number;
561
- markerGap?: number;
562
- itemGap?: number;
563
- inset?: { x?: number; y?: number };
564
- };
565
- series?: ChartSeriesStyle;
566
- }
567
-
568
- export interface ChartScaleOptions {
569
- /** Use all loaded values or only values inside the current viewport. @defaultValue 'data' */
570
- source?: ChartScaleSource;
571
- /** Include 0 in the computed domain before padding/fixed bounds. @defaultValue false */
572
- includeZero?: boolean;
573
- /** Fractional padding applied to auto domains. @defaultValue 0.05 */
574
- padding?: number;
575
- /** Fixed minimum y value. */
576
- min?: number;
577
- /** Fixed maximum y value. */
578
- max?: number;
579
- /** Controls how y-axis tick values are generated. @defaultValue 'round' */
580
- tickMode?: ChartScaleTickMode;
581
- /** Preferred number of y-axis ticks. Rounded ticks may vary slightly. @defaultValue 5 */
582
- tickTarget?: number;
583
- }
584
-
585
- export interface ChartTypeOptions {
586
- area?: {
587
- stacking?: {
588
- /** Enable stacked area rendering. When false, area series fill independently from zero. @defaultValue true */
589
- active?: boolean;
590
- /** How missing series values are resolved across the shared area x-domain. @defaultValue 'linear' */
591
- interpolation?: ChartAreaStackingInterpolation;
592
- };
593
- };
594
- }
595
-
596
- export interface ChartSeriesData {
597
- id?: string | number;
598
- title?: string;
599
- points?: ChartPointData[];
600
- style?: ChartSeriesStyle;
601
- }
602
-
603
- export interface ChartData {
604
- id: string | number;
605
- type?: ChartType;
606
- title?: string;
607
- lane?: string | number;
608
- visibility?: VisibilityState | ((chart: Chart) => VisibilityState);
609
- series?: ChartSeriesData[];
610
- scale?: ChartScaleOptions;
611
- typeOptions?: ChartTypeOptions;
612
- area?: { up?: AreaValue; down?: AreaValue };
613
- style?: ChartStyle;
614
- }
615
-
616
- export interface LaneLayout {
617
- height?: number;
618
- /**
619
- * Relative share of the remaining automatic lane height for lanes without a fixed `height`.
620
- * Hidden lanes are excluded from the distribution.
621
- *
622
- * @defaultValue 1
623
- */
624
- heightWeight?: number;
625
- header?: {
626
- visible?: boolean;
627
- height?: number;
628
- padding?: { left?: number; right?: number };
629
- };
630
- }
631
-
632
- /** Decorative lane chrome styles. Only explicitly provided values are written inline. */
633
- export interface LaneStyle {
634
- header?: {
635
- backgroundColor?: string;
636
- };
637
- body?: {
638
- backgroundColor?: string;
639
- borderColor?: string;
640
- borderWidth?: number;
641
- borderRadius?: number;
642
- };
643
- title?: {
644
- color?: string;
645
- font?: string;
646
- textAlign?: 'left' | 'center' | 'right';
647
- };
648
- }
649
-
650
- export interface DistanceToBaselineResponsiveCardLayoutOptions {
651
- /** Fraction of the available content height used to calculate the baseline distance. */
652
- ratio?: number;
653
- /** Minimum responsive baseline distance in px. */
654
- min?: number;
655
- /** Maximum responsive baseline distance in px. */
656
- max?: number;
657
- }
658
-
659
- export interface DistanceToBaselineResponsiveOptions {
660
- /** Enable responsive baseline distance calculations. @defaultValue true */
661
- active?: boolean;
662
- /**
663
- * Restrict responsive baseline calculations to lanes only.
664
- * When true, timelines without active lanes use the static `distanceToBaseline.value`.
665
- *
666
- * @defaultValue true
667
- */
668
- lanesOnly?: boolean;
669
- /**
670
- * Responsive baseline settings keyed by the resolved default card layout name.
671
- *
672
- * @defaultValue `{ portrait: { ratio: 0.75, min: 140, max: 350 }, landscape: { ratio: 0.45, min: 50, max: 160 } }`
673
- */
674
- byCardLayout?: Partial<Record<CardLayoutName, DistanceToBaselineResponsiveCardLayoutOptions>>;
675
- }
676
-
677
- export interface DistanceToBaselineOptions {
678
- /**
679
- * Distance of cards from their baseline in px:
680
- * the shared timeline axis without lanes, or the lane bottom with lanes.
681
- *
682
- * @defaultValue 350
683
- */
684
- value?: number;
685
- /** Responsive baseline distance settings. */
686
- responsive?: DistanceToBaselineResponsiveOptions;
687
- }
688
-
689
- export interface LaneArticleOverrides {
690
- defaultData?: Partial<ArticleData>;
691
- /** Lane-local density grouping/filtering input. */
692
- density?: ArticleOptions['density'];
693
- defaultCardLayout?: CardLayoutName;
694
- defaultStyle?: ArticleStyle;
695
- defaultHoverStyle?: ArticleStyle;
696
- defaultActiveStyle?: ArticleStyle;
697
- layoutStyles?: ArticleOptions['layoutStyles'];
698
- /** Distance of cards from this lane's baseline in px, plus responsive settings for this lane. */
699
- distanceToBaseline?: DistanceToBaselineOptions;
700
- /**
701
- * @deprecated Deprecated since 1.3.4 — Use `distanceToBaseline.value` instead.
702
- */
703
- distanceToMainLine?: number;
704
- autoStacking?: {
705
- /** Enable or disable automatic row assignment for this lane. */
706
- active?: boolean;
707
- rowSpacing?: number;
708
- fitToHeight?: boolean;
709
- topGap?: number | ((timeline: Timeline) => number);
710
- };
711
- periodLine?: {
712
- /** Additional gap from the active baseline in px. */
713
- baselineGap?: number;
714
- /** Gap between stacked period lines in px. */
715
- spacing?: number;
716
- /** Line thickness in px. */
717
- thickness?: number;
718
- };
719
- }
720
-
721
- export interface LaneData {
722
- id: string | number;
723
- title?: string;
724
- visibility?: VisibilityState | ((lane: Lane) => VisibilityState);
725
- /** Additional CSS class for the lane root beside `histropedia-lane` and `histropedia-lane-index-N`. */
726
- className?: string;
727
- /** Additional CSS class for the lane header beside `histropedia-lane-header`. */
728
- headerClassName?: string;
729
- /** Additional CSS class for the lane body beside `histropedia-lane-body`. */
730
- bodyClassName?: string;
731
- /** Additional CSS class for the lane title beside `histropedia-lane-title`. */
732
- titleClassName?: string;
733
- layout?: LaneLayout;
734
- style?: LaneStyle;
735
- article?: LaneArticleOverrides;
736
- }
737
-
738
- // ------------------------------------------------------------------------------------
739
- // Timeline options
740
- // ------------------------------------------------------------------------------------
741
-
742
- export interface TimelineZoomOptions {
743
- /** @defaultValue 34 */ initial?: number;
744
- /** @defaultValue 0 */ minimum?: number;
745
- /** @defaultValue 123 */ maximum?: number;
746
- /** Zoom step multiplier per wheel unit. @defaultValue 0.1 */
747
- wheelStep?: number;
748
- /** Overall wheel speed. @defaultValue 3 */
749
- wheelSpeed?: number;
750
- /** Allow Ctrl+wheel zoom. @defaultValue false */
751
- allowCtrlWheel?: boolean;
752
- /** 'auto' picks smooth vs discrete from device; 'discrete' forces stepped. @defaultValue 'auto' */
753
- wheelMode?: 'auto' | 'discrete';
754
- /** Animate discrete/notched wheel zoom steps. */
755
- discreteWheelAnimation?: {
756
- /** Enable animated wheel zoom. @defaultValue true */
757
- active?: boolean;
758
- /** Animation duration in ms. @defaultValue 250 */
759
- duration?: number;
760
- };
761
- /** Gain for proportional (trackpad) mode. @defaultValue 2 */
762
- proportionalGain?: number;
763
- /** Exponent for proportional (trackpad) mode. @defaultValue 1.1 */
764
- proportionalExponent?: number;
765
- /** Zoom ratio between unit-size levels. @defaultValue 0.8 */
766
- ratio?: number;
767
- unitSize?: {
768
- /** Size of a day at zoom=0 (and density region size). @defaultValue 200 */
769
- initial?: number;
770
- /** Unit-size threshold to show minor labels. @defaultValue 48 */
771
- showMinorLabels?: number;
772
- /** Minimum unit size in px. @defaultValue 8 */
773
- minimum?: number;
774
- };
775
- }
776
-
777
- export interface PanMomentumOptions {
778
- /** Continue timeline panning after a drag release. @defaultValue true */
779
- active?: boolean;
780
- /** Pointer movement sample window in ms used to estimate release velocity. @defaultValue 100 */
781
- sampleWindow?: number;
782
- /** Minimum release velocity in px/ms before momentum starts. @defaultValue 0.01 */
783
- minVelocity?: number;
784
- /** Velocity in px/ms at which momentum stops. @defaultValue 0.005 */
785
- stopVelocity?: number;
786
- /** Exponential friction coefficient per ms. Lower values travel farther. @defaultValue 0.004 */
787
- friction?: number;
788
- /** Maximum momentum duration in ms. @defaultValue 1200 */
789
- maxDuration?: number;
790
- }
791
-
792
- export interface TimelinePanOptions {
793
- /** Momentum behaviour after a timeline drag release. */
794
- momentum?: PanMomentumOptions;
795
- }
796
-
797
- export interface TimelineDebugOverlayOptions {
798
- /** Draw vertical guides at article-density region boundaries. @defaultValue false */
799
- densityRegions?: boolean;
800
- }
801
-
802
- export interface ArticleOptions {
803
- /** Density filter for visible articles. @defaultValue DENSITY_ALL */
804
- density?: Density;
805
- /** Allow dragging event cards. @defaultValue true */
806
- draggable?: boolean;
807
- /**
808
- * Distance of cards from their baseline in px, plus responsive settings.
809
- *
810
- * Responsive calculations use the resolved default card layout for the timeline or lane.
811
- */
812
- distanceToBaseline?: DistanceToBaselineOptions;
813
- /**
814
- * @deprecated Deprecated since 1.3.4 — Use `distanceToBaseline.value` instead.
815
- */
816
- distanceToMainLine?: number;
817
- /** Collect ongoing events on main line. @defaultValue false */
818
- collectOngoing?: boolean;
819
- /** Auto stacking options. */
820
- autoStacking?: {
821
- /** @defaultValue true */
822
- active?: boolean;
823
- /** Distance between rows in px. @defaultValue 50 */
824
- rowSpacing?: number;
825
- /** Range considered for stacking. @defaultValue RANGE_ALL */
826
- range?: AutoStackRange;
827
- /** Fit to available height by adjusting rowSpacing. @defaultValue true */
828
- fitToHeight?: boolean;
829
- /** Top gap in px or a function returning px. @defaultValue 10 */
830
- topGap?: number | ((timeline: Timeline) => number);
831
- };
832
- periodLine?: {
833
- /** Anchor period lines to the shared axis or each lane bottom. @defaultValue 'lane' */
834
- position?: 'axis' | 'lane';
835
- /**
836
- * Default value merged into `article.defaultData.hidePeriodLine`.
837
- *
838
- * @deprecated Deprecated since 1.3.0 — Use `defaultData.hidePeriodLine` instead.
839
- */
840
- defaultHide?: boolean;
841
- /** Additional gap from the active baseline in px. @defaultValue 0 */
842
- baselineGap?: number;
843
- /** Gap between stacked period lines in px. @defaultValue 4 */
844
- spacing?: number;
845
- /** Line thickness in px. @defaultValue 10 */
846
- thickness?: number;
847
- stacking?: {
848
- /** Comparator used when assigning period-line stacks. */
849
- sorter?: ArticleSorter;
850
- /** Reverse stacking order. @defaultValue false */
851
- reverseOrder?: boolean;
852
- };
853
- };
854
- animation?: {
855
- fade?: { active?: boolean; duration?: number; easing?: EasingName };
856
- move?: { active?: boolean; duration?: number; easing?: EasingName };
857
- };
858
- star?: { /** Show star icon. @defaultValue true */ visible?: boolean };
859
- /** Default data merged into every loaded article. */
860
- defaultData?: Partial<ArticleData>;
861
- /** Fallback card layout name used when no breakpoint matches. @defaultValue 'portrait' */
862
- defaultCardLayout?: CardLayoutName;
863
- /** Ordered max-height overrides evaluated before falling back to `defaultCardLayout`. */
864
- cardLayoutBreakpoints?: ArticleCardLayoutBreakpoint[];
865
- /** Base styles. */
866
- defaultStyle?: ArticleStyle;
867
- defaultHoverStyle?: ArticleStyle;
868
- defaultActiveStyle?: ArticleStyle;
869
- /** Per-layout style overrides registered via Timeline.registerCardLayout. */
870
- layoutStyles?: Record<
871
- string,
872
- Partial<{
873
- style: ArticleStyle;
874
- hoverStyle: ArticleStyle;
875
- activeStyle: ArticleStyle;
876
- }>
877
- >;
878
- }
879
-
880
- /** Pixels from the main line, the `'edge'` keyword, or `{ value: 'edge', padding?: number }` for edge-stretching with padding. */
881
- export type AreaValue = number | 'edge' | { value: 'edge'; padding?: number };
882
-
883
- export interface TimeBandOptions {
884
- /** Enable drawing of bands. @defaultValue true */
885
- visible?: boolean;
886
- /** Reserve extra space near main line when bands are present. @defaultValue true */
887
- reserveSpace?: boolean;
888
- /** Pixels to reserve when reserveSpace is true. @defaultValue 20 */
889
- reserveSpacePixels?: number;
890
- /** 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'} */
891
- area?: { up?: AreaValue; down?: AreaValue };
892
- /** Global default style merged into each band. */
893
- defaultStyle?: TimeBandStyle;
894
- /** Bands to load initially. */
895
- data?: TimeBandData[];
896
- }
897
-
898
- export interface ChartOptions {
899
- /** Enable drawing of loaded charts. @defaultValue true */
900
- visible?: boolean;
901
- /** Main-view chart area relative to the shared axis. Lane-scoped charts use lane body geometry. */
902
- area?: { up?: AreaValue; down?: AreaValue };
903
- /** Global chart grid-line defaults merged into each chart style. */
904
- gridLine?: ChartGridLineOptions;
905
- /** Default data merged into every loaded chart. */
906
- defaultData?: Partial<ChartData>;
907
- /** Global default chart style merged into each chart. */
908
- defaultStyle?: ChartStyle;
909
- /** Charts to load initially. */
910
- data?: ChartData[];
911
- }
912
-
913
- export interface LaneOptions {
914
- /**
915
- * Enable lane DOM chrome. Article layout still remains lane-aware when lane data is present.
916
- * @defaultValue true
917
- */
918
- visible?: boolean;
919
- /** Default lane id used when an article does not specify `lane`. @defaultValue 'default' */
920
- defaultId?: string | number;
921
- /** Gap between rendered lanes in px. @defaultValue 12 */
922
- gap?: number;
923
- /** Space reserved above the topmost lane in px. @defaultValue 0 */
924
- topGap?: number;
925
- /** Space between the painted edge of the shared main line and the lane area in px. @defaultValue 24 */
926
- axisGap?: number;
927
- /** Global default lane layout merged into each lane. */
928
- defaultLayout?: LaneLayout;
929
- /** Global default lane style merged into each lane. Only explicitly provided values are written inline. */
930
- defaultStyle?: LaneStyle;
931
- /** CSS class added to each lane root element beside `histropedia-lane` and `histropedia-lane-index-N`. */
932
- defaultClassName?: string;
933
- /** CSS class added to each lane header element beside `histropedia-lane-header`. */
934
- defaultHeaderClassName?: string;
935
- /** CSS class added to each lane body element beside `histropedia-lane-body`. */
936
- defaultBodyClassName?: string;
937
- /** CSS class added to each lane title element beside `histropedia-lane-title`. */
938
- defaultTitleClassName?: string;
939
- /** Lanes to load initially. */
940
- data?: LaneData[];
941
- }
942
-
943
- /**
944
- * Image loading and URL sanitization options.
945
- *
946
- * By default, the built-in sanitizer allows http, https, data:image/* and blob:.
947
- * You can restrict schemes and/or remote origins, or provide a custom sanitizer.
948
- */
949
- export type ImageDecodeMode = 'auto' | 'prefer-bitmap' | 'bitmap' | 'prefer-img' | 'img';
950
-
951
- export interface ImageSanitizerOptions {
952
- /**
953
- * Allowed URL schemes for images.
954
- * @defaultValue ['http', 'https', 'data', 'blob']
955
- */
956
- allowedSchemes?: string[];
957
-
958
- /**
959
- * Allowed origins/hosts for remote images. Supports full origins, bare hostnames,
960
- * leading-dot, and wildcard-prefixed (`*.example.com`) entries.
961
- */
962
- allowedOrigins?: string[];
963
- }
964
-
965
- export interface ImageOptions {
966
- /**
967
- * Built-in URL sanitization controls.
968
- */
969
- sanitizer?: ImageSanitizerOptions;
970
-
971
- /**
972
- * Optional override for image URL sanitization. Receives the raw URL and the
973
- * current sanitizer configuration from `image.sanitizer`. Return a trusted URL
974
- * string to allow the load or null to block it.
975
- */
976
- customSanitizer?: (url: string, options: ImageSanitizerOptions) => string | null;
977
-
978
- /**
979
- * Maximum number of concurrent image fetch/decode operations. Values < 1 are clamped to 1.
980
- * @defaultValue 6
981
- */
982
- maxConcurrent?: number;
983
-
984
- /**
985
- * Maximum decoded raster memory (in bytes) retained by the image cache.
986
- * Set to `Infinity` to disable eviction, or reduce to bound memory usage.
987
- * @defaultValue 64 * 1024 * 1024 (64 MiB)
988
- */
989
- maxCacheBytes?: number;
990
-
991
- /**
992
- * Force cross-origin requests to opt-in via CORS (sets `crossOrigin = 'anonymous'`).
993
- * When true, images served without CORS headers will fail to load but canvas readbacks
994
- * remain untainted.
995
- * @defaultValue false
996
- */
997
- requireCORS?: boolean;
998
-
999
- /**
1000
- * Controls how raster data is produced once an image loads.
1001
- *
1002
- * - 'auto' (default) prefers ImageBitmap when supported, falling back to `<img>`.
1003
- * - 'prefer-bitmap' aggressively tries ImageBitmap but falls back to `<img>` if needed.
1004
- * - 'bitmap' requires ImageBitmap support and treats failures as errors.
1005
- * - 'prefer-img' keeps `<img>` unless decoding fails, then falls back to ImageBitmap.
1006
- * - 'img' forces `<img>` and never calls `createImageBitmap`.
1007
- */
1008
- decodeMode?: ImageDecodeMode;
1009
- }
1010
-
1011
- export interface TimelineDateBoundsOptions {
1012
- /** Earliest allowed visible viewport edge. @defaultValue null */
1013
- minDate?: DateParts | null;
1014
- /** Latest allowed visible viewport edge. @defaultValue null */
1015
- maxDate?: DateParts | null;
1016
- /**
1017
- * Boundary behaviour during active user interaction.
1018
- * - 'elastic': allow resisted temporary overflow and settle back when interaction ends.
1019
- * - 'clamp': prevent overflow immediately.
1020
- * @defaultValue 'elastic'
1021
- */
1022
- overflow?: 'elastic' | 'clamp';
1023
- }
1024
-
1025
- export interface TimelineOptions {
1026
- /** Canvas width in px. @defaultValue 1000 */
1027
- width?: number;
1028
- /** Canvas height in px. @defaultValue 500 */
1029
- height?: number;
1030
- /** Canvas/backing-store configuration. */
1031
- canvas?: {
1032
- /** Device pixel ratio to use; 'auto' follows device. @defaultValue 'auto' */ dpr?:
1033
- | 'auto'
1034
- | number;
1035
- /**
1036
- * Behaviour for dpr:'auto'.
1037
- * - 'stable' (default) rounds devicePixelRatio up to the nearest integer.
1038
- * - 'native' uses the raw devicePixelRatio value.
1039
- * @defaultValue 'stable'
1040
- */
1041
- dprMode?: 'stable' | 'native';
1042
- /** Maximum DPR to apply for performance. @defaultValue 3 */ maxDpr?: number;
1043
- };
1044
- /** Per-timeline visual diagnostics. Independent from global debug logging. */
1045
- debugOverlays?: TimelineDebugOverlayOptions;
1046
- /** Vertical offset of the main line in px. @defaultValue 40 */
1047
- verticalOffset?: number;
1048
- /** Enable user interactions (wheel/drag/pinch). @defaultValue true */
1049
- enableUserControl?: boolean;
1050
- /** Show interactive cursors. @defaultValue true */
1051
- enableCursor?: boolean;
1052
- /** Zoom configuration. */
1053
- zoom?: TimelineZoomOptions;
1054
- /** Initial date (top-left when position not offset). @defaultValue {year:1990,month:1,day:1} */
1055
- initialDate?: DateParts;
1056
- /**
1057
- * Optional visible viewport date bounds. Bounds constrain viewport edges, not only the centre date.
1058
- * With both dates set, the settled viewport stays within `[minDate, maxDate]`.
1059
- */
1060
- bounds?: TimelineDateBoundsOptions;
1061
- /** Shift BCE input dates by +1 so 1BCE = year 0 internally. @defaultValue false */
1062
- shiftBceDates?: boolean;
1063
- /** Hide the built-in Histropedia branding/footer links. Defaults to false when omitted. */
1064
- disableBranding?: boolean;
1065
- /**
1066
- * Drag activation vicinity near the main line.
1067
- * - boolean: enable/disable dragging anywhere on the timeline
1068
- * - function: (event, relativePosition) => boolean; return true to allow dragging
1069
- * where relativePosition is { left: number; top: number }
1070
- * - object: { up?: number; down?: number } in pixels from the main line
1071
- * @defaultValue true
1072
- */
1073
- draggingVicinity?:
1074
- | boolean
1075
- | ((event: PointerEvent, relativePosition: { left: number; top: number }) => boolean)
1076
- | { up?: number; down?: number };
1077
- /** Timeline panning configuration. */
1078
- pan?: TimelinePanOptions;
1079
- /** Canvas/UI styling. */
1080
- style?: TimelineStyleOptions;
1081
- /** Time band configuration. */
1082
- timeBand?: TimeBandOptions;
1083
- /** Chart layer configuration. */
1084
- chart?: ChartOptions;
1085
- /** Lane configuration. */
1086
- lane?: LaneOptions;
1087
- /** Article configuration. */
1088
- article?: ArticleOptions;
1089
- /** Configure image sanitization, caching, and loader behaviour. */
1090
- image?: ImageOptions;
1091
- /** Register event handlers via object literal: { 'article-click': fn, ... } */
1092
- on?: Partial<EventHandlerMap>;
1093
- /**
1094
- * Called after every draw pass.
1095
- *
1096
- * @deprecated Deprecated since 1.3.0 — Use `on['timeline-render-end']` instead.
1097
- */
1098
- onRedraw?: (ctx: RedrawContext) => void;
1099
- /**
1100
- * Article click handler configured via this option.
1101
- *
1102
- * @deprecated Deprecated since 1.3.0 — Use `on['article-click']` for click events or `on['article-select']` for selection by any means.
1103
- */
1104
- onArticleClick?: (article: Article, event?: Event) => void;
1105
- /**
1106
- * Article double click handler configured via this option.
1107
- *
1108
- * @deprecated Deprecated since 1.3.0 — Use `on['article-dblclick']` instead.
1109
- */
1110
- onArticleDoubleClick?: (article: Article, event?: Event) => void;
1111
- /**
1112
- * Timeline save handler configured via this option.
1113
- *
1114
- * @deprecated Deprecated since 1.3.0 — Use `on['timeline-state-change']` instead.
1115
- */
1116
- onSave?: (state: string) => void;
1117
- }
1118
-
1119
- // ------------------------------------------------------------------------------------
1120
- // Events
1121
- // ------------------------------------------------------------------------------------
1122
-
1123
- /** Token produced by the timescale manager for marker drawing and viewport drag payloads. */
1124
- export interface TimelineToken {
1125
- unit: ZoomLevel;
1126
- value: DmyInstance;
1127
- length: number;
1128
- label?: string;
1129
- type?: MarkerType;
1130
- drawX?: number;
1131
- }
1132
-
1133
- export interface ZoomPayload {
1134
- zoom: number;
1135
- zoomDelta: number;
1136
- }
1137
- export interface WheelPayload {
1138
- centre: { x: number; y: number };
1139
- zoom: number;
1140
- zoomDelta: number;
1141
- }
1142
- export interface PinchPayload {
1143
- centre: { x: number; y: number } | null;
1144
- scale: number;
1145
- zoom: number;
1146
- distance: number;
1147
- zoomDelta: number;
1148
- dx?: number;
1149
- totalZoomDelta: number;
1150
- pointers: PointerEvent[];
1151
- }
1152
- export interface ViewportDragPayload {
1153
- startToken: TimelineToken;
1154
- offsetX: number;
1155
- dx?: number;
1156
- totalDx?: number;
1157
- }
1158
- export interface TimelineClickPayload {
1159
- viewport: { x: number; y: number };
1160
- }
1161
- export interface DragDelta {
1162
- dx: number;
1163
- dy: number;
1164
- totalDx: number;
1165
- totalDy: number;
1166
- }
1167
-
1168
- /** Token cache exposed to redraw hooks for advanced positioning work. */
1169
- export interface RedrawCycleContext {
1170
- tokens: {
1171
- start: DmyInstance;
1172
- end: DmyInstance;
1173
- [tokenKey: string]: DmyInstance | number;
1174
- };
1175
- }
1176
-
1177
- /** Data delivered to redraw lifecycle handlers. */
1178
- export interface RedrawContext {
1179
- canvasContext: CanvasRenderingContext2D;
1180
- /** Present for timeline-render-end; omitted for timeline-render-start. */
1181
- drawCycleContext?: RedrawCycleContext;
1182
- top: number;
1183
- }
1184
-
1185
- export interface EventPayloadMap {
1186
- // zoom
1187
- zoom: [ZoomPayload];
1188
- 'zoom-wheel': [WheelPayload, WheelEvent];
1189
- 'zoom-pinch-start': [PinchPayload, PointerEvent];
1190
- 'zoom-pinch': [PinchPayload, PointerEvent];
1191
- 'zoom-pinch-end': [PinchPayload, PointerEvent];
1192
- // viewport
1193
- 'viewport-drag-start': [ViewportDragPayload, PointerEvent];
1194
- 'viewport-drag': [ViewportDragPayload, PointerEvent];
1195
- 'viewport-drag-end': [ViewportDragPayload, PointerEvent];
1196
- // articles
1197
- 'article-pointerenter': [Article, PointerEvent];
1198
- 'article-pointermove': [Article, PointerEvent];
1199
- 'article-pointerleave': [Article, PointerEvent];
1200
- 'article-click': [Article, PointerEvent];
1201
- 'article-dblclick': [Article, PointerEvent];
1202
- 'article-select': [Article];
1203
- 'article-drag-start': [Article, PointerEvent];
1204
- 'article-drag': [Article, DragDelta, PointerEvent];
1205
- 'article-drag-end': [Article, PointerEvent];
1206
- // timeline
1207
- 'timeline-render-start': [RedrawContext];
1208
- 'timeline-render-end': [RedrawContext];
1209
- 'timeline-click': [TimelineClickPayload, PointerEvent];
1210
- 'timeline-dblclick': [TimelineClickPayload, PointerEvent];
1211
- 'timeline-state-change': [string];
1212
- }
1213
-
1214
- export type TimelineEvent = keyof EventPayloadMap;
1215
- export type EventHandlerMap = { [K in TimelineEvent]: (...args: EventPayloadMap[K]) => void };
1216
-
1217
- /** Backward-compatible alias for the article instance passed as `this` to card layout hooks. */
1218
- export type CardLayoutArticleContext = Article;
1219
-
1220
- /** Full card layout definition stored by the runtime registry. */
1221
- export interface RegisteredCardLayout {
1222
- name: string;
1223
- draw(this: Article, ctx: CanvasRenderingContext2D): void;
1224
- getWidth?(this: Article): number;
1225
- getHeight?(this: Article): number;
1226
- getIconBox?(this: Article): CardLayoutIconBox | false;
1227
- defaultStyle?: ArticleStyle;
1228
- defaultHoverStyle?: ArticleStyle;
1229
- defaultActiveStyle?: ArticleStyle;
1230
- }
1231
-
1232
- /** Alias definition that points one layout name at another registered layout. */
1233
- export interface CardLayoutAliasDefinition {
1234
- name: string;
1235
- aliasOf: CardLayoutName;
1236
- }
1237
-
1238
- /** Definition accepted by `Timeline.registerCardLayout(...)`. */
1239
- export type CardLayoutDefinition = RegisteredCardLayout | CardLayoutAliasDefinition;
1240
-
1241
- // ------------------------------------------------------------------------------------
1242
- // Classes
1243
- // ------------------------------------------------------------------------------------
1244
-
1245
- export interface AnimOptions {
1246
- /** Duration in ms. @defaultValue 2000 */ duration?: number;
1247
- /** Easing name. @defaultValue 'swing' */ easing?: EasingName;
1248
- /** Callback when animation completes. */ complete?: () => void;
1249
- /** Additional horizontal shift, used by pinch. */ offsetX?: number;
1250
- }
1251
-
1252
- export type FitDateRangePadding = number | { left?: number; right?: number };
1253
-
1254
- export interface FitDateRangeAnimationOptions {
1255
- /** Enable simultaneous animated zooming and panning. @defaultValue false */
1256
- active?: boolean;
1257
- /** Duration in ms. Inherits the viewport animation default when omitted. */
1258
- duration?: number;
1259
- /** Easing name. Inherits the viewport animation default when omitted. */
1260
- easing?: EasingName;
1261
- }
1262
-
1263
- export interface FitDateRangeOptions {
1264
- /** Edge padding in px, or per-side padding for asymmetric fits. @defaultValue 0 */
1265
- padding?: FitDateRangePadding;
1266
- /** Optional animation settings for the fit. */
1267
- animation?: FitDateRangeAnimationOptions;
1268
- }
1269
-
1270
- export interface SetStartDateOptions {
1271
- /** Left padding in px for the supplied start date. This only affects the left side. @defaultValue 0 */
1272
- padding?: number;
1273
- /** Optional animation settings matching `fitDateRange(..., { animation })`. */
1274
- animation?: FitDateRangeAnimationOptions;
1275
- }
1276
-
1277
- export interface FitArticleRangeOptions {
1278
- /** Edge padding in px, or per-side padding for asymmetric fits. @defaultValue 0 */
1279
- padding?: FitDateRangePadding;
1280
- /** Optional animation settings for the fit. */
1281
- animation?: FitDateRangeAnimationOptions;
1282
- }
1283
-
1284
- export interface FitArticlesOptions {
1285
- /** Space in px between the outermost fitted article cards and the viewport edge. @defaultValue 40 */
1286
- padding?: FitDateRangePadding;
1287
- /** Optional article predicate. Defaults to fitting articles that are not `hiddenByFilter`. */
1288
- articleFilter?: (article: Article) => boolean;
1289
- /** Optional animation settings for the fit. */
1290
- animation?: FitDateRangeAnimationOptions;
1291
- }
1292
-
1293
- export class Timeline {
1294
- /**
1295
- * Create a timeline bound to an existing canvas element or a container element (a canvas will be created).
1296
- * @example
1297
- * const tl = new Timeline(containerEl, { initialDate: { year: 1066, month: 10, day: 14 } });
1298
- */
1299
- constructor(container: HTMLCanvasElement | HTMLElement, options?: TimelineOptions);
1300
-
1301
- /** Root canvas used for rendering. */
1302
- readonly canvas: HTMLCanvasElement;
1303
- /** 2D drawing context bound to `canvas`. */
1304
- readonly canvasContext: CanvasRenderingContext2D;
1305
- /** Current merged runtime options. */
1306
- options: TimelineOptions;
1307
- /** User-visible title used by serialized state helpers. */
1308
- title: string;
1309
- /** Current y-coordinate of the timeline baseline in canvas pixels. */
1310
- top: number;
1311
- readonly articles: Article[];
1312
- readonly lanes: Lane[];
1313
- readonly timeBands: TimeBand[];
1314
- readonly charts: Chart[];
1315
-
1316
- /** Register a reusable card layout globally for all timelines. */
1317
- static registerCardLayout(definition: CardLayoutDefinition): void;
1318
- /** Resolve a previously registered card layout by name. */
1319
- static getCardLayout(name: CardLayoutName): RegisteredCardLayout | null;
1320
-
1321
- // Data loading
1322
- /** Load articles and redraw. */
1323
- load(articles: ArticleData[]): void;
1324
- /** Load lanes and redraw. */
1325
- loadLanes(lanes: LaneData[]): void;
1326
- /** Load time bands and redraw. */
1327
- loadTimeBands(bands: TimeBandData[]): void;
1328
- /** Load new charts and redraw; duplicate ids are skipped with a warning. */
1329
- loadCharts(charts: ChartData[]): void;
1330
- /** Remove a chart by id and redraw when found. */
1331
- removeChart(id: string | number): boolean;
1332
- /** Remove all loaded charts and redraw. */
1333
- clearCharts(): void;
1334
- /** Stamp a lane id onto a batch of charts, load them, and redraw. */
1335
- loadLaneCharts(laneId: string | number, charts: ChartData[]): void;
1336
- /** Stamp a lane id onto a batch of articles, load them, and redraw. */
1337
- loadLaneArticles(laneId: string | number, articles: ArticleData[]): void;
1338
-
1339
- // Query
1340
- getArticleById(id: string | number): Article | undefined;
1341
- getActiveArticle(): Article | null;
1342
- getLaneById(id: string | number): Lane | undefined;
1343
- getChartById(id: string | number): Chart | undefined;
1344
- hasArticle(id: string | number): boolean;
1345
-
1346
- // Selection and ordering
1347
- bringFront(articleId: string | number): void;
1348
- /** Selects and brings the article to the front; emits 'article-select'. */
1349
- select(articleId: string | number): void;
1350
- /** Reorder articles to match the array of ids (topmost last). */
1351
- reorderArticles(ids: Array<string | number>): void;
1352
- /** Move a lane to a public lane index where 0 is nearest the axis. */
1353
- moveLaneToIndex(lane: Lane | string | number, index: number): Lane;
1354
- /** Move lane so it ends up immediately before targetLane. */
1355
- moveLaneBefore(lane: Lane | string | number, targetLane: Lane | string | number): Lane;
1356
- /** Move lane so it ends up immediately after targetLane. */
1357
- moveLaneAfter(lane: Lane | string | number, targetLane: Lane | string | number): Lane;
1358
- /** Move lane one position farther from the axis. */
1359
- moveLaneUp(lane: Lane | string | number): Lane;
1360
- /** Move lane one position closer to the axis. */
1361
- moveLaneDown(lane: Lane | string | number): Lane;
1362
- /** Update the serializable title stored by `getStatusForSave()`. */
1363
- setTitle(title: string): void;
1364
-
1365
- // Navigation & view
1366
- /** Set start date (left edge). Accepts Dmy-compatible objects or strings in "-YYYY-MM-DD" style. */
1367
- setStartDate(date: Dmy | string, options?: SetStartDateOptions): void;
1368
- /** @deprecated Use `setStartDate(date, { padding })` instead. */
1369
- setStartDate(date: Dmy | string, pixelOffset?: number): void;
1370
- /** Set centre date using startDate + offset computation. */
1371
- setCentreDate(date: Dmy | string, pixelOffset?: number): void;
1372
- /** Current zoom value. */
1373
- getZoom(): number;
1374
- /** Set zoom (optionally pivoting around a pixel). */
1375
- setZoom(zoom: number, pivotPixel?: number, offsetX?: number): void;
1376
- /** Smooth pan by pixels (negative = right). */
1377
- goToPixelAnim(pixels: number, options?: AnimOptions): void;
1378
- /** @deprecated Use `setStartDate(dmy, { padding, animation: { active: true, ... } })` instead. */
1379
- goToDateAnim(dmy: Dmy, options?: AnimOptions): void;
1380
- /** 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. */
1381
- fitDateRange(
1382
- start: FitDateRangeInput,
1383
- end: FitDateRangeInput,
1384
- options?: FitDateRangeOptions,
1385
- ): void;
1386
- /** Fit a single article's full period into the viewport using `article.period.from` and `article.period.to`. */
1387
- fitArticleRange(article: Article, options?: FitArticleRangeOptions): boolean;
1388
- /** Fit currently unfiltered article cards into the viewport using each article's start date, card width, and connector offset. */
1389
- fitArticles(options?: FitArticlesOptions): boolean;
1390
- /** Animate to an absolute zoom value using the built-in tween. */
1391
- zoomToAnim(newZoom: number, complete?: () => void): void;
1392
-
1393
- // Drawing
1394
- /** Request a redraw (coalesced if an animation is running). */
1395
- requestRedraw(redrawFunction?: () => void): void;
1396
- /** Full draw cycle. */
1397
- redraw(): void;
1398
- /** Reposition-only draw (for zoom/start-date changes). */
1399
- repositionRedraw(): void;
1400
- /** Default draw including reposition and stacking. */
1401
- defaultRedraw(): void;
1402
- /** Toggle auto fit-to-height for article stacking and return the new enabled state. */
1403
- toggleAutoFit(): boolean;
1404
- /** Recompute row spacing so visible rows fit in the available height. */
1405
- fitToHeight(withRedraw?: boolean): number | undefined;
1406
- /**
1407
- * @deprecated Deprecated since 1.3.0 — Use `timeline.on('timeline-render-end', handler)` instead.
1408
- */
1409
- onRedraw(handler: (ctx: RedrawContext) => void): void;
1410
-
1411
- // Interaction toggles
1412
- enableZoomByWheel(): void;
1413
- disableZoomByWheel(): void;
1414
-
1415
- // Misc utilities
1416
- /** Current canvas width in CSS pixels. */
1417
- getWidth(): number;
1418
- /** Current applied device pixel ratio for the canvas. */
1419
- getCanvasDpr(): number;
1420
- /** Get canvas-relative coordinates for a DOM event. */
1421
- getRelativePosition(evt: MouseEvent | PointerEvent | TouchEvent): { left: number; top: number };
1422
-
1423
- // Options
1424
- /** Get or set options via dot-path or partial object. */
1425
- setOption(path: string, value: unknown): void;
1426
- setOption<T extends DeepPartial<TimelineOptions>>(patch: T): void;
1427
- setOption(path: string): unknown;
1428
- /** Resize the canvas. */
1429
- setSize(width: number, height: number): void;
1430
- /** Serialize the current timeline state to the legacy compact JSON string. */
1431
- getStatusForSave(): string;
1432
- /** Register a reusable card layout from an instance. Delegates to the static method. */
1433
- registerCardLayout(definition: CardLayoutDefinition): void;
1434
- /** Resolve a previously registered card layout by name. */
1435
- getCardLayout(name: CardLayoutName): RegisteredCardLayout | null;
1436
-
1437
- // Event bus
1438
- on<T extends TimelineEvent>(event: T, handler: (...args: EventPayloadMap[T]) => void): void;
1439
- on(event: string, handler: (...args: any[]) => void): void;
1440
- off<T extends TimelineEvent>(event: T, handler: (...args: EventPayloadMap[T]) => void): void;
1441
- off(event: string, handler: (...args: any[]) => void): void;
1442
- /**
1443
- * Trigger a built-in or custom event.
1444
- * Custom event payloads are intentionally untyped because the runtime does not enforce a schema.
1445
- */
1446
- trigger<T extends TimelineEvent>(event: T, ...args: EventPayloadMap[T]): void;
1447
- trigger(event: string, ...args: any[]): void;
1448
- }
1449
-
1450
- export class Article {
1451
- /** Stable article identifier. */
1452
- id: string | number;
1453
- /** Owning timeline instance. */
1454
- owner: Timeline;
1455
- /** Latest runtime data backing this article. */
1456
- data: ArticleData;
1457
- title: string;
1458
- subtitle: string;
1459
- rank: number;
1460
- laneId: string | number | null;
1461
- cardLayout: CardLayoutName | null;
1462
- period: ArticlePeriod;
1463
- position: CanvasPosition;
1464
- registeredPosition: CanvasPosition;
1465
- offset: CanvasPosition;
1466
- finalOffset: CanvasPosition;
1467
- indicator: ArticleIndicator;
1468
- row: number;
1469
- image: CanvasImageSource | null;
1470
- imageLoaded: boolean;
1471
- style: ArticleStyle;
1472
- hoverStyle: ArticleStyle;
1473
- activeStyle: ArticleStyle;
1474
- activeHoverStyle: ArticleStyle;
1475
- opacity: number;
1476
- isActive: boolean;
1477
- isVisible: boolean;
1478
- isDragging: boolean;
1479
- isStarred: boolean;
1480
- isMouseover: boolean;
1481
- hiddenByFilter: boolean;
1482
- isDataLoaded: boolean;
1483
-
1484
- /** Update article data at runtime (dot-path or object). */
1485
- setOption(path: string, value: unknown): void;
1486
- setOption(patch: Partial<ArticleData>): void;
1487
- setOption(path: string): unknown;
1488
-
1489
- /** Update normal style (dot-path or object). */
1490
- setStyle(path: string, value: unknown): void;
1491
- setStyle(style: Partial<ArticleStyle>): void;
1492
- setStyle(path: string): unknown;
1493
-
1494
- /** Update hover style (dot-path or object). */
1495
- setHoverStyle(path: string, value: unknown): void;
1496
- setHoverStyle(style: Partial<ArticleStyle>): void;
1497
- setHoverStyle(path: string): unknown;
1498
-
1499
- /** Update active style (dot-path or object). */
1500
- setActiveStyle(path: string, value: unknown): void;
1501
- setActiveStyle(style: Partial<ArticleStyle>): void;
1502
- setActiveStyle(path: string): unknown;
1503
-
1504
- /** Move instantly to absolute canvas position. */
1505
- moveTo(position: { left: number; top: number }): void;
1506
- /** Move by delta from current position. */
1507
- moveToOffset(offset: { left: number; top: number }): void;
1508
- /** Override the card layout used by this article. */
1509
- setCardLayout(cardLayout: CardLayoutName): void;
1510
- /** Small per-layout memoization helper. */
1511
- memo<T>(key: string, factory: () => T): T;
1512
- /** Clear cached measurements/layout artifacts for this article. */
1513
- invalidateCaches(): void;
1514
- /** Resolve the clickable star hit area, or `false` when none is present. */
1515
- getIconBox(): CardLayoutIconBox | false;
1516
-
1517
- /** Measured card width. */
1518
- getWidth(): number;
1519
- /** Measured card height. */
1520
- getHeight(): number;
1521
- }
1522
-
1523
- export class TimeBand {
1524
- id: string | number;
1525
- owner: Timeline;
1526
- data: TimeBandData;
1527
- title: string;
1528
- style: TimeBandStyle;
1529
- period: TimeBandPeriod;
1530
- visibility: VisibilityState;
1531
-
1532
- /** Update band data at runtime (dot-path or object). */
1533
- setOption(path: string, value: unknown): void;
1534
- setOption(patch: Partial<TimeBandData>): void;
1535
- setOption(path: string): unknown;
1536
-
1537
- /** Update band style (dot-path or object). */
1538
- setStyle(path: string, value: unknown): void;
1539
- setStyle(style: Partial<TimeBandStyle>): void;
1540
- setStyle(path: string): unknown;
1541
- }
1542
-
1543
- export class Chart {
1544
- id: string | number;
1545
- owner: Timeline;
1546
- data: ChartData;
1547
- type: ChartType;
1548
- title: string;
1549
- laneId: string | number | null;
1550
- area: { up?: AreaValue; down?: AreaValue } | null;
1551
- scale: ChartScaleOptions;
1552
- typeOptions: ChartTypeOptions;
1553
- style: ChartStyle;
1554
- series: Array<{
1555
- id: string | number;
1556
- title: string;
1557
- style: ChartSeriesStyle;
1558
- points: Array<{ date: DmyInstance; value: number | null }>;
1559
- }>;
1560
- visibility: VisibilityState;
1561
-
1562
- /** Add one series to this chart and redraw. */
1563
- addSeries(series: ChartSeriesData): void;
1564
-
1565
- /** Remove one series by id and redraw when found. */
1566
- removeSeriesById(id: string | number): boolean;
1567
-
1568
- /** Return the normalized runtime series with this id. */
1569
- getSeriesById(id: string | number):
1570
- | {
1571
- id: string | number;
1572
- title: string;
1573
- style: ChartSeriesStyle;
1574
- points: Array<{ date: DmyInstance; value: number | null }>;
1575
- }
1576
- | undefined;
1577
-
1578
- /** Update chart data at runtime (dot-path or object). */
1579
- setOption(path: string, value: unknown): void;
1580
- setOption(patch: Partial<Omit<ChartData, 'id'>>): void;
1581
- setOption(path: string): unknown;
1582
-
1583
- /** Update chart style (dot-path or object). */
1584
- setStyle(path: string, value: unknown): void;
1585
- setStyle(style: Partial<ChartStyle>): void;
1586
- setStyle(path: string): unknown;
1587
-
1588
- /** Resolve the active y-domain for the chart. */
1589
- getScaleDomain(drawCycleContext?: RedrawCycleContext): { min: number; max: number };
1590
- }
1591
-
1592
- export class Lane {
1593
- id: string | number;
1594
- owner: Timeline;
1595
- data: LaneData;
1596
- title: string;
1597
- layout: LaneLayout;
1598
- style: LaneStyle;
1599
- order: number;
1600
- isImplicit: boolean;
1601
- visibility: VisibilityState;
1602
-
1603
- /** Update lane data at runtime (dot-path or object). */
1604
- setOption(path: string, value: unknown): void;
1605
- setOption(patch: Partial<LaneData>): void;
1606
- setOption(path: string): unknown;
1607
-
1608
- /** Hide the lane by setting visibility to "hidden". */
1609
- hide(): void;
1610
-
1611
- /** Show the lane by setting visibility to "visible". */
1612
- show(): void;
1613
-
1614
- /** Move this lane to a public lane index where 0 is nearest the axis. */
1615
- moveToIndex(index: number): Lane;
1616
-
1617
- /** Move this lane so it ends up immediately before targetLane. */
1618
- moveBefore(targetLane: Lane | string | number): Lane;
1619
-
1620
- /** Move this lane so it ends up immediately after targetLane. */
1621
- moveAfter(targetLane: Lane | string | number): Lane;
1622
-
1623
- /** Move this lane one position farther from the axis. */
1624
- moveUp(): Lane;
1625
-
1626
- /** Move this lane one position closer to the axis. */
1627
- moveDown(): Lane;
1628
-
1629
- /** Update lane style (dot-path or object). */
1630
- setStyle(path: string, value: unknown): void;
1631
- setStyle(style: Partial<LaneStyle>): void;
1632
- setStyle(path: string): unknown;
1633
- }
1634
-
1635
- /** Built-in sorter signature used by stacking and layout helpers. */
1636
- export type ArticleSorter = (articleA: any, articleB: any) => number;
1637
-
1638
- export const ARTICLE_DURATION_SORTER: ArticleSorter;
1639
- export const ARTICLE_DURATION_YEARS_SORTER: ArticleSorter;
1640
- export const ARTICLE_IMAGE_QUEUE_SORTER: ArticleSorter;
1641
- export const ARTICLE_FROM_SORTER: ArticleSorter;
1642
- export const ARTICLE_TO_SORTER: ArticleSorter;
1643
- export const ARTICLE_TOP_SORTER: ArticleSorter;
1644
- export const ARTICLE_ROW_SORTER: ArticleSorter;
1645
- export const ARTICLE_RANK_SORTER: ArticleSorter;
1646
- export const ARTICLE_EFFECTIVE_RANK_SORTER: ArticleSorter;
1647
- export const ARTICLE_POSITION_SORTER: ArticleSorter;
1648
-
1649
- // ------------------------------------------------------------------------------------
1650
- // Default export for UMD / ESM default
1651
- // ------------------------------------------------------------------------------------
1652
-
1653
- export interface HistropediaApi {
1654
- Timeline: typeof Timeline;
1655
- Article: typeof Article;
1656
- Chart: typeof Chart;
1657
- Lane: typeof Lane;
1658
- TimeBand: typeof TimeBand;
1659
- Dmy: DmyConstructor;
1660
- Logger: LoggerInstance;
1661
- setDebug(enabled: boolean): void;
1662
- isDebugEnabled(): boolean;
1663
- enableDebug(): void;
1664
- disableDebug(): void;
1665
- ARTICLE_DURATION_SORTER: typeof ARTICLE_DURATION_SORTER;
1666
- ARTICLE_DURATION_YEARS_SORTER: typeof ARTICLE_DURATION_YEARS_SORTER;
1667
- ARTICLE_IMAGE_QUEUE_SORTER: typeof ARTICLE_IMAGE_QUEUE_SORTER;
1668
- ARTICLE_FROM_SORTER: typeof ARTICLE_FROM_SORTER;
1669
- ARTICLE_TO_SORTER: typeof ARTICLE_TO_SORTER;
1670
- ARTICLE_TOP_SORTER: typeof ARTICLE_TOP_SORTER;
1671
- ARTICLE_ROW_SORTER: typeof ARTICLE_ROW_SORTER;
1672
- ARTICLE_RANK_SORTER: typeof ARTICLE_RANK_SORTER;
1673
- ARTICLE_EFFECTIVE_RANK_SORTER: typeof ARTICLE_EFFECTIVE_RANK_SORTER;
1674
- ARTICLE_POSITION_SORTER: typeof ARTICLE_POSITION_SORTER;
1675
- MINOR_MARKER: typeof MINOR_MARKER;
1676
- MAJOR_MARKER: typeof MAJOR_MARKER;
1677
- ERA_MARKER: typeof ERA_MARKER;
1678
- ZOOM_DAY: typeof ZOOM_DAY;
1679
- ZOOM_MONTH: typeof ZOOM_MONTH;
1680
- ZOOM_YEAR: typeof ZOOM_YEAR;
1681
- ZOOM_DECADE: typeof ZOOM_DECADE;
1682
- ZOOM_CENTURY: typeof ZOOM_CENTURY;
1683
- ZOOM_MILLENNIUM: typeof ZOOM_MILLENNIUM;
1684
- ZOOM_10_THOUSAND_YEARS: typeof ZOOM_10_THOUSAND_YEARS;
1685
- ZOOM_100_THOUSAND_YEARS: typeof ZOOM_100_THOUSAND_YEARS;
1686
- ZOOM_MILLION_YEARS: typeof ZOOM_MILLION_YEARS;
1687
- ZOOM_10_MILLION_YEARS: typeof ZOOM_10_MILLION_YEARS;
1688
- ZOOM_100_MILLION_YEARS: typeof ZOOM_100_MILLION_YEARS;
1689
- ZOOM_BILLION_YEARS: typeof ZOOM_BILLION_YEARS;
1690
- YEAR_PREFIXES: typeof YEAR_PREFIXES;
1691
- DENSITY_SETTINGS: typeof DENSITY_SETTINGS;
1692
- PRECISION_DAY: typeof PRECISION_DAY;
1693
- PRECISION_MONTH: typeof PRECISION_MONTH;
1694
- PRECISION_YEAR: typeof PRECISION_YEAR;
1695
- PRECISION_DECADE: typeof PRECISION_DECADE;
1696
- PRECISION_CENTURY: typeof PRECISION_CENTURY;
1697
- PRECISION_MILLENNIUM: typeof PRECISION_MILLENNIUM;
1698
- PRECISION_MILLION_YEARS: typeof PRECISION_MILLION_YEARS;
1699
- PRECISION_BILLION_YEARS: typeof PRECISION_BILLION_YEARS;
1700
- DENSITY_ALL: typeof DENSITY_ALL;
1701
- DENSITY_LOW: typeof DENSITY_LOW;
1702
- DENSITY_MEDIUM: typeof DENSITY_MEDIUM;
1703
- DENSITY_HIGH: typeof DENSITY_HIGH;
1704
- RANGE_ALL: typeof RANGE_ALL;
1705
- RANGE_SCREEN: typeof RANGE_SCREEN;
1706
- }
1707
-
1708
- declare const HistropediaModule: HistropediaApi;
1709
-
1710
- export default HistropediaModule;
1711
-
1712
- declare global {
1713
- const Histropedia: HistropediaApi;
1714
-
1715
- interface Window {
1716
- Histropedia: HistropediaApi;
1717
- }
1718
- }
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
+ /** Pixel-grid mode used for built-in article cards and their connectors. */
29
+ export type ArticlePixelSnapMode = 'device' | 'css' | 'none' | boolean;
30
+
31
+ /** Canvas-relative 2D position used by rendered articles. */
32
+ export interface CanvasPosition {
33
+ left: number;
34
+ top: number;
35
+ }
36
+
37
+ /** Main-line indicator coordinates for an article currently in range. */
38
+ export interface ArticleIndicator {
39
+ fromX: number;
40
+ toX: number;
41
+ }
42
+
43
+ /** Computed article time span after precision expansion. */
44
+ export interface ArticlePeriod {
45
+ from: DmyInstance;
46
+ to: DmyInstance;
47
+ isToPresent: boolean;
48
+ /** Approximate duration in decimal years, used for sorting/layout decisions. */
49
+ years: number;
50
+ }
51
+
52
+ /** Computed time-band span after precision expansion. */
53
+ export interface TimeBandPeriod {
54
+ from: DmyInstance;
55
+ to: DmyInstance;
56
+ isToPresent: boolean;
57
+ }
58
+
59
+ /** Rectangular hover/click area returned by card layouts for the built-in star interaction. */
60
+ export interface CardLayoutIconBox extends CanvasPosition {
61
+ width: number;
62
+ height: number;
63
+ /** @deprecated Drawing geometry is derived from the rectangle and ignored by hit-testing. */
64
+ centreX?: number;
65
+ /** @deprecated Drawing geometry is derived from the rectangle and ignored by hit-testing. */
66
+ centreY?: number;
67
+ /** @deprecated Drawing geometry is derived from the rectangle and ignored by hit-testing. */
68
+ innerRadius?: number;
69
+ /** @deprecated Drawing geometry is derived from the rectangle and ignored by hit-testing. */
70
+ outerRadius?: number;
71
+ }
72
+
73
+ // ------------------------------------------------------------------------------------
74
+ // Date and precision
75
+ // ------------------------------------------------------------------------------------
76
+
77
+ /** Date precision constants (public). */
78
+ export const PRECISION_MILLISECOND: 'millisecond';
79
+ export const PRECISION_SECOND: 'second';
80
+ export const PRECISION_MINUTE: 'minute';
81
+ export const PRECISION_HOUR: 'hour';
82
+ export const PRECISION_DAY: 'day';
83
+ export const PRECISION_MONTH: 'month';
84
+ export const PRECISION_YEAR: 'year';
85
+ export const PRECISION_DECADE: 'decade';
86
+ export const PRECISION_CENTURY: 'century';
87
+ export const PRECISION_MILLENNIUM: 'millennium';
88
+ export const PRECISION_MILLION_YEARS: 'million-years';
89
+ export const PRECISION_BILLION_YEARS: 'billion-years';
90
+
91
+ /** @deprecated Use the equivalent string precision instead. */
92
+ export type LegacyPrecision = 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 3 | 1;
93
+
94
+ /**
95
+ * Date precision used by Article/TimeBand ranges.
96
+ * Legacy numeric precision values remain supported for backwards compatibility.
97
+ */
98
+ export type Precision =
99
+ | typeof PRECISION_MILLISECOND
100
+ | typeof PRECISION_SECOND
101
+ | typeof PRECISION_MINUTE
102
+ | typeof PRECISION_HOUR
103
+ | typeof PRECISION_DAY
104
+ | typeof PRECISION_MONTH
105
+ | typeof PRECISION_YEAR
106
+ | typeof PRECISION_DECADE
107
+ | typeof PRECISION_CENTURY
108
+ | typeof PRECISION_MILLENNIUM
109
+ | typeof PRECISION_MILLION_YEARS
110
+ | typeof PRECISION_BILLION_YEARS
111
+ | LegacyPrecision;
112
+
113
+ /** Plain calendar date and optional time parts (no precision). */
114
+ export type DateParts = {
115
+ year: number;
116
+ month?: number;
117
+ day?: number;
118
+ hour?: number;
119
+ minute?: number;
120
+ second?: number;
121
+ millisecond?: number;
122
+ };
123
+
124
+ /** General date input that can include precision. */
125
+ export type DateInput = DateParts & { precision?: Precision };
126
+
127
+ /**
128
+ * Calendar date string without a timezone.
129
+ *
130
+ * Accepted grammar: `year[-month[-day]][T| ]hour[:minute[:second[.SSS]]]`, with the
131
+ * time portion optional. Years may be negative and may contain more than four digits,
132
+ * for example `-44-03-15T09:01` or
133
+ * `1000000000-06-15T12:30:05.250`.
134
+ */
135
+ export type DateString = string;
136
+
137
+ /** Calendar-value shape. Fields remain writable for backwards compatibility. */
138
+ export interface Dmy {
139
+ year: number;
140
+ month?: number;
141
+ day?: number;
142
+ hour?: number;
143
+ minute?: number;
144
+ second?: number;
145
+ millisecond?: number;
146
+ precision?: Precision;
147
+ }
148
+
149
+ /** Formatting options for Dmy.format. */
150
+ export interface DmyFormatOptions {
151
+ yearPrefix?: { value?: number; label?: string };
152
+ bceText?: string;
153
+ thousandsSeparator?: string;
154
+ }
155
+
156
+ /** Histropedia date helper instance. */
157
+ export interface DmyInstance extends Dmy {
158
+ getDaysTo(other: Dmy): number;
159
+ getMonthsTo(other: Dmy): number;
160
+ getYearsTo(other: Dmy): number;
161
+ getLeapYearsSinceYearZero(): number;
162
+ getDaysSinceYearZero(): number;
163
+ getMillisecondsOfDay(): number;
164
+ getDayOfYear(): number;
165
+ getNextDay(): DmyInstance;
166
+ addDays(amount: number): DmyInstance;
167
+ addHours(amount: number): DmyInstance;
168
+ addMinutes(amount: number): DmyInstance;
169
+ addSeconds(amount: number): DmyInstance;
170
+ addMilliseconds(amount: number): DmyInstance;
171
+ /** Supports positive amounts only and returns the first day of the resulting month. */
172
+ addMonths(amount: number): DmyInstance;
173
+ /** Adds years and returns January 1 of the resulting year. */
174
+ addYears(amount: number): DmyInstance;
175
+ getPreviousDay(): DmyInstance;
176
+ getNextMonth(): DmyInstance;
177
+ getPreviousMonth(): DmyInstance;
178
+ format(format?: string, options?: DmyFormatOptions): string;
179
+ isSame(other: Dmy): boolean;
180
+ isAfter(other: Dmy): boolean;
181
+ isBetween(from: Dmy, to: Dmy): boolean;
182
+ compare(other: Dmy): number;
183
+ toKeyString(): string;
184
+ /** Approximate decimal-year position rounded to two decimal places. */
185
+ asFloat(): number;
186
+ isInFuture(): boolean;
187
+ /** Converts BCE numbering in place and returns this instance. */
188
+ convertToIso(): DmyInstance;
189
+ }
190
+
191
+ /** Date value accepted by timeline navigation and date-bound APIs. */
192
+ export type DateValue = DmyInstance | DateParts | DateString;
193
+
194
+ export type FitDateRangeInput = DateValue;
195
+
196
+ export interface DmyConstructor {
197
+ /** Creates a mutable calendar value. Raw omitted month/day fields remain undefined. */
198
+ new (
199
+ year: number,
200
+ month?: number,
201
+ day?: number,
202
+ hour?: number,
203
+ minute?: number,
204
+ second?: number,
205
+ millisecond?: number,
206
+ ): DmyInstance;
207
+ new (value: Dmy): DmyInstance;
208
+ readonly prototype: DmyInstance;
209
+ getByDayOfYear(year: number, dayOfYear: number): DmyInstance;
210
+ CreateAsStartOfPeriod(date: Dmy, precision?: Precision): DmyInstance;
211
+ CreateAsEndOfPeriod(date: Dmy, precision?: Precision): DmyInstance;
212
+ CreateAsExclusiveEndOfPeriod(date: Dmy, precision?: Precision): DmyInstance;
213
+ /** Returns the current local date at midnight. */
214
+ now(): DmyInstance;
215
+ /** Invalid grammar returns a Dmy whose year is NaN; parsed fields use legacy normalization. */
216
+ fromString(dateString: DateString): DmyInstance;
217
+ }
218
+
219
+ export const Dmy: DmyConstructor;
220
+
221
+ // ------------------------------------------------------------------------------------
222
+ // Public constants (used in options)
223
+ // ------------------------------------------------------------------------------------
224
+
225
+ export const DENSITY_ALL: 'all';
226
+ export const DENSITY_LOW: 'low';
227
+ export const DENSITY_MEDIUM: 'medium';
228
+ export const DENSITY_HIGH: 'high';
229
+ /** @deprecated Use the equivalent string density instead. */
230
+ export type LegacyDensity = 0 | 1 | 2 | 3;
231
+ export type Density =
232
+ | typeof DENSITY_ALL
233
+ | typeof DENSITY_LOW
234
+ | typeof DENSITY_MEDIUM
235
+ | typeof DENSITY_HIGH
236
+ | LegacyDensity;
237
+
238
+ export const RANGE_ALL: 'all';
239
+ export const RANGE_SCREEN: 'screen';
240
+ /** @deprecated Use the equivalent string auto-stacking range instead. */
241
+ export type LegacyAutoStackRange = 0 | 1;
242
+ export type AutoStackRange = typeof RANGE_ALL | typeof RANGE_SCREEN | LegacyAutoStackRange;
243
+
244
+ export const MINOR_MARKER: 0;
245
+ export const MAJOR_MARKER: 1;
246
+ export const ERA_MARKER: 2;
247
+ export type MarkerType = typeof MINOR_MARKER | typeof MAJOR_MARKER | typeof ERA_MARKER;
248
+
249
+ export const ZOOM_DAY: 0;
250
+ export const ZOOM_HOUR: -1;
251
+ export const ZOOM_MINUTE: -2;
252
+ export const ZOOM_SECOND: -3;
253
+ export const ZOOM_MILLISECOND: -4;
254
+ export const ZOOM_MONTH: 1;
255
+ export const ZOOM_YEAR: 2;
256
+ export const ZOOM_DECADE: 3;
257
+ export const ZOOM_CENTURY: 4;
258
+ export const ZOOM_MILLENNIUM: 5;
259
+ export const ZOOM_10_THOUSAND_YEARS: 6;
260
+ export const ZOOM_100_THOUSAND_YEARS: 7;
261
+ export const ZOOM_MILLION_YEARS: 8;
262
+ export const ZOOM_10_MILLION_YEARS: 9;
263
+ export const ZOOM_100_MILLION_YEARS: 10;
264
+ export const ZOOM_BILLION_YEARS: 11;
265
+ export type ZoomLevel =
266
+ | typeof ZOOM_MILLISECOND
267
+ | typeof ZOOM_SECOND
268
+ | typeof ZOOM_MINUTE
269
+ | typeof ZOOM_HOUR
270
+ | typeof ZOOM_DAY
271
+ | typeof ZOOM_MONTH
272
+ | typeof ZOOM_YEAR
273
+ | typeof ZOOM_DECADE
274
+ | typeof ZOOM_CENTURY
275
+ | typeof ZOOM_MILLENNIUM
276
+ | typeof ZOOM_10_THOUSAND_YEARS
277
+ | typeof ZOOM_100_THOUSAND_YEARS
278
+ | typeof ZOOM_MILLION_YEARS
279
+ | typeof ZOOM_10_MILLION_YEARS
280
+ | typeof ZOOM_100_MILLION_YEARS
281
+ | typeof ZOOM_BILLION_YEARS;
282
+
283
+ export interface YearPrefixSettings {
284
+ label: string;
285
+ value: number;
286
+ }
287
+
288
+ export const YEAR_PREFIXES: Readonly<Record<number, YearPrefixSettings>>;
289
+
290
+ export interface DensitySetting {
291
+ region: number;
292
+ zoom: {
293
+ from: number;
294
+ to: number;
295
+ };
296
+ low: number;
297
+ medium: number;
298
+ high: number;
299
+ all: number;
300
+ step: {
301
+ months: number;
302
+ days: number;
303
+ /** Binary subdivisions of one day used by sub-day density regions. */
304
+ divisionsPerDay?: number;
305
+ };
306
+ }
307
+
308
+ export const DENSITY_SETTINGS: readonly DensitySetting[];
309
+
310
+ export interface LoggerInstance {
311
+ setEnabled(enabled: boolean): void;
312
+ isEnabled(): boolean;
313
+ setPrefix(prefix: string): void;
314
+ getPrefix(): string;
315
+ log(...args: unknown[]): void;
316
+ warn(...args: unknown[]): void;
317
+ error(...args: unknown[]): void;
318
+ info(...args: unknown[]): void;
319
+ debug(...args: unknown[]): void;
320
+ group(...args: unknown[]): void;
321
+ groupCollapsed(...args: unknown[]): void;
322
+ groupEnd(): void;
323
+ }
324
+
325
+ export const Logger: LoggerInstance;
326
+
327
+ // ------------------------------------------------------------------------------------
328
+ // Animation types
329
+ // ------------------------------------------------------------------------------------
330
+
331
+ /** Allowed easing names used by animations. */
332
+ export type EasingName =
333
+ | 'linear'
334
+ | 'swing'
335
+ | 'easeInQuad'
336
+ | 'easeOutQuad'
337
+ | 'easeInOutQuad'
338
+ | 'easeInCubic'
339
+ | 'easeOutCubic'
340
+ | 'easeInOutCubic';
341
+
342
+ // ------------------------------------------------------------------------------------
343
+ // Style types
344
+ // ------------------------------------------------------------------------------------
345
+
346
+ export interface ArticleStyleText {
347
+ font?: string;
348
+ color?: string;
349
+ align?: 'left' | 'right' | 'center' | 'start' | 'end';
350
+ baseline?: 'top' | 'hanging' | 'middle' | 'alphabetic' | 'bottom';
351
+ margin?: number;
352
+ lineHeight?: number;
353
+ numberOfLines?: number;
354
+ offsetY?: number;
355
+ }
356
+
357
+ export interface ArticleStyle {
358
+ /** @defaultValue 150 */
359
+ width?: number;
360
+ /** Landscape card layout only. @defaultValue 70 */
361
+ height?: number;
362
+ /** Card background fill color. @defaultValue '#e5e5e5' */
363
+ color?: string;
364
+ /** Inner card background. @defaultValue '#fff' */
365
+ backgroundColor?: string;
366
+ /** Portrait card layout only. @defaultValue 3 */
367
+ topRadius?: number;
368
+ /** Landscape card layout only. @defaultValue 4 */
369
+ borderRadius?: number;
370
+ /**
371
+ * Shorthand for the article image maximum height.
372
+ *
373
+ * @deprecated Deprecated since 1.3.0 — Use `image.maxHeight` instead.
374
+ * @defaultValue 400
375
+ */
376
+ maxImageHeight?: number;
377
+ image?: {
378
+ /** @defaultValue 'natural' */
379
+ shape?: 'natural' | 'square' | 'circle';
380
+ /** @defaultValue 0 */
381
+ margin?: number;
382
+ /** @defaultValue 200 */
383
+ maxHeight?: number;
384
+ /** @defaultValue 0 */
385
+ borderRadius?: number;
386
+ };
387
+ header?: {
388
+ /** Portrait card layout only. @defaultValue 50 */
389
+ height?: number;
390
+ text?: ArticleStyleText;
391
+ };
392
+ subheader?: {
393
+ /** Portrait card layout only. @defaultValue 30 */
394
+ height?: number;
395
+ /** Portrait card layout only. @defaultValue '#555' */
396
+ color?: string;
397
+ text?: ArticleStyleText;
398
+ };
399
+ shadow?: {
400
+ /** @defaultValue 0 */ x?: number;
401
+ /** @defaultValue 0 */ y?: number;
402
+ /** @defaultValue 0 */ amount?: number;
403
+ /** @defaultValue '#000' */ color?: string;
404
+ };
405
+ border?: {
406
+ /** @defaultValue '#ddd' */ color?: string;
407
+ /** @defaultValue 1 */ width?: number;
408
+ };
409
+ connectorLine?: {
410
+ /** @defaultValue true */ visible?: boolean;
411
+ /** @defaultValue 18 */ offsetX?: number;
412
+ /** @defaultValue -20 */ offsetY?: number;
413
+ /** @defaultValue 1 */ thickness?: number;
414
+ arrow?: {
415
+ /** @defaultValue 16 */ width?: number;
416
+ /** @defaultValue 45 */ height?: number;
417
+ };
418
+ };
419
+ star?: {
420
+ /** @defaultValue 16 */ width?: number;
421
+ /** @defaultValue 3 */ margin?: number;
422
+ };
423
+ }
424
+
425
+ export interface TimeBandStyleText {
426
+ font?: string;
427
+ color?: string;
428
+ align?: 'left' | 'right' | 'center' | 'start' | 'end';
429
+ baseline?: 'top' | 'middle' | 'alphabetic' | 'bottom';
430
+ /** Vertical align inside band area. */
431
+ verticalAlign?: 'top' | 'middle' | 'bottom';
432
+ /** Horizontal/vertical padding in px. @defaultValue 4 */
433
+ margin?: number;
434
+ /** Extra vertical offset in px. @defaultValue 0 */
435
+ offsetY?: number;
436
+ }
437
+
438
+ export interface TimeBandStyle {
439
+ /** @defaultValue 'rgba(200, 200, 200, 0.25)' */
440
+ backgroundColor?: string;
441
+ border?: { color?: string; width?: number };
442
+ text?: TimeBandStyleText;
443
+ }
444
+
445
+ export interface TimelineStyleMarkerKind {
446
+ /** @defaultValue 12 (minor) / 30 (major) */ height?: number;
447
+ /** Extend marker to canvas top/bottom. @defaultValue false */ extend?: boolean;
448
+ color?: string;
449
+ futureColor?: string;
450
+ }
451
+
452
+ export interface TimelineStyleMainLineGradient {
453
+ type: 'gradient';
454
+ topColor: string;
455
+ bottomColor: string;
456
+ }
457
+
458
+ export interface TimelineDateIndicatorStyle {
459
+ /** @defaultValue 'rectangle' */ shape?: 'rectangle' | 'circle';
460
+ /** Rectangle width or circle diameter in px. @defaultValue 4 */ size?: number;
461
+ /** CSS colour or the article colour resolved for the indicator state. @defaultValue '#666' */
462
+ color?: string;
463
+ /** Canvas opacity from 0 to 1. @defaultValue 1 */ opacity?: number;
464
+ }
465
+
466
+ export interface TimelineDateIndicatorOptions extends TimelineDateIndicatorStyle {
467
+ /** @defaultValue true */ visible?: boolean;
468
+ /** Sparse overrides applied only to articles hidden by density filtering. @defaultValue { opacity: 0.25 } */
469
+ hiddenStyle?: TimelineDateIndicatorStyle;
470
+ /** Sparse overrides applied only to the active/selected article. @defaultValue { color: 'article', opacity: 1 } */
471
+ activeStyle?: TimelineDateIndicatorStyle;
472
+ }
473
+
474
+ export interface TimelineStyleOptions {
475
+ mainLine?: {
476
+ /** @defaultValue true */ visible?: boolean;
477
+ /** @defaultValue 'bottom' */ position?: 'top' | 'bottom';
478
+ /** thickness in px. @defaultValue 8 */ size?: number;
479
+ /** Solid CSS colour or a vertical gradient. @defaultValue '#cdd2d8' */
480
+ color?: string | TimelineStyleMainLineGradient;
481
+ /** Article date indicators drawn on the main line. */
482
+ dateIndicators?: TimelineDateIndicatorOptions;
483
+ };
484
+ draggingHighlight?: {
485
+ /** @defaultValue true */ visible?: boolean;
486
+ /** @defaultValue 'rgba(237, 247, 255, 0.5)' */ color?: string;
487
+ /** Up/down extents relative to the main line. Accepts px, `'edge'`, or `{ value: 'edge', padding?: number }`.
488
+ * @defaultValue {up: 0, down: 'edge'}
489
+ */
490
+ area?: { up?: AreaValue; down?: AreaValue };
491
+ };
492
+ marker?: {
493
+ minor?: TimelineStyleMarkerKind;
494
+ major?: TimelineStyleMarkerKind;
495
+ };
496
+ dateLabel?: {
497
+ minor?: {
498
+ font?: string;
499
+ color?: string;
500
+ futureColor?: string;
501
+ textAlign?: CanvasTextAlign;
502
+ offset?: { x?: number; y?: number };
503
+ /** Text appended for BCE labeling. */
504
+ bceText?: string;
505
+ /** Thousands separator for large year labels. */
506
+ thousandsSeparator?: string;
507
+ /** Map of year-scale prefixes to display (e.g. ka, Ma, Ga). */
508
+ yearPrefixes?: Record<string, { label: string; value: number; minDivision: number }>;
509
+ };
510
+ major?: {
511
+ font?: string;
512
+ color?: string;
513
+ futureColor?: string;
514
+ textAlign?: CanvasTextAlign;
515
+ offset?: { x?: number; y?: number };
516
+ sticky?: {
517
+ /** Keep the current major label visible at the viewport's left edge. `'auto'` enables this for month-scale and smaller divisions. @defaultValue 'auto' */
518
+ enabled?: 'auto' | boolean;
519
+ /** Horizontal inset from the viewport's left edge in px. @defaultValue 4 */
520
+ offsetX?: number;
521
+ };
522
+ bceText?: string;
523
+ thousandsSeparator?: string;
524
+ yearPrefixes?: Record<string, { label: string; value: number; minDivision: number }>;
525
+ };
526
+ parent?: {
527
+ /** Enable and reserve space for the parent-context row at every scale. @defaultValue false */
528
+ visible?: boolean;
529
+ /** Current major-label levels at which to render parent text. @defaultValue 'all' */
530
+ levels?: 'all' | Array<
531
+ | 'millisecond'
532
+ | 'second'
533
+ | 'minute'
534
+ | 'hour'
535
+ | 'day'
536
+ | 'month'
537
+ | 'year'
538
+ | 'decade'
539
+ | 'century'
540
+ | 'millennium'
541
+ >;
542
+ /** Reserved row height in px whenever the parent row is enabled. @defaultValue 20 */
543
+ height?: number;
544
+ font?: string;
545
+ color?: string;
546
+ futureColor?: string;
547
+ textAlign?: CanvasTextAlign;
548
+ offset?: { x?: number; y?: number };
549
+ sticky?: {
550
+ /** Keep the current parent interval visible at the viewport's left edge. @defaultValue true */
551
+ enabled?: boolean;
552
+ /** Horizontal inset from the viewport's left edge in px. @defaultValue 4 */
553
+ offsetX?: number;
554
+ };
555
+ };
556
+ };
557
+ cursor?: {
558
+ clickable?: string;
559
+ timeline?: { hover?: string; dragging?: string };
560
+ article?: { hoverDraggable?: string; dragging?: string };
561
+ };
562
+ }
563
+
564
+ // ------------------------------------------------------------------------------------
565
+ // Data schemas for loading
566
+ // ------------------------------------------------------------------------------------
567
+
568
+ export interface ArticleData {
569
+ id: string | number;
570
+ title: string;
571
+ subtitle?: string;
572
+ lane?: string | number;
573
+ from: DateInput;
574
+ to?: DateInput;
575
+ /** If true, the period ends at the present day. */
576
+ isToPresent?: boolean;
577
+ imageUrl?: string;
578
+ rank?: number;
579
+ starred?: boolean;
580
+ hiddenByFilter?: boolean | ((article: Article) => boolean);
581
+ hidePeriodLine?: boolean | ((article: Article) => boolean);
582
+ /** Initial offset from computed position (px). */
583
+ offsetLeft?: number;
584
+ /** Initial offset from computed position (px). */
585
+ offsetTop?: number;
586
+ /** Override default card layout for this article. */
587
+ cardLayout?: CardLayoutName;
588
+ style?: ArticleStyle;
589
+ hoverStyle?: ArticleStyle;
590
+ activeStyle?: ArticleStyle;
591
+ }
592
+
593
+ export interface ArticleCardLayoutBreakpoint {
594
+ maxHeight: number;
595
+ layout: CardLayoutName;
596
+ }
597
+
598
+ export interface TimeBandData {
599
+ id: string | number;
600
+ title: string;
601
+ from: DateInput;
602
+ to?: DateInput;
603
+ /** If true, the band ends at the present day. */
604
+ isToPresent?: boolean;
605
+ /** Visibility of this band: 'visible' | 'hidden' or a function returning one. Defaults to 'visible'. */
606
+ visibility?: VisibilityState | ((band: TimeBand) => VisibilityState);
607
+ style?: TimeBandStyle;
608
+ }
609
+
610
+ export type ChartType = 'line' | 'area';
611
+ export type ChartScaleSource = 'data' | 'viewport';
612
+ export type ChartScaleTickMode = 'round' | 'exact';
613
+ export type ChartAreaStackingInterpolation = 'linear' | 'step' | 'none';
614
+
615
+ export interface ChartPointData {
616
+ date: DateInput;
617
+ value: number | null;
618
+ }
619
+
620
+ export interface ChartSeriesStyle {
621
+ color?: string;
622
+ lineWidth?: number;
623
+ lineDash?: number[];
624
+ area?: {
625
+ fillColor?: string | null;
626
+ opacity?: number;
627
+ };
628
+ point?: {
629
+ visible?: boolean;
630
+ radius?: number;
631
+ fillColor?: string | null;
632
+ strokeColor?: string | null;
633
+ lineWidth?: number;
634
+ };
635
+ }
636
+
637
+ export interface ChartGridLineStyle {
638
+ visible?: boolean;
639
+ color?: string;
640
+ /** Stroke thickness in canvas pixels. */
641
+ thickness?: number;
642
+ }
643
+
644
+ export interface ChartMinorGridLineStyle extends ChartGridLineStyle {
645
+ /** Hide minor grid lines while the timeline minor unit size is below this many pixels. @defaultValue 20 */
646
+ minimumSpacing?: number;
647
+ }
648
+
649
+ export interface ChartGridLineOptions {
650
+ x?: {
651
+ major?: ChartGridLineStyle;
652
+ minor?: ChartMinorGridLineStyle;
653
+ };
654
+ y?: {
655
+ major?: ChartGridLineStyle;
656
+ };
657
+ }
658
+
659
+ export interface ChartStyle {
660
+ padding?: {
661
+ top?: number;
662
+ right?: number;
663
+ bottom?: number;
664
+ left?: number;
665
+ };
666
+ backgroundColor?: string;
667
+ gridLine?: ChartGridLineOptions;
668
+ axis?: {
669
+ visible?: boolean;
670
+ color?: string;
671
+ lineWidth?: number;
672
+ };
673
+ yAxis?: {
674
+ visible?: boolean;
675
+ font?: string;
676
+ color?: string;
677
+ margin?: number;
678
+ /** Thousands separator for numeric y-axis labels. Set to null or an empty string to disable. @defaultValue ',' */
679
+ thousandsSeparator?: string | null;
680
+ formatter?: ((value: number) => string) | null;
681
+ };
682
+ legend?: {
683
+ visible?: boolean | 'auto';
684
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
685
+ font?: string;
686
+ color?: string;
687
+ markerSize?: number;
688
+ markerGap?: number;
689
+ itemGap?: number;
690
+ inset?: { x?: number; y?: number };
691
+ };
692
+ series?: ChartSeriesStyle;
693
+ }
694
+
695
+ export interface ChartScaleOptions {
696
+ /** Use all loaded values or only values inside the current viewport. @defaultValue 'data' */
697
+ source?: ChartScaleSource;
698
+ /** Include 0 in the computed domain before padding/fixed bounds. @defaultValue false */
699
+ includeZero?: boolean;
700
+ /** Fractional padding applied to auto domains. @defaultValue 0.05 */
701
+ padding?: number;
702
+ /** Fixed minimum y value. */
703
+ min?: number;
704
+ /** Fixed maximum y value. */
705
+ max?: number;
706
+ /** Controls how y-axis tick values are generated. @defaultValue 'round' */
707
+ tickMode?: ChartScaleTickMode;
708
+ /** Preferred number of y-axis ticks. Rounded ticks may vary slightly. @defaultValue 5 */
709
+ tickTarget?: number;
710
+ }
711
+
712
+ export interface ChartTypeOptions {
713
+ area?: {
714
+ stacking?: {
715
+ /** Enable stacked area rendering. When false, area series fill independently from zero. @defaultValue true */
716
+ active?: boolean;
717
+ /** How missing series values are resolved across the shared area x-domain. @defaultValue 'linear' */
718
+ interpolation?: ChartAreaStackingInterpolation;
719
+ };
720
+ };
721
+ }
722
+
723
+ export interface ChartSeriesData {
724
+ id?: string | number;
725
+ title?: string;
726
+ points?: ChartPointData[];
727
+ style?: ChartSeriesStyle;
728
+ }
729
+
730
+ export interface ChartData {
731
+ id: string | number;
732
+ type?: ChartType;
733
+ title?: string;
734
+ lane?: string | number;
735
+ visibility?: VisibilityState | ((chart: Chart) => VisibilityState);
736
+ series?: ChartSeriesData[];
737
+ scale?: ChartScaleOptions;
738
+ typeOptions?: ChartTypeOptions;
739
+ area?: { up?: AreaValue; down?: AreaValue };
740
+ style?: ChartStyle;
741
+ }
742
+
743
+ export interface LaneLayout {
744
+ height?: number;
745
+ /**
746
+ * Relative share of the remaining automatic lane height for lanes without a fixed `height`.
747
+ * Hidden lanes are excluded from the distribution.
748
+ *
749
+ * @defaultValue 1
750
+ */
751
+ heightWeight?: number;
752
+ header?: {
753
+ visible?: boolean;
754
+ height?: number;
755
+ padding?: { left?: number; right?: number };
756
+ };
757
+ }
758
+
759
+ /** Decorative lane chrome styles. Only explicitly provided values are written inline. */
760
+ export interface LaneStyle {
761
+ header?: {
762
+ backgroundColor?: string;
763
+ };
764
+ body?: {
765
+ backgroundColor?: string;
766
+ borderColor?: string;
767
+ borderWidth?: number;
768
+ borderRadius?: number;
769
+ };
770
+ title?: {
771
+ color?: string;
772
+ font?: string;
773
+ textAlign?: 'left' | 'center' | 'right';
774
+ };
775
+ }
776
+
777
+ export interface DistanceToBaselineResponsiveCardLayoutOptions {
778
+ /** Fraction of the available content height used to calculate the baseline distance. */
779
+ ratio?: number;
780
+ /** Minimum responsive baseline distance in px. */
781
+ min?: number;
782
+ /** Maximum responsive baseline distance in px. */
783
+ max?: number;
784
+ }
785
+
786
+ export interface DistanceToBaselineResponsiveOptions {
787
+ /** Enable responsive baseline distance calculations. @defaultValue true */
788
+ active?: boolean;
789
+ /**
790
+ * Restrict responsive baseline calculations to lanes only.
791
+ * When true, timelines without active lanes use the static `distanceToBaseline.value`.
792
+ *
793
+ * @defaultValue true
794
+ */
795
+ lanesOnly?: boolean;
796
+ /**
797
+ * Responsive baseline settings keyed by the resolved default card layout name.
798
+ *
799
+ * @defaultValue `{ portrait: { ratio: 0.6, min: 310, max: 410 }, landscape: { ratio: 0.6, min: 100, max: 160 } }`
800
+ */
801
+ byCardLayout?: Partial<Record<CardLayoutName, DistanceToBaselineResponsiveCardLayoutOptions>>;
802
+ }
803
+
804
+ export interface DistanceToBaselineOptions {
805
+ /**
806
+ * Distance of cards from their baseline in px:
807
+ * the shared timeline axis without lanes, or the lane bottom with lanes.
808
+ *
809
+ * @defaultValue 350
810
+ */
811
+ value?: number;
812
+ /** Responsive baseline distance settings. */
813
+ responsive?: DistanceToBaselineResponsiveOptions;
814
+ }
815
+
816
+ export interface LaneArticleOverrides {
817
+ defaultData?: Partial<ArticleData>;
818
+ /** Lane-local density grouping/filtering input. */
819
+ density?: ArticleOptions['density'];
820
+ defaultCardLayout?: CardLayoutName;
821
+ defaultStyle?: ArticleStyle;
822
+ defaultHoverStyle?: ArticleStyle;
823
+ defaultActiveStyle?: ArticleStyle;
824
+ layoutStyles?: ArticleOptions['layoutStyles'];
825
+ /** Distance of cards from this lane's baseline in px, plus responsive settings for this lane. */
826
+ distanceToBaseline?: DistanceToBaselineOptions;
827
+ /**
828
+ * @deprecated Deprecated since 1.3.4 — Use `distanceToBaseline.value` instead.
829
+ */
830
+ distanceToMainLine?: number;
831
+ autoStacking?: {
832
+ /** Enable or disable automatic row assignment for this lane. */
833
+ active?: boolean;
834
+ rowSpacing?: number;
835
+ fitToHeight?: boolean;
836
+ topGap?: number | ((timeline: Timeline) => number);
837
+ };
838
+ periodLine?: {
839
+ /** Additional gap from the active baseline in px. */
840
+ baselineGap?: number;
841
+ /** Gap between stacked period lines in px. */
842
+ spacing?: number;
843
+ /** Line thickness in px. */
844
+ thickness?: number;
845
+ };
846
+ }
847
+
848
+ export interface LaneData {
849
+ id: string | number;
850
+ title?: string;
851
+ visibility?: VisibilityState | ((lane: Lane) => VisibilityState);
852
+ /** Additional CSS class for the lane root beside `histropedia-lane` and `histropedia-lane-index-N`. */
853
+ className?: string;
854
+ /** Additional CSS class for the lane header beside `histropedia-lane-header`. */
855
+ headerClassName?: string;
856
+ /** Additional CSS class for the lane body beside `histropedia-lane-body`. */
857
+ bodyClassName?: string;
858
+ /** Additional CSS class for the lane title beside `histropedia-lane-title`. */
859
+ titleClassName?: string;
860
+ layout?: LaneLayout;
861
+ style?: LaneStyle;
862
+ article?: LaneArticleOverrides;
863
+ }
864
+
865
+ // ------------------------------------------------------------------------------------
866
+ // Timeline options
867
+ // ------------------------------------------------------------------------------------
868
+
869
+ export interface TimelineZoomOptions {
870
+ /** @defaultValue 34 */ initial?: number;
871
+ /**
872
+ * Finest permitted zoom. The default `0` preserves day as the finest unit;
873
+ * set a negative value to opt in to hour, minute, second, and millisecond zoom.
874
+ * With all other zoom defaults unchanged, approximately `-82` makes one
875
+ * millisecond about 200 px wide.
876
+ * @defaultValue 0
877
+ */
878
+ minimum?: number;
879
+ /** @defaultValue 123 */ maximum?: number;
880
+ /** Zoom step multiplier per wheel unit. @defaultValue 0.2 */
881
+ wheelStep?: number;
882
+ /** Overall wheel speed. @defaultValue 3 */
883
+ wheelSpeed?: number;
884
+ /** Allow Ctrl+wheel zoom. @defaultValue false */
885
+ allowCtrlWheel?: boolean;
886
+ /** 'auto' picks smooth vs discrete from device; 'discrete' forces stepped. @defaultValue 'auto' */
887
+ wheelMode?: 'auto' | 'discrete';
888
+ /** Animate discrete/notched wheel zoom steps. */
889
+ discreteWheelAnimation?: {
890
+ /** Enable animated wheel zoom. @defaultValue true */
891
+ active?: boolean;
892
+ /** Animation duration in ms. @defaultValue 250 */
893
+ duration?: number;
894
+ };
895
+ /** Gain for proportional (trackpad) mode. @defaultValue 2 */
896
+ proportionalGain?: number;
897
+ /** Exponent for proportional (trackpad) mode. @defaultValue 1.1 */
898
+ proportionalExponent?: number;
899
+ /** Zoom ratio between unit-size levels. @defaultValue 0.8 */
900
+ ratio?: number;
901
+ unitSize?: {
902
+ /** Size of a day at zoom=0 (and density region size). @defaultValue 200 */
903
+ initial?: number;
904
+ /** Spacing threshold to show minor labels and adaptive sub-day subdivisions. @defaultValue 48 */
905
+ showMinorLabels?: number;
906
+ /** Minimum unit size in px. @defaultValue 8 */
907
+ minimum?: number;
908
+ /** Minimum spacing between sub-day major markers in px. @defaultValue 120 */
909
+ minimumMajor?: number;
910
+ };
911
+ }
912
+
913
+ export interface PanMomentumOptions {
914
+ /** Continue timeline panning after a drag release. @defaultValue true */
915
+ active?: boolean;
916
+ /** Pointer movement sample window in ms used to estimate release velocity. @defaultValue 100 */
917
+ sampleWindow?: number;
918
+ /** Minimum release velocity in px/ms before momentum starts. @defaultValue 0.01 */
919
+ minVelocity?: number;
920
+ /** Velocity in px/ms at which momentum stops. @defaultValue 0.005 */
921
+ stopVelocity?: number;
922
+ /** Exponential friction coefficient per ms. Lower values travel farther. @defaultValue 0.005 */
923
+ friction?: number;
924
+ /** Maximum momentum duration in ms. @defaultValue 5600 */
925
+ maxDuration?: number;
926
+ }
927
+
928
+ export interface TimelinePanOptions {
929
+ /** Momentum behaviour after a timeline drag release. */
930
+ momentum?: PanMomentumOptions;
931
+ }
932
+
933
+ export interface TimelineDebugOverlayOptions {
934
+ /** Draw vertical guides at article-density region boundaries. @defaultValue false */
935
+ densityRegions?: boolean;
936
+ }
937
+
938
+ export interface ArticleOptions {
939
+ /** Density filter for visible articles. @defaultValue DENSITY_ALL */
940
+ density?: Density;
941
+ /** Rendering alignment shared by built-in cards and their connectors. */
942
+ rendering?: {
943
+ /**
944
+ * Snap to physical device pixels, whole CSS pixels, or retain subpixel coordinates.
945
+ * `true` is equivalent to `'device'`; `false` is equivalent to `'none'`.
946
+ * @defaultValue 'device'
947
+ */
948
+ pixelSnap?: ArticlePixelSnapMode;
949
+ };
950
+ /** Allow dragging event cards. @defaultValue true */
951
+ draggable?: boolean;
952
+ /**
953
+ * Distance of cards from their baseline in px, plus responsive settings.
954
+ *
955
+ * Responsive calculations use the resolved default card layout for the timeline or lane.
956
+ */
957
+ distanceToBaseline?: DistanceToBaselineOptions;
958
+ /**
959
+ * @deprecated Deprecated since 1.3.4 — Use `distanceToBaseline.value` instead.
960
+ */
961
+ distanceToMainLine?: number;
962
+ /** Collect ongoing events on main line. @defaultValue false */
963
+ collectOngoing?: boolean;
964
+ /** Auto stacking options. */
965
+ autoStacking?: {
966
+ /** @defaultValue true */
967
+ active?: boolean;
968
+ /** Distance between rows in px. @defaultValue 50 */
969
+ rowSpacing?: number;
970
+ /** Range considered for stacking. @defaultValue RANGE_ALL */
971
+ range?: AutoStackRange;
972
+ /** Fit to available height by adjusting rowSpacing. @defaultValue true */
973
+ fitToHeight?: boolean;
974
+ /** Top gap in px or a function returning px. @defaultValue 10 */
975
+ topGap?: number | ((timeline: Timeline) => number);
976
+ };
977
+ periodLine?: {
978
+ /** Anchor period lines to the shared axis or each lane bottom. @defaultValue 'lane' */
979
+ position?: 'axis' | 'lane';
980
+ /**
981
+ * Default value merged into `article.defaultData.hidePeriodLine`.
982
+ *
983
+ * @deprecated Deprecated since 1.3.0 — Use `defaultData.hidePeriodLine` instead.
984
+ */
985
+ defaultHide?: boolean;
986
+ /** Additional gap from the active baseline in px. @defaultValue 0 */
987
+ baselineGap?: number;
988
+ /** Gap between stacked period lines in px. @defaultValue 4 */
989
+ spacing?: number;
990
+ /** Line thickness in px. @defaultValue 10 */
991
+ thickness?: number;
992
+ stacking?: {
993
+ /** Comparator used when assigning period-line stacks. */
994
+ sorter?: ArticleSorter;
995
+ /** Reverse stacking order. @defaultValue false */
996
+ reverseOrder?: boolean;
997
+ };
998
+ };
999
+ animation?: {
1000
+ fade?: { active?: boolean; duration?: number; easing?: EasingName };
1001
+ move?: { active?: boolean; duration?: number; easing?: EasingName };
1002
+ };
1003
+ star?: { /** Show star icon. @defaultValue true */ visible?: boolean };
1004
+ /** Default data merged into every loaded article. */
1005
+ defaultData?: Partial<ArticleData>;
1006
+ /** Fallback card layout name used when no breakpoint matches. @defaultValue 'portrait' */
1007
+ defaultCardLayout?: CardLayoutName;
1008
+ /** Ordered max-height overrides evaluated before falling back to `defaultCardLayout`. */
1009
+ cardLayoutBreakpoints?: ArticleCardLayoutBreakpoint[];
1010
+ /** Explicit timeline-wide styles applied after the selected layout's registered defaults. */
1011
+ defaultStyle?: ArticleStyle;
1012
+ defaultHoverStyle?: ArticleStyle;
1013
+ defaultActiveStyle?: ArticleStyle;
1014
+ /** Explicit per-layout overrides applied after the generic style at the same scope. */
1015
+ layoutStyles?: Record<
1016
+ string,
1017
+ Partial<{
1018
+ style: ArticleStyle;
1019
+ hoverStyle: ArticleStyle;
1020
+ activeStyle: ArticleStyle;
1021
+ }>
1022
+ >;
1023
+ }
1024
+
1025
+ /** Pixels from the main line, the `'edge'` keyword, or `{ value: 'edge', padding?: number }` for edge-stretching with padding. */
1026
+ export type AreaValue = number | 'edge' | { value: 'edge'; padding?: number };
1027
+
1028
+ export interface TimeBandOptions {
1029
+ /** Enable drawing of bands. @defaultValue true */
1030
+ visible?: boolean;
1031
+ /** Reserve extra space near main line when bands are present. @defaultValue true */
1032
+ reserveSpace?: boolean;
1033
+ /** Pixels to reserve when reserveSpace is true. @defaultValue 20 */
1034
+ reserveSpacePixels?: number;
1035
+ /** 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'} */
1036
+ area?: { up?: AreaValue; down?: AreaValue };
1037
+ /** Global default style merged into each band. */
1038
+ defaultStyle?: TimeBandStyle;
1039
+ /** Bands to load initially. */
1040
+ data?: TimeBandData[];
1041
+ }
1042
+
1043
+ export interface ChartOptions {
1044
+ /** Enable drawing of loaded charts. @defaultValue true */
1045
+ visible?: boolean;
1046
+ /** Main-view chart area relative to the shared axis. Lane-scoped charts use lane body geometry. */
1047
+ area?: { up?: AreaValue; down?: AreaValue };
1048
+ /**
1049
+ * Colors assigned cyclically to series without an explicit or shared style color.
1050
+ * @defaultValue ['#2563eb', '#dc2626', '#16a34a', '#9333ea', '#ea580c', '#0891b2', '#be123c', '#4f46e5']
1051
+ */
1052
+ seriesColors?: string[];
1053
+ /**
1054
+ * Global chart grid-line defaults merged into each chart style.
1055
+ * @deprecated Use `defaultStyle.gridLine` instead.
1056
+ */
1057
+ gridLine?: ChartGridLineOptions;
1058
+ /** Default data merged into every loaded chart. */
1059
+ defaultData?: Partial<ChartData>;
1060
+ /** Global default chart style merged into each chart. */
1061
+ defaultStyle?: ChartStyle;
1062
+ /** Charts to load initially. */
1063
+ data?: ChartData[];
1064
+ }
1065
+
1066
+ export interface LaneOptions {
1067
+ /**
1068
+ * Enable lane DOM chrome. Article layout still remains lane-aware when lane data is present.
1069
+ * @defaultValue true
1070
+ */
1071
+ visible?: boolean;
1072
+ /** Default lane id used when an article does not specify `lane`. @defaultValue 'default' */
1073
+ defaultId?: string | number;
1074
+ /** Gap between rendered lanes in px. @defaultValue 0 */
1075
+ gap?: number;
1076
+ /** Space reserved above the topmost lane in px. @defaultValue 0 */
1077
+ topGap?: number;
1078
+ /** Space between the painted edge of the shared main line and the lane area in px. @defaultValue 0 */
1079
+ axisGap?: number;
1080
+ /** Global default lane layout merged into each lane. */
1081
+ defaultLayout?: LaneLayout;
1082
+ /** Global default lane style merged into each lane. Only explicitly provided values are written inline. */
1083
+ defaultStyle?: LaneStyle;
1084
+ /** CSS class added to each lane root element beside `histropedia-lane` and `histropedia-lane-index-N`. */
1085
+ defaultClassName?: string;
1086
+ /** CSS class added to each lane header element beside `histropedia-lane-header`. */
1087
+ defaultHeaderClassName?: string;
1088
+ /** CSS class added to each lane body element beside `histropedia-lane-body`. */
1089
+ defaultBodyClassName?: string;
1090
+ /** CSS class added to each lane title element beside `histropedia-lane-title`. */
1091
+ defaultTitleClassName?: string;
1092
+ /** Lanes to load initially. */
1093
+ data?: LaneData[];
1094
+ }
1095
+
1096
+ /**
1097
+ * Image loading and URL sanitization options.
1098
+ *
1099
+ * By default, the built-in sanitizer allows http, https, data:image/* and blob:.
1100
+ * You can restrict schemes and/or remote origins, or provide a custom sanitizer.
1101
+ */
1102
+ export type ImageDecodeMode = 'auto' | 'prefer-bitmap' | 'bitmap' | 'prefer-img' | 'img';
1103
+
1104
+ export interface ImageSanitizerOptions {
1105
+ /**
1106
+ * Allowed URL schemes for images.
1107
+ * @defaultValue ['http', 'https', 'data', 'blob']
1108
+ */
1109
+ allowedSchemes?: string[];
1110
+
1111
+ /**
1112
+ * Allowed origins/hosts for remote images. Supports full origins, bare hostnames,
1113
+ * leading-dot, and wildcard-prefixed (`*.example.com`) entries.
1114
+ */
1115
+ allowedOrigins?: string[];
1116
+ }
1117
+
1118
+ export interface ImageOptions {
1119
+ /**
1120
+ * Built-in URL sanitization controls.
1121
+ */
1122
+ sanitizer?: ImageSanitizerOptions;
1123
+
1124
+ /**
1125
+ * Optional override for image URL sanitization. Receives the raw URL and the
1126
+ * current sanitizer configuration from `image.sanitizer`. Return a trusted URL
1127
+ * string to allow the load or null to block it.
1128
+ */
1129
+ customSanitizer?: (url: string, options: ImageSanitizerOptions) => string | null;
1130
+
1131
+ /**
1132
+ * Maximum number of concurrent image fetch/decode operations. Values < 1 are clamped to 1.
1133
+ * @defaultValue 6
1134
+ */
1135
+ maxConcurrent?: number;
1136
+
1137
+ /**
1138
+ * Maximum decoded raster memory (in bytes) retained by the image cache.
1139
+ * Set to `Infinity` to disable eviction, or reduce to bound memory usage.
1140
+ * @defaultValue 64 * 1024 * 1024 (64 MiB)
1141
+ */
1142
+ maxCacheBytes?: number;
1143
+
1144
+ /**
1145
+ * Force cross-origin requests to opt-in via CORS (sets `crossOrigin = 'anonymous'`).
1146
+ * When true, images served without CORS headers will fail to load but canvas readbacks
1147
+ * remain untainted.
1148
+ * @defaultValue false
1149
+ */
1150
+ requireCORS?: boolean;
1151
+
1152
+ /**
1153
+ * Controls how raster data is produced once an image loads.
1154
+ *
1155
+ * - 'auto' (default) prefers ImageBitmap when supported, falling back to `<img>`.
1156
+ * - 'prefer-bitmap' aggressively tries ImageBitmap but falls back to `<img>` if needed.
1157
+ * - 'bitmap' requires ImageBitmap support and treats failures as errors.
1158
+ * - 'prefer-img' keeps `<img>` unless decoding fails, then falls back to ImageBitmap.
1159
+ * - 'img' forces `<img>` and never calls `createImageBitmap`.
1160
+ */
1161
+ decodeMode?: ImageDecodeMode;
1162
+ }
1163
+
1164
+ export interface TimelineDateBoundsOptions {
1165
+ /** Earliest allowed visible viewport edge. @defaultValue null */
1166
+ minDate?: DateValue | null;
1167
+ /** Latest allowed visible viewport edge. @defaultValue null */
1168
+ maxDate?: DateValue | null;
1169
+ /**
1170
+ * Boundary behaviour during active user interaction.
1171
+ * - 'elastic': allow resisted temporary overflow and settle back when interaction ends.
1172
+ * - 'clamp': prevent overflow immediately.
1173
+ * @defaultValue 'elastic'
1174
+ */
1175
+ overflow?: 'elastic' | 'clamp';
1176
+ }
1177
+
1178
+ export interface TimelineOptions {
1179
+ /** Canvas width in px. @defaultValue 1000 */
1180
+ width?: number;
1181
+ /** Canvas height in px. @defaultValue 500 */
1182
+ height?: number;
1183
+ /** Canvas/backing-store configuration. */
1184
+ canvas?: {
1185
+ /** Device pixel ratio to use; 'auto' follows device. @defaultValue 'auto' */ dpr?:
1186
+ | 'auto'
1187
+ | number;
1188
+ /**
1189
+ * Behaviour for dpr:'auto'.
1190
+ * - 'stable' (default) rounds devicePixelRatio up to the nearest integer.
1191
+ * - 'native' uses the raw devicePixelRatio value.
1192
+ * @defaultValue 'stable'
1193
+ */
1194
+ dprMode?: 'stable' | 'native';
1195
+ /** Maximum DPR to apply for performance. @defaultValue 3 */ maxDpr?: number;
1196
+ };
1197
+ /** Per-timeline visual diagnostics. Independent from global debug logging. */
1198
+ debugOverlays?: TimelineDebugOverlayOptions;
1199
+ /** Vertical offset of the main line in px. @defaultValue 40 */
1200
+ verticalOffset?: number;
1201
+ /** Enable user interactions (wheel/drag/pinch). @defaultValue true */
1202
+ enableUserControl?: boolean;
1203
+ /** Show interactive cursors. @defaultValue true */
1204
+ enableCursor?: boolean;
1205
+ /** Zoom configuration. */
1206
+ zoom?: TimelineZoomOptions;
1207
+ /** Initial date/time (top-left when position not offset). @defaultValue {year:1990,month:1,day:1} */
1208
+ initialDate?: DateValue;
1209
+ /**
1210
+ * Optional visible viewport date bounds. Bounds constrain viewport edges, not only the centre date.
1211
+ * With both dates set, the settled viewport stays within `[minDate, maxDate]`.
1212
+ */
1213
+ bounds?: TimelineDateBoundsOptions;
1214
+ /** Shift BCE input dates by +1 so 1BCE = year 0 internally. @defaultValue false */
1215
+ shiftBceDates?: boolean;
1216
+ /** Hide the built-in Histropedia branding/footer links. Defaults to false when omitted. */
1217
+ disableBranding?: boolean;
1218
+ /**
1219
+ * Drag activation vicinity near the main line.
1220
+ * - boolean: enable/disable dragging anywhere on the timeline
1221
+ * - function: (event, relativePosition) => boolean; return true to allow dragging
1222
+ * where relativePosition is { left: number; top: number }
1223
+ * - object: { up?: number; down?: number } in pixels from the main line;
1224
+ * an omitted side extends to the corresponding canvas edge
1225
+ * @defaultValue true
1226
+ */
1227
+ draggingVicinity?:
1228
+ | boolean
1229
+ | ((event: PointerEvent, relativePosition: { left: number; top: number }) => boolean)
1230
+ | { up?: number; down?: number };
1231
+ /** Timeline panning configuration. */
1232
+ pan?: TimelinePanOptions;
1233
+ /** Canvas/UI styling. */
1234
+ style?: TimelineStyleOptions;
1235
+ /** Time band configuration. */
1236
+ timeBand?: TimeBandOptions;
1237
+ /** Chart layer configuration. */
1238
+ chart?: ChartOptions;
1239
+ /** Lane configuration. */
1240
+ lane?: LaneOptions;
1241
+ /** Article configuration. */
1242
+ article?: ArticleOptions;
1243
+ /** Configure image sanitization, caching, and loader behaviour. */
1244
+ image?: ImageOptions;
1245
+ /** Register event handlers via object literal: { 'article-click': fn, ... } */
1246
+ on?: Partial<EventHandlerMap>;
1247
+ /**
1248
+ * Called after every draw pass.
1249
+ *
1250
+ * @deprecated Deprecated since 1.3.0 — Use `on['timeline-render-end']` instead.
1251
+ */
1252
+ onRedraw?: (ctx: RedrawContext) => void;
1253
+ /**
1254
+ * Article click handler configured via this option.
1255
+ *
1256
+ * @deprecated Deprecated since 1.3.0 — Use `on['article-click']` for click events or `on['article-select']` for selection by any means.
1257
+ */
1258
+ onArticleClick?: (article: Article, event?: Event) => void;
1259
+ /**
1260
+ * Article double click handler configured via this option.
1261
+ *
1262
+ * @deprecated Deprecated since 1.3.0 — Use `on['article-dblclick']` instead.
1263
+ */
1264
+ onArticleDoubleClick?: (article: Article, event?: Event) => void;
1265
+ /**
1266
+ * Timeline save handler configured via this option.
1267
+ *
1268
+ * @deprecated Deprecated since 1.3.0 — Use `on['timeline-state-change']` instead.
1269
+ */
1270
+ onSave?: (state: string) => void;
1271
+ }
1272
+
1273
+ // ------------------------------------------------------------------------------------
1274
+ // Events
1275
+ // ------------------------------------------------------------------------------------
1276
+
1277
+ /** Token produced by the timescale manager for marker drawing and viewport drag payloads. */
1278
+ export interface TimelineToken {
1279
+ unit: ZoomLevel;
1280
+ value: DmyInstance;
1281
+ length: number;
1282
+ label?: string;
1283
+ type?: MarkerType;
1284
+ drawX?: number;
1285
+ }
1286
+
1287
+ export interface ZoomPayload {
1288
+ zoom: number;
1289
+ zoomDelta: number;
1290
+ }
1291
+ export interface WheelPayload {
1292
+ centre: { x: number; y: number };
1293
+ zoom: number;
1294
+ zoomDelta: number;
1295
+ }
1296
+ export interface PinchPayload {
1297
+ centre: { x: number; y: number } | null;
1298
+ scale: number;
1299
+ zoom: number;
1300
+ distance: number;
1301
+ zoomDelta: number;
1302
+ dx?: number;
1303
+ totalZoomDelta: number;
1304
+ pointers: PointerEvent[];
1305
+ }
1306
+ export interface ViewportDragPayload {
1307
+ startToken: TimelineToken;
1308
+ offsetX: number;
1309
+ dx?: number;
1310
+ totalDx?: number;
1311
+ }
1312
+ export interface TimelineClickPayload {
1313
+ viewport: { x: number; y: number };
1314
+ }
1315
+ export interface DragDelta {
1316
+ dx: number;
1317
+ dy: number;
1318
+ totalDx: number;
1319
+ totalDy: number;
1320
+ }
1321
+
1322
+ /** Token cache exposed to redraw hooks for advanced positioning work. */
1323
+ export interface RedrawCycleContext {
1324
+ tokens: {
1325
+ start: DmyInstance;
1326
+ end: DmyInstance;
1327
+ [tokenKey: string]: DmyInstance | number;
1328
+ };
1329
+ }
1330
+
1331
+ /** Data delivered to redraw lifecycle handlers. */
1332
+ export interface RedrawContext {
1333
+ canvasContext: CanvasRenderingContext2D;
1334
+ /** Present for timeline-render-end; omitted for timeline-render-start. */
1335
+ drawCycleContext?: RedrawCycleContext;
1336
+ top: number;
1337
+ }
1338
+
1339
+ export interface EventPayloadMap {
1340
+ // zoom
1341
+ zoom: [ZoomPayload];
1342
+ 'zoom-wheel': [WheelPayload, WheelEvent];
1343
+ 'zoom-pinch-start': [PinchPayload, PointerEvent];
1344
+ 'zoom-pinch': [PinchPayload, PointerEvent];
1345
+ 'zoom-pinch-end': [PinchPayload, PointerEvent];
1346
+ // viewport
1347
+ 'viewport-drag-start': [ViewportDragPayload, PointerEvent];
1348
+ 'viewport-drag': [ViewportDragPayload, PointerEvent];
1349
+ 'viewport-drag-end': [ViewportDragPayload, PointerEvent];
1350
+ // articles
1351
+ 'article-pointerenter': [Article, PointerEvent];
1352
+ 'article-pointermove': [Article, PointerEvent];
1353
+ 'article-pointerleave': [Article, PointerEvent];
1354
+ 'article-click': [Article, PointerEvent];
1355
+ 'article-dblclick': [Article, PointerEvent];
1356
+ 'article-select': [Article];
1357
+ 'article-drag-start': [Article, PointerEvent];
1358
+ 'article-drag': [Article, DragDelta, PointerEvent];
1359
+ 'article-drag-end': [Article, PointerEvent];
1360
+ // timeline
1361
+ 'timeline-render-start': [RedrawContext];
1362
+ 'timeline-render-end': [RedrawContext];
1363
+ 'timeline-click': [TimelineClickPayload, PointerEvent];
1364
+ 'timeline-dblclick': [TimelineClickPayload, PointerEvent];
1365
+ 'timeline-state-change': [string];
1366
+ }
1367
+
1368
+ export type TimelineEvent = keyof EventPayloadMap;
1369
+ export type EventHandlerMap = { [K in TimelineEvent]: (...args: EventPayloadMap[K]) => void };
1370
+
1371
+ /** Backward-compatible alias for the article instance passed as `this` to card layout hooks. */
1372
+ export type CardLayoutArticleContext = Article;
1373
+
1374
+ /** Full card layout definition stored by the runtime registry. */
1375
+ export interface RegisteredCardLayout {
1376
+ name: string;
1377
+ draw(this: Article, ctx: CanvasRenderingContext2D): void;
1378
+ getWidth?(this: Article): number;
1379
+ getHeight?(this: Article): number;
1380
+ getIconBox?(this: Article): CardLayoutIconBox | false;
1381
+ defaultStyle?: ArticleStyle;
1382
+ defaultHoverStyle?: ArticleStyle;
1383
+ defaultActiveStyle?: ArticleStyle;
1384
+ }
1385
+
1386
+ /** Alias definition that points one layout name at another registered layout. */
1387
+ export interface CardLayoutAliasDefinition {
1388
+ name: string;
1389
+ aliasOf: CardLayoutName;
1390
+ }
1391
+
1392
+ /** Definition accepted by `Timeline.registerCardLayout(...)`. */
1393
+ export type CardLayoutDefinition = RegisteredCardLayout | CardLayoutAliasDefinition;
1394
+
1395
+ // ------------------------------------------------------------------------------------
1396
+ // Classes
1397
+ // ------------------------------------------------------------------------------------
1398
+
1399
+ export interface AnimOptions {
1400
+ /** Duration in ms. @defaultValue 2000 */ duration?: number;
1401
+ /** Easing name. @defaultValue 'swing' */ easing?: EasingName;
1402
+ /** Callback when animation completes. */ complete?: () => void;
1403
+ /** Additional horizontal shift, used by pinch. */ offsetX?: number;
1404
+ }
1405
+
1406
+ export type FitDateRangePadding = number | { left?: number; right?: number };
1407
+
1408
+ export interface FitDateRangeAnimationOptions {
1409
+ /** Enable simultaneous animated zooming and panning. @defaultValue false */
1410
+ active?: boolean;
1411
+ /** Duration in ms. Inherits the viewport animation default when omitted. */
1412
+ duration?: number;
1413
+ /** Easing name. Inherits the viewport animation default when omitted. */
1414
+ easing?: EasingName;
1415
+ }
1416
+
1417
+ export interface FitDateRangeOptions {
1418
+ /** Edge padding in px, or per-side padding for asymmetric fits. @defaultValue 0 */
1419
+ padding?: FitDateRangePadding;
1420
+ /** Optional animation settings for the fit. */
1421
+ animation?: FitDateRangeAnimationOptions;
1422
+ }
1423
+
1424
+ export interface SetStartDateOptions {
1425
+ /** Left padding in px for the supplied start date. This only affects the left side. @defaultValue 0 */
1426
+ padding?: number;
1427
+ /** Optional animation settings matching `fitDateRange(..., { animation })`. */
1428
+ animation?: FitDateRangeAnimationOptions;
1429
+ }
1430
+
1431
+ export interface FitArticleRangeOptions {
1432
+ /** Edge padding in px, or per-side padding for asymmetric fits. @defaultValue 0 */
1433
+ padding?: FitDateRangePadding;
1434
+ /** Optional animation settings for the fit. */
1435
+ animation?: FitDateRangeAnimationOptions;
1436
+ }
1437
+
1438
+ export interface FitArticlesOptions {
1439
+ /** Space in px between the outermost fitted article cards and the viewport edge. @defaultValue 40 */
1440
+ padding?: FitDateRangePadding;
1441
+ /** Optional article predicate. Defaults to fitting articles that are not `hiddenByFilter`. */
1442
+ articleFilter?: (article: Article) => boolean;
1443
+ /** Optional animation settings for the fit. */
1444
+ animation?: FitDateRangeAnimationOptions;
1445
+ }
1446
+
1447
+ export class Timeline {
1448
+ /**
1449
+ * Create a timeline bound to an existing canvas element or a container element (a canvas will be created).
1450
+ * @example
1451
+ * const tl = new Timeline(containerEl, { initialDate: { year: 1066, month: 10, day: 14 } });
1452
+ */
1453
+ constructor(container: HTMLCanvasElement | HTMLElement, options?: TimelineOptions);
1454
+
1455
+ /** Root canvas used for rendering. */
1456
+ readonly canvas: HTMLCanvasElement;
1457
+ /** 2D drawing context bound to `canvas`. */
1458
+ readonly canvasContext: CanvasRenderingContext2D;
1459
+ /** Current merged runtime options. */
1460
+ options: TimelineOptions;
1461
+ /** User-visible title used by serialized state helpers. */
1462
+ title: string;
1463
+ /** Current y-coordinate of the timeline baseline in canvas pixels. */
1464
+ top: number;
1465
+ readonly articles: Article[];
1466
+ readonly lanes: Lane[];
1467
+ readonly timeBands: TimeBand[];
1468
+ readonly charts: Chart[];
1469
+
1470
+ /** Register a reusable card layout globally for all timelines. */
1471
+ static registerCardLayout(definition: CardLayoutDefinition): void;
1472
+ /** Resolve a previously registered card layout by name. */
1473
+ static getCardLayout(name: CardLayoutName): RegisteredCardLayout | null;
1474
+
1475
+ // Data loading
1476
+ /** Load articles and redraw. */
1477
+ load(articles: ArticleData[]): void;
1478
+ /** Load lanes and redraw. */
1479
+ loadLanes(lanes: LaneData[]): void;
1480
+ /** Load time bands and redraw. */
1481
+ loadTimeBands(bands: TimeBandData[]): void;
1482
+ /** Load new charts and redraw; duplicate ids are skipped with a warning. */
1483
+ loadCharts(charts: ChartData[]): void;
1484
+ /** Remove a chart by id and redraw when found. */
1485
+ removeChart(id: string | number): boolean;
1486
+ /** Remove all loaded charts and redraw. */
1487
+ clearCharts(): void;
1488
+ /** Stamp a lane id onto a batch of charts, load them, and redraw. */
1489
+ loadLaneCharts(laneId: string | number, charts: ChartData[]): void;
1490
+ /** Stamp a lane id onto a batch of articles, load them, and redraw. */
1491
+ loadLaneArticles(laneId: string | number, articles: ArticleData[]): void;
1492
+
1493
+ // Query
1494
+ getArticleById(id: string | number): Article | undefined;
1495
+ getActiveArticle(): Article | null;
1496
+ getLaneById(id: string | number): Lane | undefined;
1497
+ getChartById(id: string | number): Chart | undefined;
1498
+ hasArticle(id: string | number): boolean;
1499
+
1500
+ // Selection and ordering
1501
+ bringFront(articleId: string | number): void;
1502
+ /** Selects and brings the article to the front; emits 'article-select'. */
1503
+ select(articleId: string | number): void;
1504
+ /** Reorder articles to match the array of ids (topmost last). */
1505
+ reorderArticles(ids: Array<string | number>): void;
1506
+ /** Move a lane to a public lane index where 0 is nearest the axis. */
1507
+ moveLaneToIndex(lane: Lane | string | number, index: number): Lane;
1508
+ /** Move lane so it ends up immediately before targetLane. */
1509
+ moveLaneBefore(lane: Lane | string | number, targetLane: Lane | string | number): Lane;
1510
+ /** Move lane so it ends up immediately after targetLane. */
1511
+ moveLaneAfter(lane: Lane | string | number, targetLane: Lane | string | number): Lane;
1512
+ /** Move lane one position farther from the axis. */
1513
+ moveLaneUp(lane: Lane | string | number): Lane;
1514
+ /** Move lane one position closer to the axis. */
1515
+ moveLaneDown(lane: Lane | string | number): Lane;
1516
+ /** Update the serializable title stored by `getStatusForSave()`. */
1517
+ setTitle(title: string): void;
1518
+
1519
+ // Navigation & view
1520
+ /** Set start date (left edge). Accepts Dmy-compatible objects or ISO-like date/time strings. */
1521
+ setStartDate(date: DateValue, options?: SetStartDateOptions): void;
1522
+ /** @deprecated Use `setStartDate(date, { padding })` instead. */
1523
+ setStartDate(date: DateValue, pixelOffset?: number): void;
1524
+ /** Set centre date using startDate + offset computation. */
1525
+ setCentreDate(date: DateValue, pixelOffset?: number): void;
1526
+ /** Current zoom value. */
1527
+ getZoom(): number;
1528
+ /** Set zoom (optionally pivoting around a pixel). */
1529
+ setZoom(zoom: number, pivotPixel?: number, offsetX?: number): void;
1530
+ /** Smooth pan by pixels (negative = right). */
1531
+ goToPixelAnim(pixels: number, options?: AnimOptions): void;
1532
+ /** @deprecated Use `setStartDate(dmy, { padding, animation: { active: true, ... } })` instead. */
1533
+ goToDateAnim(dmy: DateValue, options?: AnimOptions): void;
1534
+ /** 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. */
1535
+ fitDateRange(
1536
+ start: FitDateRangeInput,
1537
+ end: FitDateRangeInput,
1538
+ options?: FitDateRangeOptions,
1539
+ ): void;
1540
+ /** Fit a single article's full period into the viewport using `article.period.from` and `article.period.to`. */
1541
+ fitArticleRange(article: Article, options?: FitArticleRangeOptions): boolean;
1542
+ /** Fit currently unfiltered article cards into the viewport using each article's start date, card width, and connector offset. */
1543
+ fitArticles(options?: FitArticlesOptions): boolean;
1544
+ /** Animate to an absolute zoom value using the built-in tween. */
1545
+ zoomToAnim(newZoom: number, complete?: () => void): void;
1546
+
1547
+ // Drawing
1548
+ /** Request a redraw (coalesced if an animation is running). */
1549
+ requestRedraw(redrawFunction?: () => void): void;
1550
+ /** Full draw cycle. */
1551
+ redraw(): void;
1552
+ /** Reposition-only draw (for zoom/start-date changes). */
1553
+ repositionRedraw(): void;
1554
+ /** Default draw including reposition and stacking. */
1555
+ defaultRedraw(): void;
1556
+ /** Toggle auto fit-to-height for article stacking and return the new enabled state. */
1557
+ toggleAutoFit(): boolean;
1558
+ /** Recompute row spacing so visible rows fit in the available height. */
1559
+ fitToHeight(withRedraw?: boolean): number | undefined;
1560
+ /**
1561
+ * @deprecated Deprecated since 1.3.0 — Use `timeline.on('timeline-render-end', handler)` instead.
1562
+ */
1563
+ onRedraw(handler: (ctx: RedrawContext) => void): void;
1564
+
1565
+ // Interaction toggles
1566
+ enableZoomByWheel(): void;
1567
+ disableZoomByWheel(): void;
1568
+
1569
+ // Misc utilities
1570
+ /** Current canvas width in CSS pixels. */
1571
+ getWidth(): number;
1572
+ /** Current applied device pixel ratio for the canvas. */
1573
+ getCanvasDpr(): number;
1574
+ /** Get canvas-relative coordinates for a DOM event. */
1575
+ getRelativePosition(evt: MouseEvent | PointerEvent | TouchEvent): { left: number; top: number };
1576
+
1577
+ // Options
1578
+ /** Get or set options via dot-path or partial object. */
1579
+ setOption(path: string, value: unknown): void;
1580
+ setOption<T extends DeepPartial<TimelineOptions>>(patch: T): void;
1581
+ setOption(path: string): unknown;
1582
+ /** Resize the canvas. */
1583
+ setSize(width: number, height: number): void;
1584
+ /** Serialize the current timeline state to the legacy compact JSON string. */
1585
+ getStatusForSave(): string;
1586
+ /** Register a reusable card layout from an instance. Delegates to the static method. */
1587
+ registerCardLayout(definition: CardLayoutDefinition): void;
1588
+ /** Resolve a previously registered card layout by name. */
1589
+ getCardLayout(name: CardLayoutName): RegisteredCardLayout | null;
1590
+
1591
+ // Event bus
1592
+ on<T extends TimelineEvent>(event: T, handler: (...args: EventPayloadMap[T]) => void): void;
1593
+ on(event: string, handler: (...args: any[]) => void): void;
1594
+ off<T extends TimelineEvent>(event: T, handler: (...args: EventPayloadMap[T]) => void): void;
1595
+ off(event: string, handler: (...args: any[]) => void): void;
1596
+ /**
1597
+ * Trigger a built-in or custom event.
1598
+ * Custom event payloads are intentionally untyped because the runtime does not enforce a schema.
1599
+ */
1600
+ trigger<T extends TimelineEvent>(event: T, ...args: EventPayloadMap[T]): void;
1601
+ trigger(event: string, ...args: any[]): void;
1602
+ }
1603
+
1604
+ export class Article {
1605
+ /** Stable article identifier. */
1606
+ id: string | number;
1607
+ /** Owning timeline instance. */
1608
+ owner: Timeline;
1609
+ /** Latest runtime data backing this article. */
1610
+ data: ArticleData;
1611
+ title: string;
1612
+ subtitle: string;
1613
+ rank: number;
1614
+ laneId: string | number | null;
1615
+ cardLayout: CardLayoutName | null;
1616
+ period: ArticlePeriod;
1617
+ position: CanvasPosition;
1618
+ registeredPosition: CanvasPosition;
1619
+ offset: CanvasPosition;
1620
+ finalOffset: CanvasPosition;
1621
+ indicator: ArticleIndicator;
1622
+ row: number;
1623
+ image: CanvasImageSource | null;
1624
+ imageLoaded: boolean;
1625
+ style: ArticleStyle;
1626
+ hoverStyle: ArticleStyle;
1627
+ activeStyle: ArticleStyle;
1628
+ activeHoverStyle: ArticleStyle;
1629
+ opacity: number;
1630
+ isActive: boolean;
1631
+ isVisible: boolean;
1632
+ isDragging: boolean;
1633
+ isStarred: boolean;
1634
+ isMouseover: boolean;
1635
+ hiddenByFilter: boolean;
1636
+ isDataLoaded: boolean;
1637
+
1638
+ /** Update article data at runtime (dot-path or object). */
1639
+ setOption(path: string, value: unknown): void;
1640
+ setOption(patch: Partial<ArticleData>): void;
1641
+ setOption(path: string): unknown;
1642
+
1643
+ /** Update normal style (dot-path or object). */
1644
+ setStyle(path: string, value: unknown): void;
1645
+ setStyle(style: Partial<ArticleStyle>): void;
1646
+ setStyle(path: string): unknown;
1647
+
1648
+ /** Update hover style (dot-path or object). */
1649
+ setHoverStyle(path: string, value: unknown): void;
1650
+ setHoverStyle(style: Partial<ArticleStyle>): void;
1651
+ setHoverStyle(path: string): unknown;
1652
+
1653
+ /** Update active style (dot-path or object). */
1654
+ setActiveStyle(path: string, value: unknown): void;
1655
+ setActiveStyle(style: Partial<ArticleStyle>): void;
1656
+ setActiveStyle(path: string): unknown;
1657
+
1658
+ /** Move instantly to absolute canvas position. */
1659
+ moveTo(position: { left: number; top: number }): void;
1660
+ /** Move by delta from current position. */
1661
+ moveToOffset(offset: { left: number; top: number }): void;
1662
+ /** Override the card layout used by this article. */
1663
+ setCardLayout(cardLayout: CardLayoutName): void;
1664
+ /** Small per-layout memoization helper. */
1665
+ memo<T>(key: string, factory: () => T): T;
1666
+ /** Clear cached measurements/layout artifacts for this article. */
1667
+ invalidateCaches(): void;
1668
+ /** Resolve the rectangular star hover/click area, or `false` when none is present. */
1669
+ getIconBox(): CardLayoutIconBox | false;
1670
+
1671
+ /** Fully merged style for the current normal, hover, active, or active-hover state. */
1672
+ getCurrentStyle(): ArticleStyle;
1673
+
1674
+ /** Measured card width. */
1675
+ getWidth(): number;
1676
+ /** Measured card height. */
1677
+ getHeight(): number;
1678
+ }
1679
+
1680
+ export class TimeBand {
1681
+ id: string | number;
1682
+ owner: Timeline;
1683
+ data: TimeBandData;
1684
+ title: string;
1685
+ style: TimeBandStyle;
1686
+ period: TimeBandPeriod;
1687
+ visibility: VisibilityState;
1688
+
1689
+ /** Update band data at runtime (dot-path or object). */
1690
+ setOption(path: string, value: unknown): void;
1691
+ setOption(patch: Partial<TimeBandData>): void;
1692
+ setOption(path: string): unknown;
1693
+
1694
+ /** Update band style (dot-path or object). */
1695
+ setStyle(path: string, value: unknown): void;
1696
+ setStyle(style: Partial<TimeBandStyle>): void;
1697
+ setStyle(path: string): unknown;
1698
+ }
1699
+
1700
+ export class Chart {
1701
+ id: string | number;
1702
+ owner: Timeline;
1703
+ data: ChartData;
1704
+ type: ChartType;
1705
+ title: string;
1706
+ laneId: string | number | null;
1707
+ area: { up?: AreaValue; down?: AreaValue } | null;
1708
+ scale: ChartScaleOptions;
1709
+ typeOptions: ChartTypeOptions;
1710
+ style: ChartStyle;
1711
+ series: Array<{
1712
+ id: string | number;
1713
+ title: string;
1714
+ style: ChartSeriesStyle;
1715
+ points: Array<{ date: DmyInstance; value: number | null }>;
1716
+ }>;
1717
+ visibility: VisibilityState;
1718
+
1719
+ /** Add one series to this chart and redraw. */
1720
+ addSeries(series: ChartSeriesData): void;
1721
+
1722
+ /** Remove one series by id and redraw when found. */
1723
+ removeSeriesById(id: string | number): boolean;
1724
+
1725
+ /** Return the normalized runtime series with this id. */
1726
+ getSeriesById(id: string | number):
1727
+ | {
1728
+ id: string | number;
1729
+ title: string;
1730
+ style: ChartSeriesStyle;
1731
+ points: Array<{ date: DmyInstance; value: number | null }>;
1732
+ }
1733
+ | undefined;
1734
+
1735
+ /** Update chart data at runtime (dot-path or object). */
1736
+ setOption(path: string, value: unknown): void;
1737
+ setOption(patch: Partial<Omit<ChartData, 'id'>>): void;
1738
+ setOption(path: string): unknown;
1739
+
1740
+ /** Update chart style (dot-path or object). */
1741
+ setStyle(path: string, value: unknown): void;
1742
+ setStyle(style: Partial<ChartStyle>): void;
1743
+ setStyle(path: string): unknown;
1744
+
1745
+ /** Resolve the active y-domain for the chart. */
1746
+ getScaleDomain(drawCycleContext?: RedrawCycleContext): { min: number; max: number };
1747
+ }
1748
+
1749
+ export class Lane {
1750
+ id: string | number;
1751
+ owner: Timeline;
1752
+ data: LaneData;
1753
+ title: string;
1754
+ layout: LaneLayout;
1755
+ style: LaneStyle;
1756
+ order: number;
1757
+ isImplicit: boolean;
1758
+ visibility: VisibilityState;
1759
+
1760
+ /** Update lane data at runtime (dot-path or object). */
1761
+ setOption(path: string, value: unknown): void;
1762
+ setOption(patch: Partial<LaneData>): void;
1763
+ setOption(path: string): unknown;
1764
+
1765
+ /** Hide the lane by setting visibility to "hidden". */
1766
+ hide(): void;
1767
+
1768
+ /** Show the lane by setting visibility to "visible". */
1769
+ show(): void;
1770
+
1771
+ /** Move this lane to a public lane index where 0 is nearest the axis. */
1772
+ moveToIndex(index: number): Lane;
1773
+
1774
+ /** Move this lane so it ends up immediately before targetLane. */
1775
+ moveBefore(targetLane: Lane | string | number): Lane;
1776
+
1777
+ /** Move this lane so it ends up immediately after targetLane. */
1778
+ moveAfter(targetLane: Lane | string | number): Lane;
1779
+
1780
+ /** Move this lane one position farther from the axis. */
1781
+ moveUp(): Lane;
1782
+
1783
+ /** Move this lane one position closer to the axis. */
1784
+ moveDown(): Lane;
1785
+
1786
+ /** Update lane style (dot-path or object). */
1787
+ setStyle(path: string, value: unknown): void;
1788
+ setStyle(style: Partial<LaneStyle>): void;
1789
+ setStyle(path: string): unknown;
1790
+ }
1791
+
1792
+ /** Built-in sorter signature used by stacking and layout helpers. */
1793
+ export type ArticleSorter = (articleA: any, articleB: any) => number;
1794
+
1795
+ export const ARTICLE_DURATION_SORTER: ArticleSorter;
1796
+ export const ARTICLE_DURATION_YEARS_SORTER: ArticleSorter;
1797
+ export const ARTICLE_IMAGE_QUEUE_SORTER: ArticleSorter;
1798
+ export const ARTICLE_FROM_SORTER: ArticleSorter;
1799
+ export const ARTICLE_TO_SORTER: ArticleSorter;
1800
+ export const ARTICLE_TOP_SORTER: ArticleSorter;
1801
+ export const ARTICLE_ROW_SORTER: ArticleSorter;
1802
+ export const ARTICLE_RANK_SORTER: ArticleSorter;
1803
+ export const ARTICLE_EFFECTIVE_RANK_SORTER: ArticleSorter;
1804
+ export const ARTICLE_POSITION_SORTER: ArticleSorter;
1805
+
1806
+ // ------------------------------------------------------------------------------------
1807
+ // Default export for UMD / ESM default
1808
+ // ------------------------------------------------------------------------------------
1809
+
1810
+ export interface HistropediaApi {
1811
+ Timeline: typeof Timeline;
1812
+ Article: typeof Article;
1813
+ Chart: typeof Chart;
1814
+ Lane: typeof Lane;
1815
+ TimeBand: typeof TimeBand;
1816
+ Dmy: DmyConstructor;
1817
+ Logger: LoggerInstance;
1818
+ setDebug(enabled: boolean): void;
1819
+ isDebugEnabled(): boolean;
1820
+ enableDebug(): void;
1821
+ disableDebug(): void;
1822
+ ARTICLE_DURATION_SORTER: typeof ARTICLE_DURATION_SORTER;
1823
+ ARTICLE_DURATION_YEARS_SORTER: typeof ARTICLE_DURATION_YEARS_SORTER;
1824
+ ARTICLE_IMAGE_QUEUE_SORTER: typeof ARTICLE_IMAGE_QUEUE_SORTER;
1825
+ ARTICLE_FROM_SORTER: typeof ARTICLE_FROM_SORTER;
1826
+ ARTICLE_TO_SORTER: typeof ARTICLE_TO_SORTER;
1827
+ ARTICLE_TOP_SORTER: typeof ARTICLE_TOP_SORTER;
1828
+ ARTICLE_ROW_SORTER: typeof ARTICLE_ROW_SORTER;
1829
+ ARTICLE_RANK_SORTER: typeof ARTICLE_RANK_SORTER;
1830
+ ARTICLE_EFFECTIVE_RANK_SORTER: typeof ARTICLE_EFFECTIVE_RANK_SORTER;
1831
+ ARTICLE_POSITION_SORTER: typeof ARTICLE_POSITION_SORTER;
1832
+ MINOR_MARKER: typeof MINOR_MARKER;
1833
+ MAJOR_MARKER: typeof MAJOR_MARKER;
1834
+ ERA_MARKER: typeof ERA_MARKER;
1835
+ ZOOM_DAY: typeof ZOOM_DAY;
1836
+ ZOOM_HOUR: typeof ZOOM_HOUR;
1837
+ ZOOM_MINUTE: typeof ZOOM_MINUTE;
1838
+ ZOOM_SECOND: typeof ZOOM_SECOND;
1839
+ ZOOM_MILLISECOND: typeof ZOOM_MILLISECOND;
1840
+ ZOOM_MONTH: typeof ZOOM_MONTH;
1841
+ ZOOM_YEAR: typeof ZOOM_YEAR;
1842
+ ZOOM_DECADE: typeof ZOOM_DECADE;
1843
+ ZOOM_CENTURY: typeof ZOOM_CENTURY;
1844
+ ZOOM_MILLENNIUM: typeof ZOOM_MILLENNIUM;
1845
+ ZOOM_10_THOUSAND_YEARS: typeof ZOOM_10_THOUSAND_YEARS;
1846
+ ZOOM_100_THOUSAND_YEARS: typeof ZOOM_100_THOUSAND_YEARS;
1847
+ ZOOM_MILLION_YEARS: typeof ZOOM_MILLION_YEARS;
1848
+ ZOOM_10_MILLION_YEARS: typeof ZOOM_10_MILLION_YEARS;
1849
+ ZOOM_100_MILLION_YEARS: typeof ZOOM_100_MILLION_YEARS;
1850
+ ZOOM_BILLION_YEARS: typeof ZOOM_BILLION_YEARS;
1851
+ YEAR_PREFIXES: typeof YEAR_PREFIXES;
1852
+ DENSITY_SETTINGS: typeof DENSITY_SETTINGS;
1853
+ PRECISION_DAY: typeof PRECISION_DAY;
1854
+ PRECISION_HOUR: typeof PRECISION_HOUR;
1855
+ PRECISION_MINUTE: typeof PRECISION_MINUTE;
1856
+ PRECISION_SECOND: typeof PRECISION_SECOND;
1857
+ PRECISION_MILLISECOND: typeof PRECISION_MILLISECOND;
1858
+ PRECISION_MONTH: typeof PRECISION_MONTH;
1859
+ PRECISION_YEAR: typeof PRECISION_YEAR;
1860
+ PRECISION_DECADE: typeof PRECISION_DECADE;
1861
+ PRECISION_CENTURY: typeof PRECISION_CENTURY;
1862
+ PRECISION_MILLENNIUM: typeof PRECISION_MILLENNIUM;
1863
+ PRECISION_MILLION_YEARS: typeof PRECISION_MILLION_YEARS;
1864
+ PRECISION_BILLION_YEARS: typeof PRECISION_BILLION_YEARS;
1865
+ DENSITY_ALL: typeof DENSITY_ALL;
1866
+ DENSITY_LOW: typeof DENSITY_LOW;
1867
+ DENSITY_MEDIUM: typeof DENSITY_MEDIUM;
1868
+ DENSITY_HIGH: typeof DENSITY_HIGH;
1869
+ RANGE_ALL: typeof RANGE_ALL;
1870
+ RANGE_SCREEN: typeof RANGE_SCREEN;
1871
+ }
1872
+
1873
+ declare const HistropediaModule: HistropediaApi;
1874
+
1875
+ export default HistropediaModule;
1876
+
1877
+ declare global {
1878
+ const Histropedia: HistropediaApi;
1879
+
1880
+ interface Window {
1881
+ Histropedia: HistropediaApi;
1882
+ }
1883
+ }