@wick-charts/react 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,119 +1,236 @@
1
1
  import { CSSProperties } from 'react';
2
2
  import { JSX as JSX_2 } from 'react/jsx-runtime';
3
+ import { NamedExoticComponent } from 'react';
3
4
  import { Provider } from 'react';
4
5
  import { ReactNode } from 'react';
5
6
 
6
7
  export declare const andromeda: ThemePreset;
7
8
 
8
9
  /**
9
- * Chart-level animation configuration. Two independent domains so per-series
10
- * defaults can't bleed into viewport-interaction timings.
10
+ * Animation behavior knobs grouped by surface:
11
11
  *
12
- * **Two layerschart-level vs per-series.** The same data-animation knobs
13
- * live on both layers; remember which is which:
12
+ * - `axis.y`Y bound chase: pluggable curve, expand/contract/gesture
13
+ * settle times.
14
+ * - `axis.x` — X viewport: streaming settle + gesture override.
15
+ * - `axis.ticks` — axis tick label cross-fade.
16
+ * - `toggle` — series visibility (alpha fade + Y re-fit, locked to one
17
+ * duration so they finish on the same frame).
18
+ * - `series.{line,candlestick,bar,pie}` — per-series-type data tweens.
14
19
  *
15
- * | Field | Chart-level (here) | Per-series option |
16
- * | ----- | ------------------ | ----------------- |
17
- * | Entry duration | {@link AnimationsConfig.points}`.enterMs` | `<XSeries options={{ entryMs }}>` (canonical name; `enterMs` is a `@deprecated` alias) |
18
- * | Live smoothing | `points.smoothMs` | `options={{ smoothMs }}` |
19
- * | Line pulse period | `points.pulseMs` | `<LineSeries options={{ pulseMs }}>` |
20
+ * Top-level `false` disables every animation category. `axis: false`
21
+ * disables both axes and ticks. `axis.y: false` / `axis.x: false` disables
22
+ * that axis only. Series-type-level `false` (`series: { line: false }`)
23
+ * disables that type only.
20
24
  *
21
- * Resolution: per-series option **wins** (it's the override). The chart-
22
- * level field acts as the default for every series that didn't set its own
23
- * override *except* when the chart-level category is explicitly `false`,
24
- * which is a hard disable that overrides per-series too.
25
- *
26
- * - `points` — applied to data: entrance tween, live-tracking smoothing of
27
- * the last candle/bar/line value, line pulse cadence.
28
- * - `viewport` — applied to viewport interactions: post-gesture rebound,
29
- * Y-axis range chase, optional per-event ease for pan/zoom. Has no
30
- * per-series equivalent — these are chart-level only.
31
- *
32
- * All settling animations share a 250 ms default so the X re-fit, Y range
33
- * update, and last-bar live-track all settle on the same frame on a
34
- * streaming tick. Pulse cycle period (600 ms) and `inputResponseMs` (0,
35
- * opt-in) keep their own values.
25
+ * The per-series numeric fields (`entry` / `smooth` / `pulse`) also exist
26
+ * on individual series options (`<XSeries options={{ entryMs }}>`). The
27
+ * chart-level field acts as the default for any series that hasn't set its
28
+ * own override; an explicit `series.<type>: false` (or top-level `false`)
29
+ * is a hard disable that overrides per-series.
36
30
  */
