@event-timeline/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/index.cjs +2626 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +439 -0
- package/dist/index.d.ts +439 -0
- package/dist/index.js +2614 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A continuous timestamp in milliseconds since the Unix epoch (UTC).
|
|
3
|
+
*
|
|
4
|
+
* "Date-level" granularity is a display concern, not storage: all calendar
|
|
5
|
+
* interpretation (day boundaries, tick labels, day-clustering) is done in UTC,
|
|
6
|
+
* but the stored value is always milliseconds (DESIGN.md §1).
|
|
7
|
+
*/
|
|
8
|
+
type Time = number;
|
|
9
|
+
/** An entity drawn as a horizontal line (lane). */
|
|
10
|
+
interface TimelineElement<TData = unknown> {
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
/** URL, emoji, or preloaded image; drawn in the label area (Phase 2+). */
|
|
14
|
+
icon?: string | HTMLImageElement;
|
|
15
|
+
/** Per-element style overrides, merged over the theme (Phase 6). */
|
|
16
|
+
style?: Partial<ElementStyle>;
|
|
17
|
+
/** Ordering hint honoured only by the `explicit` layout strategy (§7). */
|
|
18
|
+
order?: number;
|
|
19
|
+
/** Opaque host payload, echoed back verbatim in callbacks. */
|
|
20
|
+
data?: TData;
|
|
21
|
+
}
|
|
22
|
+
/** A directed connector drawn as an arrow between two elements at a point in time. */
|
|
23
|
+
interface TimelineEvent<TData = unknown> {
|
|
24
|
+
id: string;
|
|
25
|
+
/** Element the arrow leaves (source node dot). */
|
|
26
|
+
sourceId: string;
|
|
27
|
+
/** Element the arrow enters (arrowhead). */
|
|
28
|
+
targetId: string;
|
|
29
|
+
/** The moment the event occurs. */
|
|
30
|
+
time: Time;
|
|
31
|
+
label?: string;
|
|
32
|
+
style?: Partial<EventStyle>;
|
|
33
|
+
data?: TData;
|
|
34
|
+
}
|
|
35
|
+
/** A clean dataset handed to the engine to render. */
|
|
36
|
+
interface TimelineData<TData = unknown> {
|
|
37
|
+
elements: TimelineElement<TData>[];
|
|
38
|
+
events: TimelineEvent<TData>[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The complete set of stylable element properties (DESIGN.md §11). The same
|
|
42
|
+
* shape is reused at every level of the resolution chain (theme default →
|
|
43
|
+
* resolver → per-item `style`), so there is one definition of "what is
|
|
44
|
+
* stylable" rather than parallel shapes per layer (DRY). Sizes are CSS px.
|
|
45
|
+
*/
|
|
46
|
+
interface ElementStyle {
|
|
47
|
+
lineColor: string;
|
|
48
|
+
lineWidth: number;
|
|
49
|
+
labelColor: string;
|
|
50
|
+
/** CSS font shorthand, e.g. "12px Inter, sans-serif". */
|
|
51
|
+
labelFont: string;
|
|
52
|
+
iconSize: number;
|
|
53
|
+
}
|
|
54
|
+
/** The complete set of stylable event properties (DESIGN.md §11). Sizes are CSS px. */
|
|
55
|
+
interface EventStyle {
|
|
56
|
+
arrowColor: string;
|
|
57
|
+
arrowWidth: number;
|
|
58
|
+
arrowheadSize: number;
|
|
59
|
+
labelColor: string;
|
|
60
|
+
labelFont: string;
|
|
61
|
+
/** Source node dot radius. */
|
|
62
|
+
nodeRadius: number;
|
|
63
|
+
}
|
|
64
|
+
/** Engine-wide theme: defaults plus structural metrics (DESIGN.md §11). */
|
|
65
|
+
interface Theme {
|
|
66
|
+
background: string;
|
|
67
|
+
gridlineColor: string;
|
|
68
|
+
headerTextColor: string;
|
|
69
|
+
headerFont: string;
|
|
70
|
+
/** Height of the (multi-row) time header in CSS px. */
|
|
71
|
+
headerHeight: number;
|
|
72
|
+
rowHeight: number;
|
|
73
|
+
rowGap: number;
|
|
74
|
+
labelPadding: number;
|
|
75
|
+
/** Overlay colour for the hovered primitive (DESIGN.md §9). */
|
|
76
|
+
hoverColor: string;
|
|
77
|
+
/** Overlay colour for selected primitives' outlines (DESIGN.md §9). */
|
|
78
|
+
selectionColor: string;
|
|
79
|
+
/** Colour of collapsed-event cluster markers and their counts (DESIGN.md §6). */
|
|
80
|
+
clusterColor: string;
|
|
81
|
+
/** Theme-level element defaults (base of the resolution chain). */
|
|
82
|
+
element: ElementStyle;
|
|
83
|
+
/** Theme-level event defaults (base of the resolution chain). */
|
|
84
|
+
event: EventStyle;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Why an item was dropped during ingest. */
|
|
88
|
+
type DiagnosticCode = 'invalid-time' | 'dangling-edge' | 'duplicate-id';
|
|
89
|
+
interface Diagnostic {
|
|
90
|
+
code: DiagnosticCode;
|
|
91
|
+
kind: 'element' | 'event';
|
|
92
|
+
/** Id of the offending item, when known. */
|
|
93
|
+
id?: string;
|
|
94
|
+
reason: string;
|
|
95
|
+
}
|
|
96
|
+
type OnDiagnostic = (d: Diagnostic) => void;
|
|
97
|
+
|
|
98
|
+
interface LayoutStrategy {
|
|
99
|
+
/**
|
|
100
|
+
* Pure function of the data: returns element ids in top-to-bottom order.
|
|
101
|
+
* No rendering, no side effects, no pixels — trivially unit-testable.
|
|
102
|
+
*/
|
|
103
|
+
order(elements: readonly TimelineElement[], events: readonly TimelineEvent[]): string[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Per-frame context handed to a strategy; carries the current zoom (§6). */
|
|
107
|
+
interface ClusterContext {
|
|
108
|
+
/** Pixel separation under which same-key events merge (also the column width). */
|
|
109
|
+
minSeparationPx: number;
|
|
110
|
+
/** Camera time→screen-x (the single coordinate source, §3). */
|
|
111
|
+
timeToScreenX(t: Time): number;
|
|
112
|
+
}
|
|
113
|
+
interface ClusterStrategy {
|
|
114
|
+
/**
|
|
115
|
+
* Stable grouping key for an event at the current zoom. Events sharing a key
|
|
116
|
+
* and within `minSeparationPx` collapse into one marker; pure, no side effects.
|
|
117
|
+
*/
|
|
118
|
+
key(event: TimelineEvent, ctx: ClusterContext): string;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A run of one or more events that render as a unit. A single-event cluster is
|
|
122
|
+
* "un-clustered" — the renderer draws it as an ordinary arrow; a multi-event
|
|
123
|
+
* cluster draws a count marker (§6).
|
|
124
|
+
*/
|
|
125
|
+
interface Cluster<TData = unknown> {
|
|
126
|
+
events: TimelineEvent<TData>[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
type TickLevel = 'year' | 'month' | 'day';
|
|
130
|
+
type TickFormatter = (date: Date, level: TickLevel) => string;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Overlay `partials` onto a complete `base`, later partials winning. A property
|
|
134
|
+
* set to `undefined` (or absent) in a partial **falls through** to the previous
|
|
135
|
+
* layer, matching the `?? theme.default` semantics each level previously used and
|
|
136
|
+
* keeping per-property independence (§11): a partial only overrides the keys it
|
|
137
|
+
* actually sets.
|
|
138
|
+
*/
|
|
139
|
+
declare function overlayStyle<T extends object>(base: T, ...partials: Array<Partial<T> | undefined>): T;
|
|
140
|
+
/** Data-driven element style override (§11); merged between theme and `.style`. */
|
|
141
|
+
type ElementStyleResolver<TData = unknown> = (element: TimelineElement<TData>) => Partial<ElementStyle>;
|
|
142
|
+
/** Data-driven event style override (§11); merged between theme and `.style`. */
|
|
143
|
+
type EventStyleResolver<TData = unknown> = (event: TimelineEvent<TData>) => Partial<EventStyle>;
|
|
144
|
+
|
|
145
|
+
type HitResult<TData = unknown> = {
|
|
146
|
+
kind: 'element';
|
|
147
|
+
element: TimelineElement<TData>;
|
|
148
|
+
} | {
|
|
149
|
+
kind: 'event';
|
|
150
|
+
event: TimelineEvent<TData>;
|
|
151
|
+
} | {
|
|
152
|
+
kind: 'cluster';
|
|
153
|
+
events: TimelineEvent<TData>[];
|
|
154
|
+
count: number;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
interface TimelineEventMap<TData = unknown> {
|
|
158
|
+
viewportChange: {
|
|
159
|
+
timeRange: [number, number];
|
|
160
|
+
scrollY: number;
|
|
161
|
+
pxPerMs: number;
|
|
162
|
+
visibleRowRange: [number, number];
|
|
163
|
+
};
|
|
164
|
+
hover: HitResult<TData> | null;
|
|
165
|
+
click: {
|
|
166
|
+
hit: HitResult<TData>;
|
|
167
|
+
modifiers: {
|
|
168
|
+
ctrlOrMeta: boolean;
|
|
169
|
+
shift: boolean;
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
selectionChange: {
|
|
173
|
+
ids: string[];
|
|
174
|
+
};
|
|
175
|
+
dataChange: {
|
|
176
|
+
added: number;
|
|
177
|
+
removed: number;
|
|
178
|
+
updated: number;
|
|
179
|
+
};
|
|
180
|
+
ready: void;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Event collapsing/aggregation config (DESIGN.md §6); all fields defaulted. */
|
|
184
|
+
interface ClusteringOptions {
|
|
185
|
+
/** Collapse dense events into count markers. Default `true`. */
|
|
186
|
+
enabled?: boolean;
|
|
187
|
+
/** Keying strategy. Default `bySourceTargetColumn`. */
|
|
188
|
+
strategy?: ClusterStrategy;
|
|
189
|
+
/** Pixel separation under which same-key events merge. Default 12. */
|
|
190
|
+
minSeparationPx?: number;
|
|
191
|
+
}
|
|
192
|
+
/** Level-of-detail config (DESIGN.md §6); all fields defaulted. */
|
|
193
|
+
interface LodOptions {
|
|
194
|
+
/** Zoom (CSS px per ms) at/above which per-event labels are drawn. */
|
|
195
|
+
eventLabelMinPxPerMs?: number;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Per-frame timing for one drawn layer (DESIGN.md §13). Emitted to the optional
|
|
199
|
+
* `onFrameStats` sink after each layer draw so hosts and the perf benchmark can
|
|
200
|
+
* assert the §13 budgets (≤16ms base, ≤4ms overlay at 50k) without the engine
|
|
201
|
+
* itself depending on `performance` or any logging implementation (DIP).
|
|
202
|
+
*/
|
|
203
|
+
interface FrameStats {
|
|
204
|
+
layer: 'base' | 'overlay';
|
|
205
|
+
/** Wall-clock cost of the layer's draw, in milliseconds. */
|
|
206
|
+
durationMs: number;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Data-driven per-item style overrides (DESIGN.md §11). Each callback runs
|
|
210
|
+
* between the theme default and the item's own `.style`; most specific wins.
|
|
211
|
+
*/
|
|
212
|
+
interface StyleResolvers<TData = unknown> {
|
|
213
|
+
element?: ElementStyleResolver<TData>;
|
|
214
|
+
event?: EventStyleResolver<TData>;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Label/tick text formatters (DESIGN.md §11). When omitted the engine echoes the
|
|
218
|
+
* raw label (and the built-in UTC tick formatter of §8).
|
|
219
|
+
*/
|
|
220
|
+
interface Formatters<TData = unknown> {
|
|
221
|
+
tickLabel?: TickFormatter;
|
|
222
|
+
eventLabel?: (event: TimelineEvent<TData>) => string;
|
|
223
|
+
elementLabel?: (element: TimelineElement<TData>) => string;
|
|
224
|
+
}
|
|
225
|
+
interface TimelineEngineOptions<TData = unknown> {
|
|
226
|
+
theme?: Partial<Theme>;
|
|
227
|
+
onDiagnostic?: OnDiagnostic;
|
|
228
|
+
layout?: LayoutStrategy;
|
|
229
|
+
/**
|
|
230
|
+
* Trailing-edge delay (ms) for coalescing incremental re-layouts (DESIGN.md
|
|
231
|
+
* §7, §10). Applies only to the incremental seam ({@link scheduleLayoutRecompute});
|
|
232
|
+
* a full `setData` always recomputes synchronously. Default 120.
|
|
233
|
+
*/
|
|
234
|
+
layoutDebounceMs?: number;
|
|
235
|
+
/** Allow `ctrl`/`⌘`+click to extend the selection (DESIGN.md §9). */
|
|
236
|
+
multiSelect?: boolean;
|
|
237
|
+
/**
|
|
238
|
+
* Re-layout policy for incremental mutations (`addEvents`/… §10). Default
|
|
239
|
+
* `'stable-append'`: existing rows stay put and new elements append, applied
|
|
240
|
+
* synchronously so updates appear at once with no jarring reshuffle (§7).
|
|
241
|
+
* `'full'` opts into re-running the configured {@link layout} strategy on the
|
|
242
|
+
* debounced {@link layoutDebounceMs} seam. `setData` always relayouts in full.
|
|
243
|
+
*/
|
|
244
|
+
streamingLayout?: 'stable-append' | 'full';
|
|
245
|
+
clustering?: ClusteringOptions;
|
|
246
|
+
lod?: LodOptions;
|
|
247
|
+
/** Data-driven per-item style overrides (DESIGN.md §11). */
|
|
248
|
+
styleResolvers?: StyleResolvers<TData>;
|
|
249
|
+
/** Label/tick text formatters (DESIGN.md §11). */
|
|
250
|
+
formatters?: Formatters<TData>;
|
|
251
|
+
/**
|
|
252
|
+
* Optional per-frame timing sink (DESIGN.md §13). When supplied, each layer
|
|
253
|
+
* draw is bracketed with `performance.now()` and reported; when omitted the
|
|
254
|
+
* draw path stays allocation- and measurement-free (zero overhead).
|
|
255
|
+
*/
|
|
256
|
+
onFrameStats?: (stats: FrameStats) => void;
|
|
257
|
+
}
|
|
258
|
+
declare class TimelineEngine<TData = unknown> {
|
|
259
|
+
private readonly theme;
|
|
260
|
+
private readonly metrics;
|
|
261
|
+
private readonly store;
|
|
262
|
+
private readonly camera;
|
|
263
|
+
private readonly renderer;
|
|
264
|
+
private readonly scheduler;
|
|
265
|
+
private readonly emitter;
|
|
266
|
+
private readonly pointer;
|
|
267
|
+
private readonly multiSelect;
|
|
268
|
+
private readonly layoutStrategy;
|
|
269
|
+
/** Incremental re-layout policy (DESIGN.md §7, §10). */
|
|
270
|
+
private readonly streamingLayout;
|
|
271
|
+
/**
|
|
272
|
+
* Coalesced incremental re-layout (DESIGN.md §7). Streaming mutations
|
|
273
|
+
* (`addEvents`/`removeEvents`, §10) call {@link scheduleLayoutRecompute} so a
|
|
274
|
+
* burst re-runs layout once; `setData` bypasses this and recomputes inline.
|
|
275
|
+
*/
|
|
276
|
+
private readonly debouncedRecomputeLayout;
|
|
277
|
+
/** Resolved per-frame LOD/clustering config (DESIGN.md §6). */
|
|
278
|
+
private readonly lod;
|
|
279
|
+
private readonly clustering;
|
|
280
|
+
/**
|
|
281
|
+
* §11 style/label resolution, bound once from the theme + host resolvers. The
|
|
282
|
+
* builders receive these via `BuildContext`, so resolution lives in one place.
|
|
283
|
+
*/
|
|
284
|
+
private readonly resolveElementStyle;
|
|
285
|
+
private readonly resolveEventStyle;
|
|
286
|
+
private readonly formatElementLabel;
|
|
287
|
+
private readonly formatEventLabel;
|
|
288
|
+
private readonly tickFormatter;
|
|
289
|
+
/** Optional per-frame timing sink (DESIGN.md §13); undefined disables timing. */
|
|
290
|
+
private readonly onFrameStats?;
|
|
291
|
+
private readonly iconCache;
|
|
292
|
+
/**
|
|
293
|
+
* Resolve an element icon for the current frame (§1, §11): a preloaded image
|
|
294
|
+
* is used directly when decoded, a URL is loaded/cached (skipped until ready),
|
|
295
|
+
* and any other string (emoji/glyph) is drawn as text by the builder.
|
|
296
|
+
*/
|
|
297
|
+
private readonly resolveIcon;
|
|
298
|
+
private viewport;
|
|
299
|
+
private dpr;
|
|
300
|
+
private rowLayout;
|
|
301
|
+
private hasFitted;
|
|
302
|
+
private disposed;
|
|
303
|
+
/** Hit grid + targets rebuilt each base draw; reused during pure hover (§5). */
|
|
304
|
+
private hitGrid;
|
|
305
|
+
private frameTargets;
|
|
306
|
+
/** Latest cursor position pending a (frame-coalesced) hit-test, or null. */
|
|
307
|
+
private pendingPointer;
|
|
308
|
+
private hovered;
|
|
309
|
+
private readonly selectedIds;
|
|
310
|
+
constructor(baseCanvas: HTMLCanvasElement, overlayCanvas: HTMLCanvasElement, options?: TimelineEngineOptions<TData>);
|
|
311
|
+
setData(data: TimelineData<TData>): void;
|
|
312
|
+
/** Incrementally insert events without a full rebuild (DESIGN.md §10). */
|
|
313
|
+
addEvents(events: readonly TimelineEvent<TData>[]): void;
|
|
314
|
+
/** Remove events by id (DESIGN.md §10). */
|
|
315
|
+
removeEvents(ids: readonly string[]): void;
|
|
316
|
+
/** Append elements with stable-append layout (DESIGN.md §7, §10). */
|
|
317
|
+
addElements(elements: readonly TimelineElement<TData>[]): void;
|
|
318
|
+
/** Patch an element's label/icon/style/order in place (DESIGN.md §10). */
|
|
319
|
+
updateElement(id: string, patch: Partial<TimelineElement<TData>>): void;
|
|
320
|
+
/**
|
|
321
|
+
* Shared tail for the incremental mutations (§10): a no-op change emits and
|
|
322
|
+
* redraws nothing; otherwise re-layout (stable-append by default, §7), emit
|
|
323
|
+
* `dataChange`, and mark the base dirty. Streaming never refits — that would
|
|
324
|
+
* jerk the camera mid-stream.
|
|
325
|
+
*/
|
|
326
|
+
private applyMutation;
|
|
327
|
+
resize(cssWidth: number, cssHeight: number, dpr: number): void;
|
|
328
|
+
fit(): void;
|
|
329
|
+
panToTime(t: Time): void;
|
|
330
|
+
/** Frame a time range across the viewport width (DESIGN.md §12). */
|
|
331
|
+
zoomToRange(start: Time, end: Time): void;
|
|
332
|
+
/**
|
|
333
|
+
* Replace the selection imperatively (DESIGN.md §9, §12). Ids are the
|
|
334
|
+
* `kind:id` composites produced by hit-testing (see {@link hitId}), so an
|
|
335
|
+
* element and an event sharing a raw id stay distinct. A no-op replacement
|
|
336
|
+
* emits nothing and skips the overlay redraw.
|
|
337
|
+
*/
|
|
338
|
+
select(ids: string[]): void;
|
|
339
|
+
getViewport(): TimelineEventMap<TData>['viewportChange'];
|
|
340
|
+
on<K extends keyof TimelineEventMap<TData>>(key: K, fn: (payload: TimelineEventMap<TData>[K]) => void): () => void;
|
|
341
|
+
off<K extends keyof TimelineEventMap<TData>>(key: K, fn: (payload: TimelineEventMap<TData>[K]) => void): void;
|
|
342
|
+
dispose(): void;
|
|
343
|
+
private now;
|
|
344
|
+
private zoomBounds;
|
|
345
|
+
/**
|
|
346
|
+
* Edge space reserved when fitting (§3): a left gutter so the leftmost line's
|
|
347
|
+
* start marker (icon + label, §1) clears the viewport edge instead of being
|
|
348
|
+
* clipped. Derived from the theme so host themes scale it. With no elements
|
|
349
|
+
* there are no markers, so nothing is reserved (the empty-state window stays
|
|
350
|
+
* centred on `now`, §3.1).
|
|
351
|
+
*/
|
|
352
|
+
private fitInset;
|
|
353
|
+
/**
|
|
354
|
+
* Recompute row order + positions (DESIGN.md §7). `'full'` runs the configured
|
|
355
|
+
* strategy (used by `setData` and the opt-in streaming relayout); `'stable-append'`
|
|
356
|
+
* keeps existing rows and appends new elements (the streaming default, §10).
|
|
357
|
+
*/
|
|
358
|
+
private recomputeLayout;
|
|
359
|
+
/**
|
|
360
|
+
* Apply layout after an *incremental* mutation (DESIGN.md §7, §10). By default
|
|
361
|
+
* (`streamingLayout: 'stable-append'`) this runs synchronously so new rows and
|
|
362
|
+
* extents appear at once with no reshuffle; under `'full'` a burst is coalesced
|
|
363
|
+
* into one trailing strategy recompute via the debounced seam. Full `setData`
|
|
364
|
+
* bypasses this and recomputes synchronously so `fit`/scroll-clamp see
|
|
365
|
+
* `contentHeight` immediately.
|
|
366
|
+
*/
|
|
367
|
+
private scheduleLayoutRecompute;
|
|
368
|
+
/**
|
|
369
|
+
* Frame the view before the first explicit fit. With data, fit to it once and
|
|
370
|
+
* latch (`hasFitted`). Without data, frame the default window on `now` (§3.1)
|
|
371
|
+
* but stay un-latched, so real data still auto-fits when it arrives.
|
|
372
|
+
*/
|
|
373
|
+
private maybeFit;
|
|
374
|
+
/** Mark the base layer dirty and emit the new viewport (any camera change). */
|
|
375
|
+
private onCameraChange;
|
|
376
|
+
/**
|
|
377
|
+
* The semantic-intent sink for the pointer controller (§9). The controller
|
|
378
|
+
* does no math; every camera mutation and hit-test is applied here so the
|
|
379
|
+
* Camera stays the single source of coordinate truth (§3).
|
|
380
|
+
*/
|
|
381
|
+
private pointerHost;
|
|
382
|
+
/** Resolve a click against the current hit grid: emit click + update selection. */
|
|
383
|
+
private onClick;
|
|
384
|
+
/** Zoom to a clicked cluster's member time span, expanding it (§6). */
|
|
385
|
+
private expandCluster;
|
|
386
|
+
private emitViewportChange;
|
|
387
|
+
private drawFrame;
|
|
388
|
+
/**
|
|
389
|
+
* Run one layer's draw, reporting its wall-clock cost to `onFrameStats` when a
|
|
390
|
+
* sink is set (DESIGN.md §13). With no sink the draw runs directly — no
|
|
391
|
+
* `performance.now()` calls, no allocation — so timing is truly opt-in.
|
|
392
|
+
*/
|
|
393
|
+
private timed;
|
|
394
|
+
private drawBase;
|
|
395
|
+
/**
|
|
396
|
+
* Overlay pass (DESIGN.md §4, §9): resolve the pending hover against the hit
|
|
397
|
+
* grid (emitting `hover` only on change), then redraw the hovered and selected
|
|
398
|
+
* primitives in the highlight/selection colours. It reuses this frame's
|
|
399
|
+
* `frameTargets` geometry — no recompute — and never touches the base layer,
|
|
400
|
+
* so hover/selection stay independent of the expensive base redraw.
|
|
401
|
+
*/
|
|
402
|
+
private drawOverlay;
|
|
403
|
+
/**
|
|
404
|
+
* Overlay colour for a target, or null when it should not highlight. A target
|
|
405
|
+
* highlights when it is itself hovered/selected, or — for an event/cluster —
|
|
406
|
+
* when one of its endpoints is a hovered/selected element (§9). Hover wins the
|
|
407
|
+
* colour over selection.
|
|
408
|
+
*/
|
|
409
|
+
private highlightColorFor;
|
|
410
|
+
/** Run the coalesced hit-test for the latest cursor position and emit on change. */
|
|
411
|
+
private resolveHover;
|
|
412
|
+
/** Redraw one target's geometry in `color`, slightly thickened, on the overlay. */
|
|
413
|
+
private drawHighlight;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
declare const byFirstEvent: LayoutStrategy;
|
|
417
|
+
|
|
418
|
+
declare const barycenter: LayoutStrategy;
|
|
419
|
+
|
|
420
|
+
declare const explicit: LayoutStrategy;
|
|
421
|
+
|
|
422
|
+
/** Collapse every event sharing a pixel column, regardless of endpoints. */
|
|
423
|
+
declare const byPixelColumn: ClusterStrategy;
|
|
424
|
+
/** Collapse events between the same source and target (proximity-gated, §6). */
|
|
425
|
+
declare const byElementPair: ClusterStrategy;
|
|
426
|
+
/** Collapse events sharing a UTC calendar day (proximity-gated, §6). */
|
|
427
|
+
declare const byDay: ClusterStrategy;
|
|
428
|
+
/**
|
|
429
|
+
* Default: `(sourceId, targetId, pixelColumn)` — same-direction events in the
|
|
430
|
+
* same column collapse, so a dense fan-out between two lines reads as one marker.
|
|
431
|
+
*/
|
|
432
|
+
declare const bySourceTargetColumn: ClusterStrategy;
|
|
433
|
+
|
|
434
|
+
declare const defaultTheme: Theme;
|
|
435
|
+
|
|
436
|
+
/** Current package version placeholder; real releases are driven by Changesets. */
|
|
437
|
+
declare const VERSION = "0.0.0";
|
|
438
|
+
|
|
439
|
+
export { type Cluster, type ClusterContext, type ClusterStrategy, type ClusteringOptions, type Diagnostic, type DiagnosticCode, type ElementStyle, type ElementStyleResolver, type EventStyle, type EventStyleResolver, type Formatters, type FrameStats, type HitResult, type LayoutStrategy, type LodOptions, type OnDiagnostic, type StyleResolvers, type Theme, type TickFormatter, type TickLevel, type Time, type TimelineData, type TimelineElement, TimelineEngine, type TimelineEngineOptions, type TimelineEvent, type TimelineEventMap, VERSION, barycenter, byDay, byElementPair, byFirstEvent, byPixelColumn, bySourceTargetColumn, defaultTheme, explicit, overlayStyle };
|