@wick-charts/react 0.3.2 → 0.3.4
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/README.md +18 -164
- package/dist/index.cjs +2 -2
- package/dist/index.d.ts +248 -67
- package/dist/index.js +2945 -2631
- package/package.json +2 -2
- package/src/ChartContainer.tsx +49 -1
- package/src/index.ts +1 -0
- package/src/ui/TimeAxis.tsx +6 -3
- package/src/ui/YAxis.tsx +4 -2
- package/src/ui/axisFade.ts +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wick-charts/react",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "High-performance canvas timeseries charts for React — candlestick, line, bar, pie. Tree-shakeable, zero runtime deps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"react-dom": ">=18.0.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@wick-charts/core": "^0.3.
|
|
52
|
+
"@wick-charts/core": "^0.3.4"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "vite build"
|
package/src/ChartContainer.tsx
CHANGED
|
@@ -11,7 +11,13 @@ import {
|
|
|
11
11
|
useState,
|
|
12
12
|
} from 'react';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
type AnimationsConfig,
|
|
16
|
+
type AxisConfig,
|
|
17
|
+
ChartInstance,
|
|
18
|
+
type ChartOptions,
|
|
19
|
+
type ChartTheme,
|
|
20
|
+
} from '@wick-charts/core';
|
|
15
21
|
|
|
16
22
|
type PerfOption = NonNullable<ChartOptions['perf']>;
|
|
17
23
|
|
|
@@ -74,6 +80,36 @@ export interface ChartContainerProps {
|
|
|
74
80
|
* the title. The chart background still spans the full container.
|
|
75
81
|
*/
|
|
76
82
|
headerLayout?: 'overlay' | 'inline';
|
|
83
|
+
/**
|
|
84
|
+
* Chart-level animation configuration. See {@link AnimationsConfig} for the
|
|
85
|
+
* full shape.
|
|
86
|
+
*
|
|
87
|
+
* Two layers — remember which is which:
|
|
88
|
+
*
|
|
89
|
+
* - **Chart-level (this prop)** — `animations.points.{enterMs, smoothMs,
|
|
90
|
+
* pulseMs}` and `animations.viewport.{reboundMs, yAxisMs,
|
|
91
|
+
* inputResponseMs}`. Acts as the default for every series.
|
|
92
|
+
* - **Per-series** — `<LineSeries options={{ entryMs, smoothMs, pulseMs }}>`
|
|
93
|
+
* (and the analogous CandlestickSeries / BarSeries options). Overrides
|
|
94
|
+
* the chart-level default for that one series. Note the spelling:
|
|
95
|
+
* `entryMs` per-series, `enterMs` chart-level — historical artefact,
|
|
96
|
+
* both refer to the same animation.
|
|
97
|
+
*
|
|
98
|
+
* Resolution: per-series option wins over chart-level numeric value.
|
|
99
|
+
* Chart-level wins only when its category is explicitly `false` — that's
|
|
100
|
+
* a hard disable that overrides per-series too.
|
|
101
|
+
*
|
|
102
|
+
* Shorthands:
|
|
103
|
+
* - `true` / omitted — built-in defaults (every settling animation 250 ms,
|
|
104
|
+
* pulse cycle 600 ms, input ease 0 / off).
|
|
105
|
+
* - `false` — disables every animation category.
|
|
106
|
+
* - `{ points: false }` / `{ viewport: false }` — disables a category.
|
|
107
|
+
*
|
|
108
|
+
* Runtime updates: changing this prop after mount calls
|
|
109
|
+
* `chart.setAnimations(...)` so the new durations take effect on the next
|
|
110
|
+
* animation / render.
|
|
111
|
+
*/
|
|
112
|
+
animations?: boolean | AnimationsConfig;
|
|
77
113
|
/**
|
|
78
114
|
* Enable runtime performance instrumentation. Off by default.
|
|
79
115
|
*
|
|
@@ -183,6 +219,7 @@ export function ChartContainer({
|
|
|
183
219
|
grid,
|
|
184
220
|
headerLayout = 'overlay',
|
|
185
221
|
perf,
|
|
222
|
+
animations,
|
|
186
223
|
style,
|
|
187
224
|
className,
|
|
188
225
|
}: ChartContainerProps) {
|
|
@@ -208,6 +245,7 @@ export function ChartContainer({
|
|
|
208
245
|
if (interactive !== undefined) options.interactive = interactive;
|
|
209
246
|
if (grid !== undefined) options.grid = grid;
|
|
210
247
|
if (perfRef.current !== undefined) options.perf = perfRef.current;
|
|
248
|
+
if (animations !== undefined) options.animations = animations;
|
|
211
249
|
chartRef.current = new ChartInstance(containerRef.current, options);
|
|
212
250
|
|
|
213
251
|
// Note: the init path above already propagated `grid` into the chart. The
|
|
@@ -241,6 +279,16 @@ export function ChartContainer({
|
|
|
241
279
|
}
|
|
242
280
|
}, [axis?.y?.width, axis?.y?.min, axis?.y?.max, axis?.y?.visible, axis?.x?.height, axis?.x?.visible]);
|
|
243
281
|
|
|
282
|
+
useEffect(() => {
|
|
283
|
+
if (chartRef.current && animations !== undefined) {
|
|
284
|
+
chartRef.current.setAnimations(animations);
|
|
285
|
+
}
|
|
286
|
+
// Dep array is the JSON shape of the config — covers both the boolean
|
|
287
|
+
// shorthand and the full object. Cheap to stringify (the object is tiny)
|
|
288
|
+
// and lets callers pass a fresh reference each render without thrashing
|
|
289
|
+
// animator state when nothing has actually changed.
|
|
290
|
+
}, [JSON.stringify(animations)]);
|
|
291
|
+
|
|
244
292
|
// Top-overlay height (title + info bar) — measured below. Declared here so
|
|
245
293
|
// the padding effect can fold it into `padding.top`.
|
|
246
294
|
const topOverlayRef = useRef<HTMLDivElement>(null);
|
package/src/index.ts
CHANGED
package/src/ui/TimeAxis.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { formatTime, resolveAxisFontSize, resolveAxisTextColor } from '@wick-cha
|
|
|
4
4
|
|
|
5
5
|
import { useChartInstance } from '../context';
|
|
6
6
|
import { useVisibleRange } from '../store-bridge';
|
|
7
|
+
import { AXIS_LABEL_CLEANUP_MS, AXIS_LABEL_FADE_CSS } from './axisFade';
|
|
7
8
|
|
|
8
9
|
interface TrackedTick {
|
|
9
10
|
opacity: number;
|
|
@@ -61,9 +62,11 @@ export function TimeAxis({ labelCount, minLabelSpacing }: TimeAxisProps = {}) {
|
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
// Clean up ticks that have finished fading
|
|
65
|
+
// Clean up ticks that have finished fading. Buffer = AXIS_LABEL_FADE_MS + 250
|
|
66
|
+
// (one transition + a frame margin) so the DOM node sticks around past the
|
|
67
|
+
// visible fade.
|
|
65
68
|
for (const [t, entry] of map) {
|
|
66
|
-
if (entry.opacity === 0 && entry.fadedAt !== undefined && now - entry.fadedAt >
|
|
69
|
+
if (entry.opacity === 0 && entry.fadedAt !== undefined && now - entry.fadedAt > AXIS_LABEL_CLEANUP_MS) {
|
|
67
70
|
map.delete(t);
|
|
68
71
|
}
|
|
69
72
|
}
|
|
@@ -99,7 +102,7 @@ export function TimeAxis({ labelCount, minLabelSpacing }: TimeAxisProps = {}) {
|
|
|
99
102
|
userSelect: 'none',
|
|
100
103
|
whiteSpace: 'nowrap',
|
|
101
104
|
opacity: entry.opacity,
|
|
102
|
-
transition:
|
|
105
|
+
transition: AXIS_LABEL_FADE_CSS,
|
|
103
106
|
willChange: 'opacity',
|
|
104
107
|
}}
|
|
105
108
|
>
|
package/src/ui/YAxis.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { resolveAxisFontSize, resolveAxisTextColor, type ValueFormatter } from '
|
|
|
4
4
|
|
|
5
5
|
import { useChartInstance } from '../context';
|
|
6
6
|
import { useYRange } from '../store-bridge';
|
|
7
|
+
import { AXIS_LABEL_CLEANUP_MS, AXIS_LABEL_FADE_CSS } from './axisFade';
|
|
7
8
|
|
|
8
9
|
interface TrackedTick {
|
|
9
10
|
opacity: number;
|
|
@@ -74,8 +75,9 @@ export function YAxis({ format, labelCount, minLabelSpacing }: YAxisProps = {})
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
// Cleanup buffer matches the shared AXIS_LABEL_CLEANUP_MS — see axisFade.ts.
|
|
77
79
|
for (const [p, entry] of map) {
|
|
78
|
-
if (entry.opacity === 0 && entry.fadedAt !== undefined && now - entry.fadedAt >
|
|
80
|
+
if (entry.opacity === 0 && entry.fadedAt !== undefined && now - entry.fadedAt > AXIS_LABEL_CLEANUP_MS) {
|
|
79
81
|
map.delete(p);
|
|
80
82
|
}
|
|
81
83
|
}
|
|
@@ -109,7 +111,7 @@ export function YAxis({ format, labelCount, minLabelSpacing }: YAxisProps = {})
|
|
|
109
111
|
fontVariantNumeric: 'tabular-nums',
|
|
110
112
|
userSelect: 'none',
|
|
111
113
|
opacity: entry.opacity,
|
|
112
|
-
transition:
|
|
114
|
+
transition: AXIS_LABEL_FADE_CSS,
|
|
113
115
|
willChange: 'opacity',
|
|
114
116
|
}}
|
|
115
117
|
>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Axis-label fade timing — shared between {@link TimeAxis} and {@link YAxis}.
|
|
3
|
+
*
|
|
4
|
+
* The fade is a pure CSS opacity transition, not Animator-driven, because the
|
|
5
|
+
* label set itself is rebuilt on every render: a tick that "leaves" the
|
|
6
|
+
* range becomes a separate DOM node fading out while a new node fades in,
|
|
7
|
+
* and inline `transition` is the cheapest way to crossfade them without a
|
|
8
|
+
* per-tick Animator instance.
|
|
9
|
+
*
|
|
10
|
+
* The duration matches the chart-level `DEFAULT_ENTER_MS` / `streamTick` so
|
|
11
|
+
* label transitions land in lockstep with the X re-fit, Y range chase, and
|
|
12
|
+
* series live-track. Cleanup buffer leaves the node mounted past the
|
|
13
|
+
* visible fade so React doesn't unmount it mid-transition.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const AXIS_LABEL_FADE_MS = 250;
|
|
17
|
+
|
|
18
|
+
/** Inline `style.transition` value the axis label spans use. */
|
|
19
|
+
export const AXIS_LABEL_FADE_CSS = `opacity ${AXIS_LABEL_FADE_MS / 1000}s ease`;
|
|
20
|
+
|
|
21
|
+
/** Time after which a faded-out tick can be dropped from the persistent map.
|
|
22
|
+
* `2 * AXIS_LABEL_FADE_MS` — one transition plus a frame margin. */
|
|
23
|
+
export const AXIS_LABEL_CLEANUP_MS = AXIS_LABEL_FADE_MS * 2;
|