37
31
  export declare interface AnimationsConfig {
38
32
  /**
39
- * Data-series animations. `false` disables every point animation (entrance,
40
- * live-smoothing, pulse) across every series — overrides any per-series
41
- * option set on the same fields. An object overrides individual categories;
42
- * omitted fields fall back to the built-in defaults.
43
- *
44
- * Per-series options (`<LineSeries options={{ entryMs, smoothMs, pulseMs }}>`)
45
- * win over chart-level numeric values. The chart-level field becomes the
46
- * default for series that don't set their own.
33
+ * Axis-level animation. `false` collapses both axes and ticks to instant.
47
34
  */
48
- points?: false | {
35
+ axis?: false | {
49
36
  /**
50
- * Per-point entrance duration (ms). Default: 250.
51
- * Per-series equivalent: `<XSeries options={{ entryMs }}>` (note the
52
- * `y` — chart-level uses `enterMs`, per-series uses `entryMs` for
53
- * historical reasons; both refer to the same animation). `false` /
54
- * `0` disables.
37
+ * Y bound chase. `false` snaps Y instantly.
55
38
  */
56
- enterMs?: AnimationTime;
39
+ y?: false | {
40
+ /** Y curve. See {@link hermite}, {@link spring}, {@link snap}. */
41
+ curve?: TransitionFactory<YRange>;
42
+ /**
43
+ * Outward settle time — bound expanding to a new extreme.
44
+ *
45
+ * @default {@link DEFAULT_Y_SETTLE_MS}
46
+ */
47
+ settle?: AnimationTime;
48
+ /**
49
+ * Inward (contraction) settle — applied when a bound recedes
50
+ * after an extreme leaves the window (the "sticky-Y" hold).
51
+ *
52
+ * - `{ min, max }` — **dynamic**: the contract duration scales
53
+ * with the contraction magnitude, from `min` (a tiny recede)
54
+ * up to `max` (a full-range contraction). Big outliers ease
55
+ * off slowly while small receders finish quickly, so the axis
56
+ * doesn't crawl for seconds over a few pixels. Either side
57
+ * falls back to its default when omitted.
58
+ * - a scalar `AnimationTime` — **fixed**: `min === max`, every
59
+ * contraction takes the same time regardless of size (the
60
+ * legacy sticky feel). `false` snaps contractions instantly.
61
+ *
62
+ * @default `{ min:` {@link DEFAULT_Y_STICKY_MIN_MS}`, max:` {@link DEFAULT_Y_STICKY_MAX_MS}` }`
63
+ */
64
+ sticky?: AnimationTime | {
65
+ min?: AnimationTime;
66
+ max?: AnimationTime;
67
+ };
68
+ /**
69
+ * One-shot override during a user gesture (pan/zoom). Shorter
70
+ * than `sticky` so contractions during interaction converge in
71
+ * ~one frame per wheel tick.
72
+ *
73
+ * @default {@link DEFAULT_Y_GESTURE_MS}
74
+ */
75
+ gesture?: AnimationTime;
76
+ };
57
77
  /**
58
- * Live-value chase duration (ms) for the displayed last point on
59
- * `updateData` ticks. Animator-driven cubic ease after this many ms
60
- * with no new updates, the displayed value reaches exactly the
61
- * actual last value. Default: 250. Per-series equivalent:
62
- * `options={{ smoothMs }}`. `false` / `0` snaps.
78
+ * X viewport. `false` snaps X instantly. The default critically-
79
+ * damped spring carries velocity across retargets so wheel-zoom
80
+ * sequences feel continuous and stream ticks blend smoothly into
81
+ * gesture motion.
63
82
  */
64
- smoothMs?: AnimationTime;
83
+ x?: false | {
84
+ /** X curve. See {@link spring}, {@link snap}. */
85
+ curve?: TransitionFactory<VisibleRange>;
86
+ /**
87
+ * Streaming settle time. Spring reaches ~99% of the target
88
+ * after this many ms. Used for streaming retargets; the
89
+ * streaming-cadence EMA tunes the effective value upward when
90
+ * data arrives slower than the baseline.
91
+ *
92
+ * @default {@link DEFAULT_X_SETTLE_MS}
93
+ */
94
+ settle?: AnimationTime;
95
+ /**
96
+ * One-shot override applied to user pan/zoom commits and to
97
+ * programmatic `fitContent`.
98
+ *
99
+ * @default {@link DEFAULT_X_GESTURE_MS}
100
+ */
101
+ gesture?: AnimationTime;
102
+ };
65
103
  /**
66
- * Pulse cycle period (ms) for the line last-point halo — periodic,
67
- * not a one-shot transition. Default: 600. Per-series equivalent:
68
- * `<LineSeries options={{ pulseMs }}>`. `false` / `0` disables.
104
+ * Axis tick label cross-fade. `false` makes tick relabel instant.
105
+ *
106
+ * @default {@link DEFAULT_TICKS_MS}
69
107
  */
70
- pulseMs?: AnimationTime;
108
+ ticks?: AnimationTime;
71
109
  };
72
110
  /**
73
- * Viewport interaction animations. `false` disables both rebound and Y-axis
74
- * smoothing viewport changes snap instantly.
111
+ * Series-visibility toggle duration. Drives BOTH the renderer's alpha
112
+ * cross-fade and the engine's Y re-fit ease, so the two animations land
113
+ * on the same frame. `false` makes `setSeriesVisible` instant.
114
+ *
115
+ * @default {@link DEFAULT_TOGGLE_MS}
116
+ */
117
+ toggle?: AnimationTime;
118
+ /**
119
+ * Per-series-type data animations. `false` disables every per-point
120
+ * animation across every series. Setting a single type to `false`
121
+ * (`series: { line: false }`) disables that type only.
122
+ *
123
+ * Per-series options (`<LineSeries options={{ entryMs, smoothMs, pulseMs }}>`)
124
+ * win over chart-level numeric values. The chart-level field becomes the
125
+ * default for series that don't set their own.
75
126
  */
76
- viewport?: false | {
77
- /** Rebound (snap-back) duration after pan/zoom overshoot (ms). Default: 350. */
78
- reboundMs?: AnimationTime;
127
+ series?: false | {
79
128
  /**
80
- * Y-axis range transition duration in wall-clock milliseconds. The Y
81
- * min and max each ride their own {@link Animator} that retargets on
82
- * data updates and eases toward the new bound over this many ms.
83
- * Default `250` (shares the lockstep-arrival budget with viewport and
84
- * live-track). Inward contraction always eases. Outward expansion
85
- * eases when the per-point entrance is enabled (the entering candle's
86
- * fade masks the brief overshoot); when entrance is hard-disabled
87
- * (`points.enterMs === 0`), Y bounds snap outward to keep new
88
- * highs/lows from clipping at the canvas edge. `false` / `0` snaps
89
- * the Y range instantly on every update.
129
+ * Line-series tweens. `false` disables all line animations
130
+ * (entrance, live-smoothing, pulse).
90
131
  */
91
- yAxisMs?: AnimationTime;
132
+ line?: false | {
133
+ /**
134
+ * Per-point entrance duration. `false` disables the entrance
135
+ * (equivalent to `entryAnimation: 'none'`).
136
+ *
137
+ * @default {@link DEFAULT_LINE_ENTRY}
138
+ */
139
+ entry?: AnimationTime;
140
+ /**
141
+ * Last-value chase duration on `updateLastPoint`. `false` /
142
+ * `0` snaps to the target instantly.
143
+ *
144
+ * @default {@link DEFAULT_LINE_SMOOTH}
145
+ */
146
+ smooth?: AnimationTime;
147
+ /**
148
+ * Halo cycle period at the line tail. Periodic loop, not a
149
+ * one-shot. `false` / `0` turns the halo off entirely (drawing
150
+ * and animation loop).
151
+ *
152
+ * @default {@link DEFAULT_LINE_PULSE}
153
+ */
154
+ pulse?: AnimationTime;
155
+ };
92
156
  /**
93
- * Per-event ease applied to user pan/zoom commits. Logical state
94
- * advances synchronously (gesture math, edge detection, autoscroll
95
- * all read the committed target); the visual range eases over this
96
- * duration so back-to-back wheel/trackpad events interpolate
97
- * smoothly through the same animator.
98
- *
99
- * Default `0` (instant-apply, matches the long-standing pre-Phase-5
100
- * behaviour). Opt in via `inputResponseMs: 60` for an eased pan/zoom
101
- * feel — the default is conservative because the animated visual
102
- * range diverges from the committed target until the ease completes,
103
- * and existing consumers reading `chart.getVisibleRange()`
104
- * synchronously after a wheel/pan expect the new value.
157
+ * Candlestick tweens. `false` disables candle entrance + OHLC chase.
105
158
  */
106
- inputResponseMs?: AnimationTime;
159
+ candlestick?: false | {
160
+ /**
161
+ * Per-candle entrance duration. `false` disables the entrance
162
+ * (equivalent to `entryAnimation: 'none'`).
163
+ *
164
+ * @default {@link DEFAULT_CANDLESTICK_ENTRY}
165
+ */
166
+ entry?: AnimationTime;
167
+ /**
168
+ * Live OHLC chase duration on `updateLastPoint`. `false` / `0`
169
+ * snaps to the target instantly.
170
+ *
171
+ * @default {@link DEFAULT_CANDLESTICK_SMOOTH}
172
+ */
173
+ smooth?: AnimationTime;
174
+ };
175
+ /**
176
+ * Bar-series tweens. `false` disables bar entrance + value chase.
177
+ */
178
+ bar?: false | {
179
+ /**
180
+ * Per-bar entrance duration. `false` disables the entrance
181
+ * (equivalent to `entryAnimation: 'none'`).
182
+ *
183
+ * @default {@link DEFAULT_BAR_ENTRY}
184
+ */
185
+ entry?: AnimationTime;
186
+ /**
187
+ * Live value chase duration on `updateLastPoint`. `false` /
188
+ * `0` snaps to the target instantly.
189
+ *
190
+ * @default {@link DEFAULT_BAR_SMOOTH}
191
+ */
192
+ smooth?: AnimationTime;
193
+ };
194
+ /**
195
+ * Pie segment entry/update tweens. Parsed at config-time; the actual
196
+ * wiring lands in a later phase — providing a value here today is a
197
+ * no-op but the shape is stable.
198
+ */
199
+ pie?: false | {
200
+ /**
201
+ * Slice grow-in duration on first paint.
202
+ *
203
+ * @default {@link DEFAULT_PIE_ENTRY}
204
+ */
205
+ entry?: AnimationTime;
206
+ /**
207
+ * Slice resize duration when data changes.
208
+ *
209
+ * @default {@link DEFAULT_PIE_UPDATE}
210
+ */
211
+ update?: AnimationTime;
212
+ };
107
213
  };
108
214
  }
109
215
 
110
- /** Options passed when creating a new {@link ChartInstance}. */
111
216
  /**
112
- * Time-value or boolean used throughout the animation API. `false` disables
113
- * the category; a number configures its duration/time-constant in milliseconds
114
- * (`0` also disables, useful when the caller wants a number shape).
217
+ * Per-frame engine snapshot same shape returned by the legacy
218
+ * `AnimationEngine.tick()` so existing consumers (chart render loop,
219
+ * `chart.getAnimationState()` public API, series overlay hooks) keep working.
220
+ */
221
+ declare interface AnimationState {
222
+ readonly xRange: XRange;
223
+ readonly yRange: YRange;
224
+ readonly animating: boolean;
225
+ }
226
+
227
+ /**
228
+ * Public animation-time input. Accepts:
229
+ * - `number` — already milliseconds (`250`)
230
+ * - `string` in `<n>ms` or `<n>s` form (`"250ms"`, `"1s"`, `"2.5s"`)
231
+ * - `false` — disable (equivalent to `0`)
115
232
  */
116
- declare type AnimationTime = number | false;
233
+ export declare type AnimationTime = Milliseconds | string | false;
117
234
 
118
235
  /**
119
236
  * Build a 2-stop candle-body gradient around a single hex color — a lightened
@@ -162,6 +279,51 @@ export declare interface AxisConfig {
162
279
  x?: XAxisConfig;
163
280
  }
164
281
 
282
+ declare class AxisTickTracker {
283
+ #private;
284
+ constructor(opts?: AxisTickTrackerOptions);
285
+ /**
286
+ * Record the latest tick set. Diffs against the current set and either
287
+ * starts a `0 → 1` fade-in for new values or a `1 → 0` fade-out for
288
+ * dropped ones. Idempotent on element-wise equal arrays.
289
+ *
290
+ * Callers must invoke {@link tick} every frame to advance the animators —
291
+ * `snapshot()` reads the current state but does not advance time.
292
+ */
293
+ setCurrentTicks(next: readonly number[]): void;
294
+ /**
295
+ * Advance each tracked animator against `now`. Animators that settle
296
+ * at zero are dropped from the map; non-zero settled animators stay so
297
+ * a subsequent re-entering tick can pick them up without a flicker.
298
+ * Returns `true` while any animator is still in flight.
299
+ */
300
+ tick(now: number): boolean;
301
+ /**
302
+ * Build a renderer-ready snapshot from the tracker's own animator state.
303
+ * No external opacity map is consulted — `setCurrentTicks` + `tick(now)`
304
+ * are the only inputs.
305
+ */
306
+ snapshot(): TickTrackerSnapshot;
307
+ getCurrentTicks(): readonly number[];
308
+ getPreviousTicks(): readonly number[];
309
+ /**
310
+ * Reconfigure the cross-fade duration. Chart calls this after resolving
311
+ * `animations.axis.ticks` so the tracker honors the user's config
312
+ * without plumbing options through the scale constructors.
313
+ */
314
+ setFadeMs(ms: number): void;
315
+ /** Whether subsequent tick-set changes should fade-in or snap. */
316
+ markArmed(): void;
317
+ get isArmed(): boolean;
318
+ /** Drop all tracked ticks. Use after dataset swap so stale values don't linger. */
319
+ reset(): void;
320
+ }
321
+
322
+ declare interface AxisTickTrackerOptions {
323
+ /** Cross-fade duration in ms once {@link markArmed} has flipped. Defaults to {@link DEFAULT_TICKS_MS}. */
324
+ fadeMs?: number;
325
+ }
326
+
165
327
  export declare const ayuMirage: ThemePreset;
166
328
 
167
329
  /**
@@ -194,8 +356,6 @@ export declare interface BarSeriesOptions {
194
356
  * @see entryMs — cross-linked duration for this animation.
195
357
  */
196
358
  entryAnimation?: BarEntryAnimation;
197
- /** @deprecated Use {@link entryAnimation} instead. */
198
- enterAnimation?: BarEntryAnimation;
199
359
  /**
200
360
  * Per-bar entrance duration in milliseconds. Default: `250`.
201
361
  *
@@ -203,24 +363,19 @@ export declare interface BarSeriesOptions {
203
363
  * // Override for one series:
204
364
  * <BarSeries options={{ entryMs: 600 }} data={data} />
205
365
  *
206
- * // Or set the default for every series at once:
207
- * <ChartContainer animations={{ points: { enterMs: 600 } }}>
366
+ * // Or set the default for every bar series at once:
367
+ * <ChartContainer animations={{ series: { bar: { entry: 600 } } }}>
208
368
  * <BarSeries data={data} />
209
369
  * </ChartContainer>
210
370
  * ```
211
371
  *
212
- * Note: chart-level uses `enterMs`, per-series uses `entryMs` — same
213
- * animation, two names for historical reasons.
214
- *
215
372
  * `false` or `0` disables the entrance (equivalent to
216
- * `entryAnimation: 'none'`). A chart-level `animations.points: false` is a
217
- * hard disable that wins over this field.
373
+ * `entryAnimation: 'none'`). A chart-level `animations.series.bar: false`
374
+ * is a hard disable that wins over this field.
218
375
  *
219
376
  * @see BarSeriesOptions.entryAnimation
220
377
  */
221
378
  entryMs?: number | false;
222
- /** @deprecated Use {@link entryMs} instead. */
223
- enterMs?: number | false;
224
379
  /**
225
380
  * How long the displayed bar value takes to catch up to the actual one
226
381
  * on every `updateLastPoint`. Default: `250` ms.
@@ -230,13 +385,13 @@ export declare interface BarSeriesOptions {
230
385
  * <BarSeries options={{ smoothMs: 100 }} data={data} />
231
386
  *
232
387
  * // Chart-level default:
233
- * <ChartContainer animations={{ points: { smoothMs: 100 } }}>
388
+ * <ChartContainer animations={{ series: { bar: { smooth: 100 } } }}>
234
389
  * <BarSeries data={data} />
235
390
  * </ChartContainer>
236
391
  * ```
237
392
  *
238
393
  * `0` or `false` snaps the displayed value to the target on every tick
239
- * (no smoothing). A chart-level `animations.points: false` is a hard
394
+ * (no smoothing). A chart-level `animations.series.bar: false` is a hard
240
395
  * disable that wins over this field.
241
396
  */
242
397
  smoothMs?: number | false;
@@ -251,9 +406,6 @@ declare interface BarSeriesProps {
251
406
  id?: string;
252
407
  }
253
408
 
254
- /** @deprecated Use {@link StackingMode} instead. */
255
- export declare type BarStacking = StackingMode;
256
-
257
409
  /**
258
410
  * Snapshot every visible series / layer at `time`. Used by hover overlays
259
411
  * (`InfoBar` in hover mode, `Tooltip`). Hidden series and hidden layers are
@@ -341,8 +493,6 @@ export declare interface CandlestickSeriesOptions {
341
493
  * @see entryMs — cross-linked duration for this animation.
342
494
  */
343
495
  entryAnimation?: CandlestickEntryAnimation;
344
- /** @deprecated Use {@link entryAnimation} instead. */
345
- enterAnimation?: CandlestickEntryAnimation;
346
496
  /**
347
497
  * Per-candle entrance duration in milliseconds. Default: `250`.
348
498
  *
@@ -350,24 +500,20 @@ export declare interface CandlestickSeriesOptions {
350
500
  * // Override for one series:
351
501
  * <CandlestickSeries options={{ entryMs: 600 }} data={data} />
352
502
  *
353
- * // Or set the default for every series at once:
354
- * <ChartContainer animations={{ points: { enterMs: 600 } }}>
503
+ * // Or set the default for every candlestick series at once:
504
+ * <ChartContainer animations={{ series: { candlestick: { entry: 600 } } }}>
355
505
  * <CandlestickSeries data={data} />
356
506
  * </ChartContainer>
357
507
  * ```
358
508
  *
359
- * Note: chart-level uses `enterMs`, per-series uses `entryMs` — same
360
- * animation, two names for historical reasons.
361
- *
362
509
  * `false` or `0` disables the entrance (equivalent to
363
- * `entryAnimation: 'none'`). A chart-level `animations.points: false` is a
364
- * hard disable that wins over this field.
510
+ * `entryAnimation: 'none'`). A chart-level
511
+ * `animations.series.candlestick: false` is a hard disable that wins over
512
+ * this field.
365
513
  *
366
514
  * @see CandlestickSeriesOptions.entryAnimation
367
515
  */
368
516
  entryMs?: number | false;
369
- /** @deprecated Use {@link entryMs} instead. */
370
- enterMs?: number | false;
371
517
  /**
372
518
  * How long the displayed OHLC takes to catch up to the actual last value
373
519
  * on every `updateLastPoint`. Default: `250` ms.
@@ -377,14 +523,14 @@ export declare interface CandlestickSeriesOptions {
377
523
  * <CandlestickSeries options={{ smoothMs: 100 }} data={data} />
378
524
  *
379
525
  * // Chart-level default:
380
- * <ChartContainer animations={{ points: { smoothMs: 100 } }}>
526
+ * <ChartContainer animations={{ series: { candlestick: { smooth: 100 } } }}>
381
527
  * <CandlestickSeries data={data} />
382
528
  * </ChartContainer>
383
529
  * ```
384
530
  *
385
531
  * `0` or `false` snaps the displayed value to the target on every tick
386
- * (no smoothing). A chart-level `animations.points: false` is a hard
387
- * disable that wins over this field.
532
+ * (no smoothing). A chart-level `animations.series.candlestick: false` is
533
+ * a hard disable that wins over this field.
388
534
  */
389
535
  smoothMs?: number | false;
390
536
  }
@@ -412,7 +558,7 @@ export declare const catppuccin: ThemePreset;
412
558
  * - Legend — flex sibling at the bottom (or right, when `position="right"`), so its height is
413
559
  * reserved by browser layout.
414
560
  */
415
- export declare function ChartContainer({ children, theme, axis, padding, gradient, interactive, grid, headerLayout, perf, animations, onEdgeReached, style, className, }: ChartContainerProps): JSX_2.Element;
561
+ export declare function ChartContainer({ children, theme, axis, padding, viewport, gradient, interactive, grid, headerLayout, perf, animations, onEdgeReached, style, className, }: ChartContainerProps): JSX_2.Element;
416
562
 
417
563
  /** Props for the {@link ChartContainer} component. */
418
564
  declare interface ChartContainerProps {
@@ -451,6 +597,30 @@ declare interface ChartContainerProps {
451
597
  intervals: number;
452
598
  };
453
599
  };
600
+ /**
601
+ * Viewport-level streaming behavior. Captured at mount only — changing this
602
+ * prop after the chart is created is ignored.
603
+ */
604
+ viewport?: {
605
+ /**
606
+ * Width of the visible window in data bars, set on the first data load
607
+ * to `maxVisibleBars * dataInterval`. While the dataset is smaller than
608
+ * this width, streaming ticks render into the empty right-side gap and
609
+ * the viewport stays put; once the data reaches the right edge, the
610
+ * viewport pans forward to keep the latest bar pinned (tail-scroll).
611
+ * Default: 200.
612
+ */
613
+ maxVisibleBars?: number;
614
+ /**
615
+ * Initial visible range applied before the first paint with data. Same
616
+ * shape as the imperative `chart.setVisibleRange` — pass a bar count
617
+ * (e.g. `35`), an explicit `{from, to}` window, or `{from, bars}` for
618
+ * a warm-up pair. The standard alternative is calling
619
+ * `setVisibleRange` from a `useEffect`, but that runs post-paint and
620
+ * makes the chart visibly re-zoom on the next RAF. Captured at mount.
621
+ */
622
+ initialRange?: VisibleRangeSpec;
623
+ };
454
624
  /** Show the chart background gradient. Defaults to true. */
455
625
  gradient?: boolean;
456
626
  /** Enable zoom, pan, and crosshair interactions. Defaults to true. */
@@ -470,33 +640,16 @@ declare interface ChartContainerProps {
470
640
  */
471
641
  headerLayout?: 'overlay' | 'inline';
472
642
  /**
473
- * Chart-level animation configuration. See {@link AnimationsConfig} for the
474
- * full shape.
475
- *
476
- * Two layers remember which is which:
477
- *
478
- * - **Chart-level (this prop)** — `animations.points.{enterMs, smoothMs,
479
- * pulseMs}` and `animations.viewport.{reboundMs, yAxisMs,
480
- * inputResponseMs}`. Acts as the default for every series.
481
- * - **Per-series** — `<LineSeries options={{ entryMs, smoothMs, pulseMs }}>`
482
- * (and the analogous CandlestickSeries / BarSeries options). Overrides
483
- * the chart-level default for that one series. Note the spelling:
484
- * `entryMs` per-series, `enterMs` chart-level — historical artefact,
485
- * both refer to the same animation.
486
- *
487
- * Resolution: per-series option wins over chart-level numeric value.
488
- * Chart-level wins only when its category is explicitly `false` — that's
489
- * a hard disable that overrides per-series too.
643
+ * Animation control. `true` / omitted uses built-in defaults; `false`
644
+ * disables every category. Per-series options on `<LineSeries>` /
645
+ * `<CandlestickSeries>` / `<BarSeries>` override these chart-level
646
+ * defaults unless the category here is explicitly `false`.
490
647
  *
491
- * Shorthands:
492
- * - `true` / omitted — built-in defaults (every settling animation 250 ms,
493
- * pulse cycle 600 ms, input ease 0 / off).
494
- * - `false` disables every animation category.
495
- * - `{ points: false }` / `{ viewport: false }` disables a category.
496
- *
497
- * Runtime updates: changing this prop after mount calls
498
- * `chart.setAnimations(...)` so the new durations take effect on the next
499
- * animation / render.
648
+ * **Init-only by reference identity.** A new `animations` object
649
+ * recreates the underlying `ChartInstance` (and its canvas). Wrap the
650
+ * value in `useMemo(() => ({...}), [deps])` so an unstable parent
651
+ * render doesn't tear down the chart every commit. In dev mode the
652
+ * container emits a console warning when it detects >3 recreates / s.
500
653
  */
501
654
  animations?: boolean | AnimationsConfig;
502
655
  /**
@@ -541,41 +694,58 @@ declare interface ChartEvents {
541
694
  * subscribe to this instead of stacking multiple listeners.
542
695
  */
543
696
  overlayChange: () => void;
697
+ /**
698
+ * Fired once per main-layer frame *while* an axis tick is still in the
699
+ * middle of its fade-in/out animation. DOM axis components (`<TimeAxis>`,
700
+ * `<YAxis>`) listen to this to re-read the per-tick opacity from
701
+ * `timeScale.tickTracker` / `yScale.tickTracker` so the DOM labels and
702
+ * canvas grid lines fade in lockstep. Stops firing as soon as every
703
+ * tracked tick has reached its target opacity.
704
+ */
705
+ tickFrame: () => void;
544
706
  }
545
707
 
546
708
  /**
547
709
  * Core chart controller. Manages series, viewport, scales, and rendering.
548
710
  * Create one per chart container and call {@link destroy} on unmount.
549
711
  */
550
- export declare class ChartInstance extends EventEmitter<ChartEvents> {
712
+ export declare class ChartInstance extends EventEmitter<ChartEvents> implements PanZoomTarget {
551
713
  #private;
552
714
  /** Maps time values to horizontal pixel coordinates. */
553
- readonly timeScale: TimeScale;
715
+ readonly timeScale: XScale;
554
716
  /** Maps price/value to vertical pixel coordinates. */
555
717
  readonly yScale: YScale;
556
718
  get yAxisWidth(): number;
557
719
  get xAxisHeight(): number;
558
720
  constructor(container: HTMLElement, options?: ChartOptions);
559
- /** Add a candlestick (OHLC) series and return its unique ID. */
560
- addCandlestickSeries(options?: Partial<CandlestickSeriesOptions & {
721
+ /**
722
+ * Add a series of the given `type` and return its unique ID. `options` is
723
+ * typed per `type` via the overloads. `layers` (line/bar) and `id` are
724
+ * consumed here; everything else is forwarded to the renderer.
725
+ */
726
+ addSeries(type: 'candlestick', options?: Partial<CandlestickSeriesOptions & {
561
727
  id?: string;
562
728
  }>): string;
563
- /** Add a line series and return its unique ID. */
564
- addLineSeries(options?: Partial<LineSeriesOptions & {
729
+ addSeries(type: 'line', options?: Partial<LineSeriesOptions & {
565
730
  layers?: number;
566
731
  id?: string;
567
732
  }>): string;
568
- /** Add a bar series and return its unique ID. */
569
- addBarSeries(options?: Partial<BarSeriesOptions & {
733
+ addSeries(type: 'bar', options?: Partial<BarSeriesOptions & {
570
734
  layers?: number;
571
735
  id?: string;
572
736
  }>): string;
573
- /** Add a pie/donut series. Set `innerRadiusRatio > 0` for donut. */
574
- addPieSeries(options?: Partial<PieSeriesOptions & {
737
+ addSeries(type: 'pie', options?: Partial<PieSeriesOptions & {
575
738
  id?: string;
576
739
  }>): string;
577
740
  /** Remove a series by ID and clean up its resources. */
578
741
  removeSeries(id: string): void;
742
+ /**
743
+ * Read-only snapshot of the chart's X / Y viewport animation. The same
744
+ * shape (`xRange`, `yRange`, `animating`) every frame — call this inside
745
+ * the current render pass rather than caching the reference, as the
746
+ * underlying animator mutates between frames.
747
+ */
748
+ getAnimationState(): AnimationState;
579
749
  /**
580
750
  * Replace all data for a series.
581
751
  *
@@ -590,6 +760,20 @@ export declare class ChartInstance extends EventEmitter<ChartEvents> {
590
760
  appendData(id: string, point: OHLCInput | TimePointInput, layerIndex?: number): void;
591
761
  /** Update the last data point of a series in place (e.g. live candle update). */
592
762
  updateData(id: string, point: OHLCInput | TimePointInput, layerIndex?: number): void;
763
+ /**
764
+ * Keep only the most recent `count` points of a series — drop the oldest
765
+ * tail when the series exceeds the cap. Smooth Y-range chase (no snap):
766
+ * unlike {@link setSeriesData}, this does NOT set the bulk-replace snap
767
+ * flag, so streaming windows can roll without per-tick Y jitter.
768
+ *
769
+ * Idempotent: a no-op when the series is already at or below `count`.
770
+ * Use after {@link appendData} in a rolling-window stream:
771
+ * ```ts
772
+ * chart.appendData('feed', point);
773
+ * chart.keepLast('feed', 100);
774
+ * ```
775
+ */
776
+ keepLast(id: string, count: number, layerIndex?: number): void;
593
777
  /** Update visual options (color, width, etc.) for an existing series. */
594
778
  updateSeriesOptions(id: string, options: Partial<CandlestickSeriesOptions> | Partial<LineSeriesOptions> | Partial<BarSeriesOptions> | Partial<PieSeriesOptions>): void;
595
779
  /**
@@ -597,37 +781,71 @@ export declare class ChartInstance extends EventEmitter<ChartEvents> {
597
781
  * inside `fn` still flush the batch so counters don't leak across calls.
598
782
  */
599
783
  batch(fn: () => void): void;
600
- /** Show or hide a series. Hidden series are not rendered and excluded from Y-range. */
784
+ /** Show or hide a series. The series cross-fades over
785
+ * `animations.toggle`; the Y range re-fits on the same schedule
786
+ * (one-shot duration override) so the fade and the axis adjustment
787
+ * finish on the same frame. Hidden series are excluded from Y-range
788
+ * computation immediately so the axis can start moving while the line
789
+ * fades out in parallel.
790
+ */
601
791
  setSeriesVisible(seriesId: string, visible: boolean): void;
792
+ /**
793
+ * Whether streaming ticks slide the visible range to track the data tail.
794
+ * Flipped off by a pan that pushes the tail off screen; re-engaged when
795
+ * a follow-up pan brings the tail back into the destination window or
796
+ * when {@link fitContent} / {@link setVisibleRange} commits a range
797
+ * containing the tail.
798
+ */
799
+ getAutoScroll(): boolean;
602
800
  isSeriesVisible(seriesId: string): boolean;
603
- /** Show or hide a specific layer within a multi-layer series. */
801
+ /**
802
+ * Show or hide a specific layer within a multi-layer series.
803
+ *
804
+ * Symmetric with {@link setSeriesVisible}: drives both the renderer's
805
+ * per-layer alpha fade and the engine's Y re-fit through `toggleMs` so the
806
+ * line fade and the axis adjustment finish on the same frame. Hidden layers
807
+ * are excluded from Y-range computation immediately (via the renderer's
808
+ * `getValueRange` reading `store.isVisible()`); the alpha fade keeps the
809
+ * layer's geometry on screen until the engine's Y ease settles.
810
+ */
604
811
  setLayerVisible(seriesId: string, layerIndex: number, visible: boolean): void;
605
812
  isLayerVisible(seriesId: string, layerIndex: number): boolean;
606
813
  /** Auto-fit the viewport to show all data across every series. */
607
814
  fitContent(): void;
608
- getVisibleRange(): VisibleRange;
815
+ getVisibleRange(): XRange;
609
816
  /**
610
817
  * Return the full span of registered data, or `null` before any data has
611
- * arrived. `{ from: dataStart, to: dataEnd }` mirrors the viewport's own
818
+ * arrived. `{ from: dataStart, to: dataEnd }` mirrors the chart's own
612
819
  * tracking and is cheaper than recomputing from series stores.
613
820
  */
614
- getDataRange(): VisibleRange | null;
821
+ getDataRange(): XRange | null;
615
822
  /**
616
- * Imperatively set the visible time range.
823
+ * Imperatively set the visible time range. Three forms:
617
824
  *
618
- * Two forms:
619
- * - `{ from, to }` explicit millisecond range. Cancels any in-flight
620
- * animation and applies immediately. Auto-scroll stays on only if the
621
- * tail is inside the new range (mirrors pan semantics).
622
- * - `number N` shorthand for "show the last N bars from the tail".
623
- * Resolved against the current data bounds and data interval; keeps
624
- * auto-scroll on so streaming ticks continue to track the tail.
625
- * No-op if data hasn't loaded yet.
825
+ * - `number N` — show the last N bars from the data tail (anchor right).
826
+ * Resolved against current data bounds and data interval; keeps
827
+ * auto-scroll on. No-op if data hasn't loaded yet.
828
+ * - `{ from, to }` — explicit time range. `from`/`to` accept either
829
+ * epoch milliseconds or `Date`. Cancels any in-flight animation and
830
+ * applies immediately. Auto-scroll stays on only if the tail is
831
+ * inside the new range (mirrors pan semantics).
832
+ * - `{ from, bars }` — anchor the visible window at `from` and extend
833
+ * right by `bars` data intervals. Useful for warm-up windows where
834
+ * seed points sit on the left and a stream fills the gap on the
835
+ * right. `from` accepts a `Date` too.
626
836
  *
627
837
  * Typical use: on mount, zoom to the last N bars while keeping the full
628
838
  * buffer available for pan-back history inspection.
629
- */
630
- setVisibleRange(range: VisibleRange | number): void;
839
+ *
840
+ * `opts.gesture` (explicit `{ from, to }` form only) eases X and Y on the
841
+ * same fast, velocity-matched profile a user pan/zoom uses instead of the
842
+ * default programmatic settle (snap X, sticky-Y ease). Use it for
843
+ * multi-chart sync so a synced pane tracks the originating gesture in
844
+ * lockstep rather than lagging behind on the long sticky contract.
845
+ */
846
+ setVisibleRange(spec: VisibleRangeSpec, opts?: {
847
+ gesture?: boolean;
848
+ }): void;
631
849
  getYRange(): YRange;
632
850
  getCrosshairPosition(): CrosshairPosition | null;
633
851
  /**
@@ -683,11 +901,6 @@ export declare class ChartInstance extends EventEmitter<ChartEvents> {
683
901
  color: string;
684
902
  }[] | null;
685
903
  getSeriesIds(): string[];
686
- /**
687
- * Type of a registered series, or `null` for unknown ids. `'pie'` for
688
- * `PieRenderer`; everything else is a time-series (`'time'`).
689
- */
690
- getSeriesType(seriesId: string): 'pie' | 'time' | null;
691
904
  /**
692
905
  * Filter `getSeriesIds()` by renderer type. `'pie'` returns pie series,
693
906
  * `'time'` returns line/bar/candlestick.
@@ -766,25 +979,6 @@ export declare class ChartInstance extends EventEmitter<ChartEvents> {
766
979
  * a wrapper re-applies padding reactively (e.g. in response to a Title /
767
980
  * InfoBar ResizeObserver).
768
981
  */
769
- /**
770
- * Replace the chart-level animation configuration at runtime. Updates the
771
- * resolved durations and propagates them to every dependent subsystem:
772
- *
773
- * - viewport: reboundMs (next rebound), inputResponseMs (next pan/zoom).
774
- * - per-frame: yAxisMs (next render).
775
- * - existing series: only the **hard-disable** signal (0 / false) is pushed
776
- * into renderers — that's the documented contract on
777
- * {@link AnimationsConfig.points}, "chart-level `false` wins over per-
778
- * series". Numeric chart-level changes update the default for *new*
779
- * series but leave existing per-series overrides intact, which avoids
780
- * silently clobbering custom values on every prop update from a React
781
- * wrapper.
782
- *
783
- * In-flight animations are NOT cancelled — the new durations apply to the
784
- * next animation that fires. To force-snap the current animation, call
785
- * the relevant API explicitly (e.g. `setRange`) afterwards.
786
- */
787
- setAnimations(animations: ChartOptions['animations']): void;
788
982
  setPadding(padding: ChartOptions['padding']): void;
789
983
  /** Show or hide the background grid. Takes effect on the next render frame. */
790
984
  setGrid(grid: {
@@ -802,9 +996,13 @@ export declare class ChartInstance extends EventEmitter<ChartEvents> {
802
996
  setEdgeState(side: EdgeSide, state: EdgeState): void;
803
997
  /** Read the current host-declared state for a given edge. */
804
998
  getEdgeState(side: EdgeSide): EdgeState;
805
- /** Notify chart that a YLabel is present (affects right padding). */
806
- setYLabel(has: boolean): void;
807
- private updateViewportPadding;
999
+ /**
1000
+ * Notify chart that a YLabel overlay is mounted / unmounted. Currently a
1001
+ * no-op — placeholder for the right-padding reflow that will adjust the
1002
+ * chart area to make room for the badge.
1003
+ * TODO: implement reflow.
1004
+ */
1005
+ setYLabel(_has: boolean): void;
808
1006
  /** Tear down the chart: cancel animations, remove listeners, and detach the canvas. */
809
1007
  destroy(): void;
810
1008
  /** The attached performance monitor, or `null` when instrumentation is disabled. */
@@ -814,39 +1012,42 @@ export declare class ChartInstance extends EventEmitter<ChartEvents> {
814
1012
  private onDataChanged;
815
1013
  private updateDataInterval;
816
1014
  /**
817
- * Sample data inside `targetVisible` and return the unbounded [min, max] of
818
- * the visible series, or `null` when nothing is in view. Bounds are NOT
819
- * applied here the caller composes them via {@link resolveBound}.
1015
+ * Shift the visible time range by `timeDelta` ms. Hard-clamped so at
1016
+ * least one data point stays on screen no rubber-band, no overshoot.
1017
+ * A pan that moves the viewport off the live-tail position flips
1018
+ * autoscroll off. Fires `edgeReached` whenever the clamp trims the
1019
+ * gesture (i.e., the user pushed past the boundary), so consumers can
1020
+ * trigger history loads. Forwards the committed target to the engine
1021
+ * as a gesture for visual easing. Public so consumers can drive pan
1022
+ * programmatically.
1023
+ */
1024
+ pan(timeDelta: number, chartWidth?: number): void;
1025
+ /**
1026
+ * Zoom around a time anchor. `factor < 1` zooms in, `> 1` zooms out.
1027
+ * In historical mode the window anchors to `centerTime` (cursor-driven).
1028
+ * Zoom-out is hard-capped at the padded data span. Forwards the committed
1029
+ * target to the engine as a gesture for visual easing. Public so consumers
1030
+ * can drive zoom programmatically.
820
1031
  *
821
- * `targetVisible` is the X *destination* (logicalRange), not the animating
822
- * current. Sampling against a moving X would make Y chase a definition of
823
- * "in view" that shifts every frame; passing the destination explicitly
824
- * keeps Y on a stable target so X and Y converge together.
1032
+ * Sticky-follow: while `#autoScroll` is on, the new window is repositioned
1033
+ * so its right edge sits at `dataEnd + paddingRight` (follow position).
1034
+ * Span (zoom level) from `computeZoom` is preserved; only the position
1035
+ * is locked to the tail. This avoids the next-tick `computeStreamingTarget`
1036
+ * offset clamp (which would otherwise slide X left to the follow position
1037
+ * and produce a visible jump). Cursor-anchored zoom is intentionally
1038
+ * sacrificed in follow-live mode — pan first to inspect history.
825
1039
  */
826
- private computeTargetYRange;
827
- private updateYRange;
828
- /** Resolve an {@link AxisBound} to a concrete numeric value. */
829
- private resolveBound;
1040
+ zoomAt(centerTime: number, factor: number, chartWidth?: number): void;
830
1041
  /**
831
1042
  * Lightweight scale sync: updates timeScale/yScale from current viewport state
832
1043
  * without advancing the Y smoothing animation. Called from the viewport 'change'
833
- * handler so DOM axis components always read fresh coordinates on re-render.
1044
+ * handler, so DOM axis components always read fresh coordinates on re-render.
834
1045
  */
835
1046
  private syncScales;
836
- private updateScales;
837
1047
  /** Expensive: background, grid, all series. Only on data/viewport/resize change. */
838
1048
  private renderMain;
839
1049
  /** Cheap overlay: crosshair, nearest-point dots, pulse animation, edge indicator. */
840
1050
  private renderOverlay;
841
- private drawEdgeIndicators;
842
- /**
843
- * Pick the boundary time to anchor the edge indicator at. Prefer the
844
- * cached value emitted by the most recent `edgeReached` — that's the
845
- * *exact* point the user overshot. Fall back to the current data edge
846
- * when no gesture has fired yet (host might invoke `setEdgeState`
847
- * directly on mount to show a "no-data" marker from the start).
848
- */
849
- private resolveEdgeBoundary;
850
1051
  }
851
1052
 
852
1053
  /** Layout metrics describing the chart area, Y axis, and time axis sizes. */
@@ -856,6 +1057,7 @@ export declare interface ChartLayout {
856
1057
  xAxisHeight: number;
857
1058
  }
858
1059
 
1060
+ /** Options passed when creating a new ChartInstance. */
859
1061
  export declare interface ChartOptions {
860
1062
  theme?: ChartTheme;
861
1063
  axis?: AxisConfig;
@@ -866,12 +1068,35 @@ export declare interface ChartOptions {
866
1068
  padding?: {
867
1069
  top?: number;
868
1070
  bottom?: number;
869
- right?: number | {
870
- intervals: number;
871
- };
872
- left?: number | {
873
- intervals: number;
874
- };
1071
+ right?: HorizontalPadding;
1072
+ left?: HorizontalPadding;
1073
+ };
1074
+ /**
1075
+ * Viewport-level streaming behavior.
1076
+ */
1077
+ viewport?: {
1078
+ /**
1079
+ * Maximum number of data bars (candles/points) the viewport will fit
1080
+ * before it stops growing and switches to tail-scroll. While the data
1081
+ * span is below this threshold, streaming ticks expand the right edge
1082
+ * to absorb new points; once the span exceeds it, the visible window
1083
+ * holds at this width and slides forward as new data arrives.
1084
+ * Default: 200. Values below 2 are clamped.
1085
+ */
1086
+ maxVisibleBars?: number;
1087
+ /**
1088
+ * Initial visible range applied **before** the first paint after data
1089
+ * arrives. Same shape as `ChartInstance.setVisibleRange` (a bar count,
1090
+ * an explicit `{from, to}` window, or a `{from, bars}` warm-up pair).
1091
+ * Calling `setVisibleRange` after mount via `useEffect` runs post-paint
1092
+ * and visually re-zooms the chart on the next frame; this option folds
1093
+ * the same intent into the first render so the very first paint
1094
+ * already shows the requested window.
1095
+ *
1096
+ * One-shot — consumed by the first `onDataChanged` call that has data,
1097
+ * then cleared. Subsequent `setSeriesData` calls don't re-apply it.
1098
+ */
1099
+ initialRange?: VisibleRangeSpec;
875
1100
  };
876
1101
  /** Enable zoom, pan, and crosshair interactions. Defaults to true. */
877
1102
  interactive?: boolean;
@@ -880,17 +1105,16 @@ export declare interface ChartOptions {
880
1105
  visible: boolean;
881
1106
  };
882
1107
  /**
883
- * Animation control. Split into `points` (data-series animations) and
884
- * `viewport` (pan/zoom rebound + Y-axis smoothing). See
885
- * {@link AnimationsConfig} for the full shape and defaults.
1108
+ * Animation control. Grouped as `axis: { y, x, ticks }` (axis-side
1109
+ * behaviour), `toggle` (series visibility — alpha + Y refit), and
1110
+ * `series.{line,candlestick,bar,pie}` (per-series-type data tweens).
1111
+ * See {@link AnimationsConfig} for the full shape and defaults.
886
1112
  *
887
1113
  * Shorthands:
888
1114
  * - `animations: true` (or omitted) uses built-in defaults.
889
1115
  * - `animations: false` disables every animation category.
890
- * - `animations: { points: false }` disables all data-series animations.
891
- * - `animations: { viewport: false }` disables rebound + Y-axis smoothing.
892
1116
  *
893
- * Per-series options (`enterMs`, `smoothMs`, etc.) override chart-level
1117
+ * Per-series options (`entryMs`, `smoothMs`, etc.) override chart-level
894
1118
  * defaults unless the category is explicitly `false` — then the chart-
895
1119
  * level gate wins.
896
1120
  */
@@ -899,7 +1123,7 @@ export declare interface ChartOptions {
899
1123
  * Invoked after the user releases a pan/zoom gesture that pulled the
900
1124
  * viewport past a data edge by more than 10% of the visible range. Hosts
901
1125
  * typically respond by prefetching more history and calling
902
- * {@link ChartInstance.setEdgeState} to show a spinner or "no more data"
1126
+ * `ChartInstance.setEdgeState` to show a spinner or "no more data"
903
1127
  * indicator at the corresponding edge.
904
1128
  */
905
1129
  onEdgeReached?: (info: EdgeReachedInfo) => void;
@@ -1131,15 +1355,6 @@ export declare interface CrosshairPosition {
1131
1355
  y: number;
1132
1356
  }
1133
1357
 
1134
- /**
1135
- * @deprecated Use a named preset instead — `catppuccin.theme` is the new
1136
- * default dark palette, and `dracula`, `nightOwl`, `oneDarkPro`, `gruvbox`,
1137
- * `monokaiPro`, `materialPalenight`, or `panda` cover the rest of the
1138
- * dark spectrum. `darkTheme` is kept as an alias for the previous default
1139
- * and will be removed in a future major release.
1140
- */
1141
- export declare const darkTheme: ChartTheme;
1142
-
1143
1358
  export declare function detectInterval(times: number[]): number;
1144
1359
 
1145
1360
  export declare const dracula: ThemePreset;
@@ -1271,8 +1486,25 @@ export declare const gruvbox: ThemePreset;
1271
1486
 
1272
1487
  export declare const handwritten: ThemePreset;
1273
1488
 
1489
+ /**
1490
+ * Velocity-matched cubic Hermite Y transition. Direction-aware: per-call
1491
+ * `expandMs` (outward, default 250 ms via the engine) and `contractMs`
1492
+ * (inward, default 2500 ms — sticky-Y) are supplied through `retarget()`.
1493
+ *
1494
+ * Default Y transition. Bounded duration — after the supplied settle each
1495
+ * side lands exactly at the target with zero velocity, unlike spring's
1496
+ * asymptotic approach. Predictable for animations whose end-state matters
1497
+ * (axis labels can settle in a known time).
1498
+ */
1499
+ export declare function hermite(): TransitionFactory<YRange>;
1500
+
1274
1501
  export declare const highContrast: ThemePreset;
1275
1502
 
1503
+ /** Horizontal padding expressed as a fixed pixel offset or a number of data intervals. */
1504
+ declare type HorizontalPadding = number | {
1505
+ intervals: number;
1506
+ };
1507
+
1276
1508
  /** Shape returned by {@link SeriesRenderer.getHoverInfo} / per-slice entries of {@link SeriesRenderer.getSliceInfo}. */
1277
1509
  export declare interface HoverInfo {
1278
1510
  label: string;
@@ -1318,17 +1550,6 @@ export declare interface InfoBarRenderContext {
1318
1550
  readonly isHover: boolean;
1319
1551
  }
1320
1552
 
1321
- /**
1322
- * `true` when the given `#rrggbb` background reads as dark — Rec. 601 luma
1323
- * below the half-byte threshold (`< 128`). Used internally by
1324
- * {@link createTheme} to flip default palette/contrast picks, and exported
1325
- * for callers (docs UI chrome, host apps) that want to branch on theme
1326
- * polarity without carrying their own copy of the heuristic.
1327
- *
1328
- * Falls through (`false`) for non-hex inputs (e.g. `transparent`,
1329
- * `rgba(...)`, gradients) — the built-in presets all use plain hex on the
1330
- * top-level `background`, so this is fine for the vast majority of cases.
1331
- */
1332
1553
  export declare function isDarkBg(bg: string): boolean;
1333
1554
 
1334
1555
  export declare const lavenderMist: ThemePreset;
@@ -1413,18 +1634,6 @@ declare interface LegendRenderContext {
1413
1634
 
1414
1635
  export declare const lightPink: ThemePreset;
1415
1636
 
1416
- /**
1417
- * @deprecated Use a named light preset instead — `quietLight`, `githubLight`,
1418
- * `solarizedLight`, `minimalLight`, `peachCream`, `sandDune`, `lightPink`,
1419
- * `lavenderMist`, `mintBreeze`, `rosePineDawn`, or `handwritten`. `lightTheme`
1420
- * is kept as an alias for the previous default and will be removed in a
1421
- * future major release.
1422
- */
1423
- export declare const lightTheme: ChartTheme;
1424
-
1425
- /** @deprecated Use {@link TimePoint} instead. */
1426
- export declare type LineData = TimePoint;
1427
-
1428
1637
  /**
1429
1638
  * Entrance animation styles for new line points.
1430
1639
  * - `'none'` — new segments appear instantly.
@@ -1462,14 +1671,14 @@ export declare interface LineSeriesOptions {
1462
1671
  * <LineSeries options={{ pulseMs: 1200 }} data={data} />
1463
1672
  *
1464
1673
  * // Chart-level default:
1465
- * <ChartContainer animations={{ points: { pulseMs: 1200 } }}>
1674
+ * <ChartContainer animations={{ series: { line: { pulse: 1200 } } }}>
1466
1675
  * <LineSeries data={data} />
1467
1676
  * </ChartContainer>
1468
1677
  * ```
1469
1678
  *
1470
1679
  * `false` or `0` turns the halo off entirely (drawing and animation loop).
1471
- * A chart-level `animations.points: false` is a hard disable that wins
1472
- * over this field.
1680
+ * A chart-level `animations.series.line: false` is a hard disable that
1681
+ * wins over this field.
1473
1682
  */
1474
1683
  pulseMs?: number | false;
1475
1684
  /** Stacking mode. Default: 'off'. */
@@ -1482,8 +1691,6 @@ export declare interface LineSeriesOptions {
1482
1691
  * @see entryMs — cross-linked duration for this animation.
1483
1692
  */
1484
1693
  entryAnimation?: LineEntryAnimation;
1485
- /** @deprecated Use {@link entryAnimation} instead. */
1486
- enterAnimation?: LineEntryAnimation;
1487
1694
  /**
1488
1695
  * Per-point entrance duration in milliseconds. Default: `250`.
1489
1696
  *
@@ -1491,24 +1698,19 @@ export declare interface LineSeriesOptions {
1491
1698
  * // Override for one series:
1492
1699
  * <LineSeries options={{ entryMs: 600 }} data={data} />
1493
1700
  *
1494
- * // Or set the default for every series at once:
1495
- * <ChartContainer animations={{ points: { enterMs: 600 } }}>
1701
+ * // Or set the default for every line series at once:
1702
+ * <ChartContainer animations={{ series: { line: { entry: 600 } } }}>
1496
1703
  * <LineSeries data={data} />
1497
1704
  * </ChartContainer>
1498
1705
  * ```
1499
1706
  *
1500
- * Note: chart-level uses `enterMs`, per-series uses `entryMs` — same
1501
- * animation, two names for historical reasons.
1502
- *
1503
1707
  * `false` or `0` disables the entrance (equivalent to
1504
- * `entryAnimation: 'none'`). A chart-level `animations.points: false` is a
1505
- * hard disable that wins over this field.
1708
+ * `entryAnimation: 'none'`). A chart-level `animations.series.line: false`
1709
+ * is a hard disable that wins over this field.
1506
1710
  *
1507
1711
  * @see LineSeriesOptions.entryAnimation
1508
1712
  */
1509
1713
  entryMs?: number | false;
1510
- /** @deprecated Use {@link entryMs} instead. */
1511
- enterMs?: number | false;
1512
1714
  /**
1513
1715
  * How long the displayed last value takes to catch up to the actual one
1514
1716
  * on every `updateLastPoint`. Default: `250` ms.
@@ -1518,13 +1720,13 @@ export declare interface LineSeriesOptions {
1518
1720
  * <LineSeries options={{ smoothMs: 100 }} data={data} />
1519
1721
  *
1520
1722
  * // Chart-level default:
1521
- * <ChartContainer animations={{ points: { smoothMs: 100 } }}>
1723
+ * <ChartContainer animations={{ series: { line: { smooth: 100 } } }}>
1522
1724
  * <LineSeries data={data} />
1523
1725
  * </ChartContainer>
1524
1726
  * ```
1525
1727
  *
1526
1728
  * `0` or `false` snaps the displayed value to the target on every tick
1527
- * (no smoothing). A chart-level `animations.points: false` is a hard
1729
+ * (no smoothing). A chart-level `animations.series.line: false` is a hard
1528
1730
  * disable that wins over this field.
1529
1731
  */
1530
1732
  smoothMs?: number | false;
@@ -1543,6 +1745,15 @@ declare type Listener = (...args: any[]) => void;
1543
1745
 
1544
1746
  export declare const materialPalenight: ThemePreset;
1545
1747
 
1748
+ /**
1749
+ * Time helpers for the animation config surface. The public config accepts
1750
+ * three shapes: a number (already milliseconds), a string with an explicit
1751
+ * unit (`"250ms"`, `"1s"`, `"2.5s"`), or `false` to disable. The resolver
1752
+ * parses each input once at chart construction; the hot render path then
1753
+ * only ever sees numbers.
1754
+ */
1755
+ declare type Milliseconds = number;
1756
+
1546
1757
  export declare const minimalLight: ThemePreset;
1547
1758
 
1548
1759
  export declare const mintBreeze: ThemePreset;
@@ -1703,6 +1914,32 @@ export declare const oneDarkPro: ThemePreset;
1703
1914
 
1704
1915
  export declare const panda: ThemePreset;
1705
1916
 
1917
+ /**
1918
+ * Narrow interface the interaction handlers expect from their host. Lets
1919
+ * `InteractionHandler` / `PanHandler` / `ZoomHandler` drive pan / zoom
1920
+ * without depending on the full ChartInstance surface.
1921
+ */
1922
+ declare interface PanZoomTarget {
1923
+ /**
1924
+ * Shift the visible time range by `timeDelta` ms. `chartWidth` is used
1925
+ * by pixel-padding resolution; passing the current canvas width yields
1926
+ * correct rubber-band clamps.
1927
+ */
1928
+ pan(timeDelta: number, chartWidth?: number): void;
1929
+ /**
1930
+ * Zoom around a time anchor. `factor < 1` zooms in, `> 1` zooms out.
1931
+ * `chartWidth` participates in soft-bound math the same way as in `pan`.
1932
+ */
1933
+ zoomAt(centerTime: number, factor: number, chartWidth?: number): void;
1934
+ }
1935
+
1936
+ /**
1937
+ * Parse an {@link AnimationTime} to milliseconds. Invalid strings throw at
1938
+ * config-resolution time, so a typo surfaces at startup, not silently as zero
1939
+ * animation later. Used once per field in `resolveAnimationsConfig`.
1940
+ */
1941
+ export declare function parseAnimationTime(value: AnimationTime): Milliseconds;
1942
+
1706
1943
  export declare const peachCream: ThemePreset;
1707
1944
 
1708
1945
  /**
@@ -1785,8 +2022,6 @@ declare interface PerfStats {
1785
2022
  mainRendersPerSec: number;
1786
2023
  /** Same as {@link mainRendersPerSec} but for the overlay (crosshair + pulse) layer. */
1787
2024
  overlayRendersPerSec: number;
1788
- /** @deprecated Alias for {@link mainRendersPerSec}. Kept for older consumers. */
1789
- fps: number;
1790
2025
  mainFrameMs: FrameTimingSample;
1791
2026
  overlayFrameMs: FrameTimingSample;
1792
2027
  /** Total frames recorded per layer. Lets consumers distinguish "no data yet" from "drew once and ms is 0". */
@@ -1848,13 +2083,6 @@ declare interface PieLabelsOptions {
1848
2083
  * expressed as a multiplier of {@link fontSize}. Default: 1.8.
1849
2084
  */
1850
2085
  labelGap?: number;
1851
- /**
1852
- * @deprecated No effect in the radial per-slice layout. A label's X is
1853
- * pinned to its slice midangle, so forcing the "other" side would flip
1854
- * text direction without moving the label and push text across the pie.
1855
- * Kept in the option surface for backwards compatibility.
1856
- */
1857
- balanceSides?: boolean;
1858
2086
  }
1859
2087
 
1860
2088
  export declare function PieLegend({ seriesId, mode: modeProp, format, position, children }: PieLegendProps): JSX_2.Element | null;
@@ -2040,6 +2268,30 @@ export declare function resolveAxisTextColor(theme: ChartTheme, axis?: 'x' | 'y'
2040
2268
  */
2041
2269
  export declare function resolveCandlestickBodyColor(body: string | [string, string]): string;
2042
2270
 
2271
+ /**
2272
+ * Per-call settle times supplied by the engine to {@link Transition.retarget}.
2273
+ * The engine owns all durations and pushes them per call; the curves carry
2274
+ * no baseline of their own.
2275
+ *
2276
+ * X transitions use only `expandMs` (single direction). Y transitions use
2277
+ * both `expandMs` (outward — new extreme enters window) and `contractMs`
2278
+ * (inward — extreme leaves; the sticky-Y mechanism).
2279
+ */
2280
+ declare interface RetargetOptions {
2281
+ /** Optional wall clock. Defaults to `performance.now()`. */
2282
+ now?: number;
2283
+ /**
2284
+ * Settle time (ms) for *outward* motion — bound reaching a new extreme.
2285
+ * Also used by X (single-direction) as the spring's settle time.
2286
+ */
2287
+ expandMs?: number;
2288
+ /**
2289
+ * Settle time (ms) for *inward* motion — bound contracting after an
2290
+ * extreme leaves the window. Y-only; X transitions ignore this field.
2291
+ */
2292
+ contractMs?: number;
2293
+ }
2294
+
2043
2295
  export declare const rosePineDawn: ThemePreset;
2044
2296
 
2045
2297
  export declare const sandDune: ThemePreset;
@@ -2081,16 +2333,47 @@ declare interface Size {
2081
2333
 
2082
2334
  export declare type SliceInfo = HoverInfo;
2083
2335
 
2336
+ /**
2337
+ * Snap factory — no animation, no parameters. Generic so it works for both
2338
+ * Y and X. Exposed so power users can opt out of axis motion without
2339
+ * disabling other animations:
2340
+ *
2341
+ * ```
2342
+ * animations: { axis: { y: { curve: snap() } } }
2343
+ * ```
2344
+ */
2345
+ export declare function snap<T extends YRange | VisibleRange = YRange>(): TransitionFactory<T>;
2346
+
2084
2347
  /** Sort order applied to a snapshot list by the helper. */
2085
2348
  export declare type SnapshotSort = 'none' | 'asc' | 'desc';
2086
2349
 
2087
2350
  export declare const solarizedLight: ThemePreset;
2088
2351
 
2089
- export declare function Sparkline({ data, theme, variant, valuePosition, formatValue, label, sublabel, color, negativeColor, area, areaFill, width, height, strokeWidth, gradient, style, }: SparklineProps): JSX_2.Element;
2352
+ export declare function Sparkline({ data, theme, variant, valuePosition, formatValue, label, sublabel, color, negativeColor, area, areaFill, yRange, flow, width, height, strokeWidth, gradient, style, }: SparklineProps): JSX_2.Element;
2090
2353
 
2091
2354
  export declare interface SparklineProps {
2092
2355
  /** Data points plotted by the sparkline. A flat `TimePoint[]` — the sparkline only ever shows one tiny line/bar. */
2093
2356
  data: TimePoint[];
2357
+ /**
2358
+ * Streaming-window mode: viewport is fixed at `capacity` bars wide. Pass
2359
+ * at least two seed points in `data` so the initial window can infer the
2360
+ * tick interval.
2361
+ *
2362
+ * `align` controls where the seed sits at mount:
2363
+ * - `'right'` *(default)* — seed flush with the right edge; each tick
2364
+ * shifts the viewport left by one interval and the new tick lands at
2365
+ * the right edge.
2366
+ * - `'left'` — seed flush with the left edge; the viewport is held in
2367
+ * place until empty bars on the right are consumed, then normal
2368
+ * tail-scroll resumes.
2369
+ * - `'offscreen'` — seed starts one interval past the right edge so the
2370
+ * first tick's tail-scroll animates it onto canvas (a brief "drive-in"
2371
+ * effect).
2372
+ */
2373
+ flow?: {
2374
+ capacity: number;
2375
+ align?: 'left' | 'right' | 'offscreen';
2376
+ };
2094
2377
  /** Visual theme. Drives series colour, background gradient, and the change-direction colours used in the value block. */
2095
2378
  theme: ChartTheme;
2096
2379
  /** 'line' (default) or 'bar' */
@@ -2114,6 +2397,18 @@ export declare interface SparklineProps {
2114
2397
  };
2115
2398
  /** @deprecated Use {@link area} instead. */
2116
2399
  areaFill?: boolean;
2400
+ /**
2401
+ * Fixed Y-axis bounds. Omit a side (or pass `'auto'`) to keep that edge
2402
+ * auto-scaled. Useful to pin a baseline (`{ min: 0 }`) or hold a stable
2403
+ * window so streaming ticks don't rescale the line.
2404
+ *
2405
+ * Each bound is an {@link AxisBound}: a number, `'auto'`, a percentage
2406
+ * offset string (`'+10%'`), or a `(values) => number` reducer.
2407
+ */
2408
+ yRange?: {
2409
+ min?: AxisBound;
2410
+ max?: AxisBound;
2411
+ };
2117
2412
  /** Chart width (default: 140) */
2118
2413
  width?: number;
2119
2414
  /** Overall height (default: 48) */
@@ -2130,6 +2425,36 @@ export declare type SparklineValuePosition = 'left' | 'right' | 'none';
2130
2425
 
2131
2426
  export declare type SparklineVariant = 'line' | 'bar';
2132
2427
 
2428
+ /**
2429
+ * Critically-damped spring transition factory. Generic over the value type
2430
+ * so the same factory works for both Y (`YRange`) and X (`VisibleRange`).
2431
+ *
2432
+ * The factory dispatches at construction time based on the shape of the
2433
+ * initial value:
2434
+ *
2435
+ * - `{ min, max }` → {@link YRangeSpring} (direction-aware ω: expand vs
2436
+ * contract, used by the sticky-Y mechanism).
2437
+ * - `{ from, to }` → {@link VisibleRangeSpring} (single ω, no direction
2438
+ * asymmetry — X has no analog of sticky-Y).
2439
+ *
2440
+ * Both implementations carry velocity through mid-flight retargets so
2441
+ * wheel-zoom sequences and rapid streaming ticks stay continuous.
2442
+ *
2443
+ * Usage:
2444
+ * ```ts
2445
+ * animations: {
2446
+ * axis: {
2447
+ * y: { curve: spring() },
2448
+ * x: { curve: spring() },
2449
+ * },
2450
+ * }
2451
+ * ```
2452
+ *
2453
+ * The engine is the single source of truth for the per-call durations —
2454
+ * settle times come through `RetargetOptions.expandMs` / `contractMs`.
2455
+ */
2456
+ export declare function spring<T extends YRange | VisibleRange = YRange>(): TransitionFactory<T>;
2457
+
2133
2458
  /** Stacking mode for bar/line series: off (overlap), normal (stacked), percent (100% stacked). */
2134
2459
  export declare type StackingMode = 'off' | 'normal' | 'percent';
2135
2460
 
@@ -2185,6 +2510,36 @@ export declare interface ThemePreset {
2185
2510
 
2186
2511
  export declare const ThemeProvider: Provider<ChartTheme | null>;
2187
2512
 
2513
+ /**
2514
+ * Per-axis tick-set holder with self-managed fade.
2515
+ *
2516
+ * Each tick value gets its own {@link Animator}: entering ticks ramp `0 → 1`,
2517
+ * exiting ticks ramp `1 → 0` and are removed once they settle at zero.
2518
+ * Callers drive the clock with {@link tick} once per frame, then read the
2519
+ * current opacity table via {@link snapshot}.
2520
+ *
2521
+ * Pre-arming (initial mount, dataset swap) the tracker uses duration `0` so
2522
+ * the very first nice-tick sets snap into place without an opening fade —
2523
+ * transient tick churn during layout settling should not look animated.
2524
+ */
2525
+ declare interface TickEntry {
2526
+ /** Tick value (time or price). */
2527
+ readonly value: number;
2528
+ /** Current opacity in [0, 1]. */
2529
+ readonly opacity: number;
2530
+ }
2531
+
2532
+ declare interface TickTrackerSnapshot {
2533
+ /**
2534
+ * Every tick the tracker still considers alive — current ones at their
2535
+ * resolved opacity (1.0 once any pending fade-in settles) and fading-out
2536
+ * ones above 0.
2537
+ */
2538
+ readonly entries: readonly TickEntry[];
2539
+ /** True while at least one entry hasn't reached its target opacity. */
2540
+ readonly isAnimating: boolean;
2541
+ }
2542
+
2188
2543
  declare function TimeAxis({ labelCount, minLabelSpacing }?: TimeAxisProps): JSX_2.Element;
2189
2544
  export { TimeAxis }
2190
2545
  export { TimeAxis as XAxis }
@@ -2207,57 +2562,6 @@ export declare type TimePointInput = Omit<TimePoint, 'time'> & {
2207
2562
  time: TimeValue;
2208
2563
  };
2209
2564
 
2210
- declare class TimeScale {
2211
- private from;
2212
- private to;
2213
- private width;
2214
- private pixelRatio;
2215
- private dataInterval;
2216
- private labelCountHintValue;
2217
- private minSpacingValue;
2218
- private resolvedInterval;
2219
- private lastInterval;
2220
- /**
2221
- * "want" (desired interval post-floor) at the time lastInterval was snapped.
2222
- * Hysteresis band anchors to this so micro range drift back across a tier
2223
- * boundary doesn't flip the label density — mirrors YScale, see there for
2224
- * the full reasoning.
2225
- */
2226
- private lastWant;
2227
- /** Identifies the dataInterval bucket whose tier list drove lastInterval. */
2228
- private lastBucketKey;
2229
- private get labelCountHint();
2230
- private get minLabelSpacing();
2231
- update(range: VisibleRange, mediaWidth: number, pixelRatio: number, dataInterval?: number): void;
2232
- setLabelCount(n: number | null | undefined): void;
2233
- setMinSpacing(px: number | null | undefined): void;
2234
- private resetHysteresis;
2235
- timeToX(time: number): number;
2236
- timeToBitmapX(time: number): number;
2237
- xToTime(x: number): number;
2238
- pixelDeltaToTimeDelta(pixelDelta: number): number;
2239
- barWidthMedia(dataInterval: number): number;
2240
- barWidthBitmap(dataInterval: number): number;
2241
- /**
2242
- * Evenly spaced "nice" tick times. Resolution uses the cached
2243
- * `dataInterval` set by `update()`; callers that bypass `update()` can
2244
- * still pass it here for back-compat.
2245
- */
2246
- niceTickValues(dataInterval: number): {
2247
- ticks: number[];
2248
- tickInterval: number;
2249
- };
2250
- getRange(): VisibleRange;
2251
- getMediaWidth(): number;
2252
- /**
2253
- * Resolve the next tick interval. Walks `niceTimeIntervals(dataInterval)`
2254
- * looking for the smallest tier ≥ desired spacing; retains the previous
2255
- * tier inside a ratio band so micro pan/zoom doesn't flip label density.
2256
- */
2257
- private resolveInterval;
2258
- private countTicks;
2259
- }
2260
-
2261
2565
  /**
2262
2566
  * Accepted time input: a timestamp in **milliseconds** (like `Date.now()`) or a `Date` object.
2263
2567
  * `Date` values are converted to milliseconds internally via `Date.getTime()`.
@@ -2281,7 +2585,7 @@ export declare type TimeValue = number | Date;
2281
2585
  * </ChartContainer>
2282
2586
  * ```
2283
2587
  */
2284
- export declare function Title({ children, sub, style }: TitleProps): JSX_2.Element;
2588
+ export declare const Title: NamedExoticComponent<TitleProps>;
2285
2589
 
2286
2590
  /** Props for the {@link Title} component. */
2287
2591
  export declare interface TitleProps {
@@ -2380,6 +2684,57 @@ export declare interface TooltipRenderContext {
2380
2684
  /** Sort order for multi-series tooltip values. */
2381
2685
  export declare type TooltipSort = 'none' | 'asc' | 'desc';
2382
2686
 
2687
+ /**
2688
+ * Smoothing-curve contract. Implementations (`YRangeHermite`, `YRangeSpring`,
2689
+ * `RangeSnap` for Y; `VisibleRangeSpring`, `RangeSnap` for X) drive a value
2690
+ * of type `T` from one snapshot to the next while maintaining `current` /
2691
+ * `target` / velocity continuity.
2692
+ *
2693
+ * Parametric over `T` so X and Y share the same interface — Y uses
2694
+ * `Transition<YRange>` (default), X uses `Transition<VisibleRange>`.
2695
+ *
2696
+ * This is the single public customization point for the chart's curves.
2697
+ * Other animation math (entry tweens, pulse, axis tick fade) stays
2698
+ * engine-fixed.
2699
+ */
2700
+ export declare interface Transition<T> {
2701
+ /** Current sampled position. Read by renderers on every frame. */
2702
+ readonly current: T;
2703
+ /** Active retarget destination. May equal `current` after settle. */
2704
+ readonly target: T;
2705
+ /** True while position has not yet converged to `target`. */
2706
+ readonly animating: boolean;
2707
+ /**
2708
+ * Begin a new transition toward `value`. Existing velocity is carried
2709
+ * over (no reset twitch). The engine supplies the active settle times
2710
+ * via `opts.expandMs` (and `opts.contractMs` for Y).
2711
+ */
2712
+ retarget(value: T, opts?: RetargetOptions): void;
2713
+ /** Land at `value` instantly. Velocity resets to zero. */
2714
+ snap(value: T, opts?: {
2715
+ now?: number;
2716
+ }): void;
2717
+ /** Advance to `now`. Returns `true` while still perceptibly moving. */
2718
+ tick(now: number): boolean;
2719
+ }
2720
+
2721
+ /**
2722
+ * Context handed to a {@link TransitionFactory} at chart construction.
2723
+ * Holds the initial value so the produced transition starts pinned to
2724
+ * the same value the chart will render on its first frame.
2725
+ */
2726
+ export declare interface TransitionContext<T> {
2727
+ initial: T;
2728
+ }
2729
+
2730
+ /**
2731
+ * Factory function returning a fresh {@link Transition}. Pass one as
2732
+ * `animations.axis.y.curve` (or `animations.axis.x.curve`) to plug a custom
2733
+ * smoothing strategy. Built-in factories: {@link hermite} (Y default),
2734
+ * {@link spring} (X default, also valid for Y), {@link snap} (no animation).
2735
+ */
2736
+ export declare type TransitionFactory<T> = (ctx: TransitionContext<T>) => Transition<T>;
2737
+
2383
2738
  /** Font family and base font size used across the chart. */
2384
2739
  export declare interface Typography {
2385
2740
  /** CSS `font-family` stack applied to all text rendered by the chart. */
@@ -2428,11 +2783,26 @@ export declare function useYRange(chart: ChartInstance): YRange;
2428
2783
  /** Signature for a value-to-string formatter (YAxis, YLabel, Pie, Sparkline, NumberFlow). */
2429
2784
  export declare type ValueFormatter = (value: number) => string;
2430
2785
 
2431
- /** Time range (timestamps in milliseconds) of the currently visible portion of the chart. */
2432
- export declare interface VisibleRange {
2433
- from: number;
2434
- to: number;
2435
- }
2786
+ /** @deprecated Use {@link XRange} instead. Visible time range of the chart. */
2787
+ export declare type VisibleRange = XRange;
2788
+
2789
+ /**
2790
+ * Argument accepted by {@link Chart.setVisibleRange}. Three forms:
2791
+ *
2792
+ * - `number N` — show the last N bars from the data tail (anchor right).
2793
+ * - `{ from, to }` — explicit time range; `from`/`to` accept either epoch
2794
+ * milliseconds or `Date`, normalised to ms internally.
2795
+ * - `{ from, bars }` — anchor the visible window at `from` and extend
2796
+ * right by `bars` data intervals. Useful for "show N bars starting
2797
+ * here" patterns (warm-up windows for streaming charts, etc.).
2798
+ */
2799
+ declare type VisibleRangeSpec = number | {
2800
+ from: TimeValue;
2801
+ to: TimeValue;
2802
+ } | {
2803
+ from: TimeValue;
2804
+ bars: number;
2805
+ };
2436
2806
 
2437
2807
  /** Configuration for the X (time) axis. */
2438
2808
  export declare interface XAxisConfig {
@@ -2442,6 +2812,69 @@ export declare interface XAxisConfig {
2442
2812
  visible?: boolean;
2443
2813
  }
2444
2814
 
2815
+ /** Time range (timestamps in milliseconds) of the currently visible portion of the chart. */
2816
+ declare interface XRange {
2817
+ from: number;
2818
+ to: number;
2819
+ }
2820
+
2821
+ declare class XScale {
2822
+ /**
2823
+ * Shared fade state for time ticks. Grid lines (canvas) and DOM tick labels
2824
+ * read opacity from the same tracker, so a tick fading in/out looks
2825
+ * identical on both surfaces.
2826
+ */
2827
+ readonly tickTracker: AxisTickTracker;
2828
+ private from;
2829
+ private to;
2830
+ private width;
2831
+ private pixelRatio;
2832
+ private dataInterval;
2833
+ private labelCountHintValue;
2834
+ private minSpacingValue;
2835
+ private resolvedInterval;
2836
+ private lastInterval;
2837
+ /**
2838
+ * "want" (desired interval post-floor) at the time lastInterval was snapped.
2839
+ * Hysteresis band anchors to this so micro range drift back across a tier
2840
+ * boundary doesn't flip the label density — mirrors YScale, see there for
2841
+ * the full reasoning.
2842
+ */
2843
+ private lastWant;
2844
+ /** Identifies the dataInterval bucket whose tier list drove lastInterval. */
2845
+ private lastBucketKey;
2846
+ private get labelCountHint();
2847
+ private get minLabelSpacing();
2848
+ update(range: XRange, mediaWidth: number, pixelRatio: number, dataInterval?: number): void;
2849
+ setLabelCount(n: number | null | undefined): void;
2850
+ setMinSpacing(px: number | null | undefined): void;
2851
+ private resetHysteresis;
2852
+ timeToX(time: number): number;
2853
+ timeToBitmapX(time: number): number;
2854
+ xToTime(x: number): number;
2855
+ pixelDeltaToTimeDelta(pixelDelta: number): number;
2856
+ barWidthMedia(dataInterval: number): number;
2857
+ barWidthBitmap(dataInterval: number): number;
2858
+ /**
2859
+ * Evenly spaced "nice" tick times. Resolution uses the cached
2860
+ * `dataInterval` set by `update()`; callers that bypass `update()` can
2861
+ * still pass it here for back-compat.
2862
+ */
2863
+ niceTickValues(dataInterval: number): {
2864
+ ticks: number[];
2865
+ tickInterval: number;
2866
+ };
2867
+ getRange(): XRange;
2868
+ getMediaWidth(): number;
2869
+ /**
2870
+ * Resolve the next tick interval. Walks `niceTimeIntervals(dataInterval)`
2871
+ * looking for the smallest tier ≥ desired spacing; retains the previous
2872
+ * tier inside a ratio band so micro pan/zoom doesn't flip label density.
2873
+ */
2874
+ private resolveInterval;
2875
+ private countTicks;
2876
+ }
2877
+
2445
2878
  export declare function YAxis({ format, labelCount, minLabelSpacing }?: YAxisProps): JSX_2.Element;
2446
2879
 
2447
2880
  /** Configuration for the Y axis. */
@@ -2511,7 +2944,10 @@ declare interface YLabelRenderContext {
2511
2944
  readonly format: ValueFormatter;
2512
2945
  }
2513
2946
 
2514
- /** Min/max value range for the Y axis. */
2947
+ /** Value range covered by the chart's Y axis at a given frame. Animated as a
2948
+ * single struct so the upper and lower bounds always move in lockstep — no
2949
+ * half-tween states where the visible domain is wider on one side than the
2950
+ * other for a frame. */
2515
2951
  export declare interface YRange {
2516
2952
  min: number;
2517
2953
  max: number;
@@ -2522,6 +2958,8 @@ export declare interface YRange {
2522
2958
  * Also provides tick generation and value formatting for the Y axis.
2523
2959
  */
2524
2960
  declare class YScale {
2961
+ /** Shared fade state for Y ticks; see {@link TimeScale.tickTracker}. */
2962
+ readonly tickTracker: AxisTickTracker;
2525
2963
  private min;
2526
2964
  private max;
2527
2965
  private height;