react-native-livechart 1.0.0 → 2.0.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/README.md +15 -3
- package/dist/components/CrosshairLine.d.ts +6 -1
- package/dist/components/CrosshairLine.d.ts.map +1 -1
- package/dist/components/CrosshairOverlay.d.ts +7 -1
- package/dist/components/CrosshairOverlay.d.ts.map +1 -1
- package/dist/components/DotOverlay.d.ts +12 -5
- package/dist/components/DotOverlay.d.ts.map +1 -1
- package/dist/components/LiveChart.d.ts.map +1 -1
- package/dist/components/LiveChartSeries.d.ts.map +1 -1
- package/dist/components/LoadingOverlay.d.ts +6 -2
- package/dist/components/LoadingOverlay.d.ts.map +1 -1
- package/dist/components/MarkerOverlay.d.ts +8 -4
- package/dist/components/MarkerOverlay.d.ts.map +1 -1
- package/dist/components/MultiSeriesDots.d.ts +8 -2
- package/dist/components/MultiSeriesDots.d.ts.map +1 -1
- package/dist/components/YAxisOverlay.d.ts +4 -2
- package/dist/components/YAxisOverlay.d.ts.map +1 -1
- package/dist/constants.d.ts +16 -13
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/liveChartEngineTick.d.ts +2 -0
- package/dist/core/liveChartEngineTick.d.ts.map +1 -1
- package/dist/core/liveChartSeriesEngineTick.d.ts +2 -0
- package/dist/core/liveChartSeriesEngineTick.d.ts.map +1 -1
- package/dist/core/resolveConfig.d.ts +34 -4
- package/dist/core/resolveConfig.d.ts.map +1 -1
- package/dist/core/useLiveChartEngine.d.ts +3 -0
- package/dist/core/useLiveChartEngine.d.ts.map +1 -1
- package/dist/core/useLiveChartSeriesEngine.d.ts +3 -0
- package/dist/core/useLiveChartSeriesEngine.d.ts.map +1 -1
- package/dist/draw/candle.d.ts +2 -2
- package/dist/draw/candle.d.ts.map +1 -1
- package/dist/draw/grid.d.ts +2 -1
- package/dist/draw/grid.d.ts.map +1 -1
- package/dist/draw/line.d.ts +8 -8
- package/dist/draw/line.d.ts.map +1 -1
- package/dist/draw/markerAtlas.d.ts +44 -0
- package/dist/draw/markerAtlas.d.ts.map +1 -0
- package/dist/hooks/crosshairShared.d.ts +12 -4
- package/dist/hooks/crosshairShared.d.ts.map +1 -1
- package/dist/hooks/resolveChartLayout.d.ts +3 -1
- package/dist/hooks/resolveChartLayout.d.ts.map +1 -1
- package/dist/hooks/useBadge.d.ts +2 -2
- package/dist/hooks/useBadge.d.ts.map +1 -1
- package/dist/hooks/useCandlePaths.d.ts +2 -2
- package/dist/hooks/useCandlePaths.d.ts.map +1 -1
- package/dist/hooks/useCrosshair.d.ts +3 -1
- package/dist/hooks/useCrosshair.d.ts.map +1 -1
- package/dist/hooks/useCrosshairSeries.d.ts +3 -1
- package/dist/hooks/useCrosshairSeries.d.ts.map +1 -1
- package/dist/hooks/useYAxis.d.ts +2 -1
- package/dist/hooks/useYAxis.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +136 -5
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/CrosshairLine.tsx +30 -10
- package/src/components/CrosshairOverlay.tsx +31 -10
- package/src/components/DotOverlay.tsx +29 -14
- package/src/components/LiveChart.tsx +67 -15
- package/src/components/LiveChartSeries.tsx +76 -17
- package/src/components/LoadingOverlay.tsx +20 -11
- package/src/components/MarkerOverlay.tsx +119 -164
- package/src/components/MultiSeriesDots.tsx +30 -2
- package/src/components/YAxisOverlay.tsx +8 -4
- package/src/constants.ts +54 -14
- package/src/core/liveChartEngineTick.ts +7 -2
- package/src/core/liveChartSeriesEngineTick.ts +8 -2
- package/src/core/resolveConfig.ts +137 -38
- package/src/core/useLiveChartEngine.ts +6 -0
- package/src/core/useLiveChartSeriesEngine.ts +6 -0
- package/src/draw/candle.ts +11 -6
- package/src/draw/grid.ts +5 -5
- package/src/draw/line.ts +106 -25
- package/src/draw/markerAtlas.ts +304 -0
- package/src/hooks/crosshairShared.ts +24 -3
- package/src/hooks/resolveChartLayout.ts +11 -2
- package/src/hooks/useBadge.ts +38 -24
- package/src/hooks/useCandlePaths.ts +4 -2
- package/src/hooks/useCrosshair.ts +15 -2
- package/src/hooks/useCrosshairSeries.ts +8 -2
- package/src/hooks/useYAxis.ts +4 -1
- package/src/index.ts +7 -0
- package/src/types.ts +147 -5
package/src/hooks/useBadge.ts
CHANGED
|
@@ -6,11 +6,8 @@ import {
|
|
|
6
6
|
type SharedValue,
|
|
7
7
|
} from "react-native-reanimated";
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
BADGE_PILL_PAD_X,
|
|
12
|
-
BADGE_PILL_PAD_Y,
|
|
13
|
-
BADGE_TAIL_LEN,
|
|
9
|
+
BADGE_METRICS_DEFAULTS,
|
|
10
|
+
MOTION_METRICS_DEFAULTS,
|
|
14
11
|
MS_PER_FRAME_60FPS,
|
|
15
12
|
} from "../constants";
|
|
16
13
|
import { measureFontTextWidth } from "../lib/measureFontTextWidth";
|
|
@@ -22,9 +19,12 @@ import {
|
|
|
22
19
|
} from "../draw/line";
|
|
23
20
|
import { hexToRgb, lerpColor } from "../math/color";
|
|
24
21
|
import { lerp } from "../math/lerp";
|
|
25
|
-
import type {
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
import type {
|
|
23
|
+
BadgeMetrics,
|
|
24
|
+
BadgeVariant,
|
|
25
|
+
LiveChartPalette,
|
|
26
|
+
Momentum,
|
|
27
|
+
} from "../types";
|
|
28
28
|
|
|
29
29
|
export function useBadge(
|
|
30
30
|
engine: ChartEngineWithLiveValue,
|
|
@@ -37,6 +37,8 @@ export function useBadge(
|
|
|
37
37
|
momentum?: SharedValue<Momentum>,
|
|
38
38
|
position: "right" | "left" = "right",
|
|
39
39
|
background?: string,
|
|
40
|
+
badgeMetrics: BadgeMetrics = BADGE_METRICS_DEFAULTS,
|
|
41
|
+
badgeColorSpeed: number = MOTION_METRICS_DEFAULTS.badgeColorSpeed,
|
|
40
42
|
) {
|
|
41
43
|
const colorR = useSharedValue(0);
|
|
42
44
|
const colorG = useSharedValue(0);
|
|
@@ -93,7 +95,7 @@ export function useBadge(
|
|
|
93
95
|
const text = formatValue(engine.displayValue.get());
|
|
94
96
|
const textW = measureFontTextWidth(font, text);
|
|
95
97
|
|
|
96
|
-
const pillH = font.getSize() +
|
|
98
|
+
const pillH = font.getSize() + badgeMetrics.padY * 2;
|
|
97
99
|
const r = pillH / 2;
|
|
98
100
|
const badgeY = dotY - pillH / 2;
|
|
99
101
|
let textX: number;
|
|
@@ -101,9 +103,9 @@ export function useBadge(
|
|
|
101
103
|
if (position === "left") {
|
|
102
104
|
// Pill to the left of the live dot; no tail (`showTail` applies to right gutter only).
|
|
103
105
|
const dotXPos = w - padding.right;
|
|
104
|
-
const pillW = 2 *
|
|
105
|
-
const bodyRight = dotXPos -
|
|
106
|
-
const bodyLeft = Math.max(
|
|
106
|
+
const pillW = 2 * badgeMetrics.padX + textW;
|
|
107
|
+
const bodyRight = dotXPos - badgeMetrics.dotGap;
|
|
108
|
+
const bodyLeft = Math.max(badgeMetrics.marginEdge, bodyRight - pillW);
|
|
107
109
|
const pillBodyW = bodyRight - bodyLeft;
|
|
108
110
|
textX = (bodyLeft + bodyRight - textW) / 2;
|
|
109
111
|
path.addRRect({
|
|
@@ -113,15 +115,21 @@ export function useBadge(
|
|
|
113
115
|
});
|
|
114
116
|
} else {
|
|
115
117
|
// Right-gutter badge (default): asymmetric layout with optional tail.
|
|
116
|
-
const tl = badgeTailAndCap(font.getSize(), showTail);
|
|
117
|
-
const bodyLeft = w - padding.right +
|
|
118
|
-
const bodyRight = w -
|
|
118
|
+
const tl = badgeTailAndCap(font.getSize(), showTail, badgeMetrics);
|
|
119
|
+
const bodyLeft = w - padding.right + badgeMetrics.dotGap + tl;
|
|
120
|
+
const bodyRight = w - badgeMetrics.marginEdge;
|
|
119
121
|
const pillW = bodyRight - bodyLeft;
|
|
120
122
|
// Text centered in pill body — same formula used by GridOverlay.
|
|
121
|
-
textX = pillTextLeftX(
|
|
123
|
+
textX = pillTextLeftX(
|
|
124
|
+
w,
|
|
125
|
+
padding.right,
|
|
126
|
+
badgeMetrics.dotGap + tl,
|
|
127
|
+
textW,
|
|
128
|
+
badgeMetrics,
|
|
129
|
+
);
|
|
122
130
|
|
|
123
131
|
if (showTail) {
|
|
124
|
-
const badgeX = w - padding.right +
|
|
132
|
+
const badgeX = w - padding.right + badgeMetrics.dotGap;
|
|
125
133
|
const cx = tl + pillW - r;
|
|
126
134
|
|
|
127
135
|
path.moveTo(badgeX + tl, badgeY);
|
|
@@ -134,17 +142,17 @@ export function useBadge(
|
|
|
134
142
|
);
|
|
135
143
|
path.lineTo(badgeX + tl, badgeY + pillH);
|
|
136
144
|
path.cubicTo(
|
|
137
|
-
badgeX +
|
|
145
|
+
badgeX + badgeMetrics.tailLength + 2,
|
|
138
146
|
badgeY + pillH,
|
|
139
147
|
badgeX + 3,
|
|
140
|
-
badgeY + r +
|
|
148
|
+
badgeY + r + badgeMetrics.tailSpread,
|
|
141
149
|
badgeX,
|
|
142
150
|
badgeY + r,
|
|
143
151
|
);
|
|
144
152
|
path.cubicTo(
|
|
145
153
|
badgeX + 3,
|
|
146
|
-
badgeY + r -
|
|
147
|
-
badgeX +
|
|
154
|
+
badgeY + r - badgeMetrics.tailSpread,
|
|
155
|
+
badgeX + badgeMetrics.tailLength + 2,
|
|
148
156
|
badgeY,
|
|
149
157
|
badgeX + tl,
|
|
150
158
|
badgeY,
|
|
@@ -170,9 +178,15 @@ export function useBadge(
|
|
|
170
178
|
} else if (momentum) {
|
|
171
179
|
const m = momentum.get();
|
|
172
180
|
const targetRgb = m === "up" ? upRgb : m === "down" ? downRgb : accentRgb;
|
|
173
|
-
colorR.set(
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
colorR.set(
|
|
182
|
+
lerp(colorR.get(), targetRgb[0], badgeColorSpeed, MS_PER_FRAME_60FPS),
|
|
183
|
+
);
|
|
184
|
+
colorG.set(
|
|
185
|
+
lerp(colorG.get(), targetRgb[1], badgeColorSpeed, MS_PER_FRAME_60FPS),
|
|
186
|
+
);
|
|
187
|
+
colorB.set(
|
|
188
|
+
lerp(colorB.get(), targetRgb[2], badgeColorSpeed, MS_PER_FRAME_60FPS),
|
|
189
|
+
);
|
|
176
190
|
bgColor = lerpColor(
|
|
177
191
|
[colorR.get(), colorG.get(), colorB.get()],
|
|
178
192
|
[colorR.get(), colorG.get(), colorB.get()],
|
|
@@ -6,12 +6,12 @@ import {
|
|
|
6
6
|
useSharedValue,
|
|
7
7
|
type SharedValue,
|
|
8
8
|
} from "react-native-reanimated";
|
|
9
|
-
import { MS_PER_FRAME_60FPS } from "../constants";
|
|
9
|
+
import { CANDLE_METRICS_DEFAULTS, MS_PER_FRAME_60FPS } from "../constants";
|
|
10
10
|
import type { SingleEngineState } from "../core/useLiveChartEngine";
|
|
11
11
|
import { buildCandleGeometry } from "../draw/candle";
|
|
12
12
|
import type { ChartPadding } from "../draw/line";
|
|
13
13
|
import { lerp } from "../math/lerp";
|
|
14
|
-
import type { CandlePoint } from "../types";
|
|
14
|
+
import type { CandleMetrics, CandlePoint } from "../types";
|
|
15
15
|
|
|
16
16
|
const CANDLE_WIDTH_LERP_SPEED = 0.08;
|
|
17
17
|
|
|
@@ -29,6 +29,7 @@ export function useCandlePaths(
|
|
|
29
29
|
liveCandle: SharedValue<CandlePoint | null> | undefined,
|
|
30
30
|
candleWidthSecs: number,
|
|
31
31
|
active: boolean,
|
|
32
|
+
candleMetrics: CandleMetrics = CANDLE_METRICS_DEFAULTS,
|
|
32
33
|
) {
|
|
33
34
|
const targetCandleWidth = useDerivedValue(() => candleWidthSecs);
|
|
34
35
|
const displayCandleWidth = useSharedValue(candleWidthSecs);
|
|
@@ -92,6 +93,7 @@ export function useCandlePaths(
|
|
|
92
93
|
engine.displayMin.value,
|
|
93
94
|
engine.displayMax.value,
|
|
94
95
|
displayCandleWidth.get(),
|
|
96
|
+
candleMetrics,
|
|
95
97
|
);
|
|
96
98
|
});
|
|
97
99
|
|
|
@@ -55,6 +55,8 @@ export function useCrosshair(
|
|
|
55
55
|
enabled: boolean,
|
|
56
56
|
onScrub?: (point: ScrubPoint | null) => void,
|
|
57
57
|
candleOpts?: CrosshairCandleOpts,
|
|
58
|
+
/** Press-and-hold delay (ms) before scrubbing activates. 0 = immediate. */
|
|
59
|
+
panGestureDelay = 0,
|
|
58
60
|
): CrosshairState {
|
|
59
61
|
const scrubX = useSharedValue(-1);
|
|
60
62
|
const scrubActive = useSharedValue(false);
|
|
@@ -110,6 +112,11 @@ export function useCrosshair(
|
|
|
110
112
|
),
|
|
111
113
|
);
|
|
112
114
|
|
|
115
|
+
// Monospace advance width, measured once per render (not per scrub frame) so
|
|
116
|
+
// the tooltip layout worklet can size text by character count instead of a
|
|
117
|
+
// per-frame Skia measureText.
|
|
118
|
+
const monoCharWidth = font.measureText("0").width;
|
|
119
|
+
|
|
113
120
|
const tooltipLayout = useDerivedValue(() => {
|
|
114
121
|
if (isCandleMode) {
|
|
115
122
|
return computeCandleTooltipLayout(
|
|
@@ -122,6 +129,7 @@ export function useCrosshair(
|
|
|
122
129
|
formatValue,
|
|
123
130
|
formatTime,
|
|
124
131
|
font,
|
|
132
|
+
monoCharWidth,
|
|
125
133
|
);
|
|
126
134
|
}
|
|
127
135
|
return deriveCrosshairTooltipSingle(
|
|
@@ -134,6 +142,7 @@ export function useCrosshair(
|
|
|
134
142
|
formatValue,
|
|
135
143
|
formatTime,
|
|
136
144
|
font,
|
|
145
|
+
monoCharWidth,
|
|
137
146
|
);
|
|
138
147
|
});
|
|
139
148
|
|
|
@@ -207,10 +216,14 @@ export function useCrosshair(
|
|
|
207
216
|
|
|
208
217
|
let gesture = Gesture.Pan()
|
|
209
218
|
.minDistance(Platform.OS === "android" ? 10 : 0)
|
|
210
|
-
.activateAfterLongPress(
|
|
219
|
+
.activateAfterLongPress(panGestureDelay)
|
|
211
220
|
.maxPointers(1)
|
|
212
221
|
.shouldCancelWhenOutside(false)
|
|
213
|
-
|
|
222
|
+
// Start scrubbing on ACTIVE (onStart), not on touch-down (onBegin):
|
|
223
|
+
// `activateAfterLongPress` only delays activation, so onBegin still fires
|
|
224
|
+
// immediately — using it would scrub instantly and ignore panGestureDelay,
|
|
225
|
+
// and leave scrubActive stuck for taps that never reach the long-press.
|
|
226
|
+
.onStart(
|
|
214
227
|
/* istanbul ignore next */ (e) => {
|
|
215
228
|
"worklet";
|
|
216
229
|
if (!enabled) return;
|
|
@@ -28,6 +28,8 @@ export function useCrosshairSeries(
|
|
|
28
28
|
padding: ChartPadding,
|
|
29
29
|
enabled: boolean,
|
|
30
30
|
onScrub?: (point: ScrubPointMulti | null) => void,
|
|
31
|
+
/** Press-and-hold delay (ms) before scrubbing activates. 0 = immediate. */
|
|
32
|
+
panGestureDelay = 0,
|
|
31
33
|
): CrosshairState {
|
|
32
34
|
const scrubX = useSharedValue(-1);
|
|
33
35
|
const scrubActive = useSharedValue(false);
|
|
@@ -121,10 +123,14 @@ export function useCrosshairSeries(
|
|
|
121
123
|
|
|
122
124
|
let gesture = Gesture.Pan()
|
|
123
125
|
.minDistance(Platform.OS === "android" ? 10 : 0)
|
|
124
|
-
.activateAfterLongPress(
|
|
126
|
+
.activateAfterLongPress(panGestureDelay)
|
|
125
127
|
.maxPointers(1)
|
|
126
128
|
.shouldCancelWhenOutside(false)
|
|
127
|
-
|
|
129
|
+
// Start scrubbing on ACTIVE (onStart), not on touch-down (onBegin):
|
|
130
|
+
// `activateAfterLongPress` only delays activation, so onBegin still fires
|
|
131
|
+
// immediately — using it would scrub instantly and ignore panGestureDelay,
|
|
132
|
+
// and leave scrubActive stuck for taps that never reach the long-press.
|
|
133
|
+
.onStart(
|
|
128
134
|
/* istanbul ignore next */ (e) => {
|
|
129
135
|
"worklet";
|
|
130
136
|
if (!enabled) return;
|
package/src/hooks/useYAxis.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { useDerivedValue, useSharedValue } from "react-native-reanimated";
|
|
2
2
|
|
|
3
3
|
import type { SkFont } from "@shopify/react-native-skia";
|
|
4
|
-
import { MS_PER_FRAME_60FPS } from "../constants";
|
|
4
|
+
import { GRID_METRICS_DEFAULTS, MS_PER_FRAME_60FPS } from "../constants";
|
|
5
5
|
import type { ChartEngineLayout } from "../core/useLiveChartEngine";
|
|
6
6
|
import { computeGridEntries } from "../draw/grid";
|
|
7
7
|
import type { ChartPadding } from "../draw/line";
|
|
8
|
+
import type { GridMetrics } from "../types";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Compute Y-axis grid entries (values + labels) with animated fade-in/out.
|
|
@@ -17,6 +18,7 @@ export function useYAxis(
|
|
|
17
18
|
formatValue: (v: number) => string,
|
|
18
19
|
font: SkFont,
|
|
19
20
|
minGap = 36,
|
|
21
|
+
gridMetrics: GridMetrics = GRID_METRICS_DEFAULTS,
|
|
20
22
|
) {
|
|
21
23
|
const prevInterval = useSharedValue(0);
|
|
22
24
|
const labelAlphas = useSharedValue<Record<number, number>>({});
|
|
@@ -36,6 +38,7 @@ export function useYAxis(
|
|
|
36
38
|
formatValue,
|
|
37
39
|
dt,
|
|
38
40
|
minGap,
|
|
41
|
+
gridMetrics,
|
|
39
42
|
);
|
|
40
43
|
|
|
41
44
|
prevInterval.set(result.interval);
|
package/src/index.ts
CHANGED
|
@@ -29,20 +29,26 @@ export { useTradeStream } from "./hooks/useTradeStream";
|
|
|
29
29
|
|
|
30
30
|
export type {
|
|
31
31
|
BadgeConfig,
|
|
32
|
+
BadgeMetrics,
|
|
32
33
|
BadgeVariant,
|
|
34
|
+
CandleMetrics,
|
|
33
35
|
CandlePoint,
|
|
34
36
|
ChartInsets,
|
|
35
37
|
DegenOptions,
|
|
36
38
|
DegenShakePayload,
|
|
39
|
+
EmptyStateMetrics,
|
|
37
40
|
FontConfig,
|
|
38
41
|
FontWeight,
|
|
39
42
|
GradientConfig,
|
|
43
|
+
GridMetrics,
|
|
40
44
|
GridStyleConfig,
|
|
41
45
|
LeftEdgeFadeConfig,
|
|
42
46
|
LegendConfig,
|
|
43
47
|
LegendStyle,
|
|
44
48
|
LineConfig,
|
|
45
49
|
LiveChartCoreProps,
|
|
50
|
+
LiveChartMetrics,
|
|
51
|
+
LiveChartMetricsOverride,
|
|
46
52
|
LiveChartPalette,
|
|
47
53
|
LiveChartPoint,
|
|
48
54
|
LiveChartProps,
|
|
@@ -52,6 +58,7 @@ export type {
|
|
|
52
58
|
MarkerKind,
|
|
53
59
|
Momentum,
|
|
54
60
|
MomentumConfig,
|
|
61
|
+
MotionMetrics,
|
|
55
62
|
MultiSeriesDotConfig,
|
|
56
63
|
PulseConfig,
|
|
57
64
|
ReferenceLine,
|
package/src/types.ts
CHANGED
|
@@ -203,6 +203,14 @@ export interface ScrubConfig {
|
|
|
203
203
|
tooltipColor?: string;
|
|
204
204
|
/** Tooltip pill border color. Omit to use theme `tooltipBorder`. */
|
|
205
205
|
tooltipBorderColor?: string;
|
|
206
|
+
/**
|
|
207
|
+
* Press-and-hold delay in milliseconds before scrubbing activates — think of
|
|
208
|
+
* it as "press and hold to scrub." During the delay the pan is not captured,
|
|
209
|
+
* so a quick horizontal swipe falls through to a parent gesture (e.g. a
|
|
210
|
+
* navigator's swipe-back-to-previous-route). `0` = scrub immediately on drag.
|
|
211
|
+
* Default `0`.
|
|
212
|
+
*/
|
|
213
|
+
panGestureDelay?: number;
|
|
206
214
|
}
|
|
207
215
|
|
|
208
216
|
/** Left-edge fade — soft erase so the chart blends into the left gutter (web liveline parity). */
|
|
@@ -392,10 +400,43 @@ export interface DegenShakePayload {
|
|
|
392
400
|
direction: "up" | "down";
|
|
393
401
|
}
|
|
394
402
|
|
|
395
|
-
/**
|
|
396
|
-
|
|
397
|
-
|
|
403
|
+
/** Contrasting outer ring drawn behind each series dot — the "haloed" look the
|
|
404
|
+
* single-series live dot has, so dots stand out against the lines and one
|
|
405
|
+
* another. */
|
|
406
|
+
export interface DotRingConfig {
|
|
407
|
+
/** Ring color. Default: theme `badgeOuterBg` (a near-background halo). */
|
|
408
|
+
color?: string;
|
|
409
|
+
/** Ring thickness in pixels — how far the halo extends past the dot. Default `2.5`. */
|
|
410
|
+
width?: number;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Shared live-dot styling, used by both `LiveChart` (`dot`) and
|
|
415
|
+
* `LiveChartSeries` (`dot`, which extends this). A dot is a color-filled circle
|
|
416
|
+
* of `radius` with an optional contrasting outer `ring` (halo).
|
|
417
|
+
*/
|
|
418
|
+
export interface DotConfig {
|
|
419
|
+
/** Radius of the (color-filled) dot in pixels. Default `3.5`. */
|
|
398
420
|
radius?: number;
|
|
421
|
+
/**
|
|
422
|
+
* Contrasting outer ring (halo) behind the dot, so it reads clearly against
|
|
423
|
+
* the line(s). `true` = defaults, `false` = a flat circle, or pass
|
|
424
|
+
* `DotRingConfig`. Default `true`.
|
|
425
|
+
*/
|
|
426
|
+
ring?: boolean | DotRingConfig;
|
|
427
|
+
/**
|
|
428
|
+
* Show the dot. `false` hides it (line, badge, and labels still render). Default `true`.
|
|
429
|
+
*
|
|
430
|
+
* @deprecated Pass `dot={false}` to hide the dot — the uniform `boolean | Config`
|
|
431
|
+
* convention. `show` still works and is equivalent to `dot={{ show: false }}`.
|
|
432
|
+
*/
|
|
433
|
+
show?: boolean;
|
|
434
|
+
/** Dot fill color. Defaults to the chart line color (per series for multi-series). */
|
|
435
|
+
color?: string;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/** Live dot configuration for multi-series charts (extends the shared {@link DotConfig}). */
|
|
439
|
+
export interface MultiSeriesDotConfig extends DotConfig {
|
|
399
440
|
/** Pulsing ring animation on each series dot. `true` = defaults, or pass `PulseConfig`. Default `true`. */
|
|
400
441
|
pulse?: boolean | PulseConfig;
|
|
401
442
|
/** Horizontal dashed line at each series' live value. `true` = defaults, or pass `ValueLineConfig`. Default `false`. */
|
|
@@ -513,6 +554,92 @@ export interface CandlePoint {
|
|
|
513
554
|
close: number;
|
|
514
555
|
}
|
|
515
556
|
|
|
557
|
+
// ── Metrics (sizing & motion tokens) ─────────────────────────────────────────
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Spatial sizing & motion tokens — the geometry/timing analogue of
|
|
561
|
+
* {@link LiveChartPalette}. Where `palette` controls *color*, `metrics` controls
|
|
562
|
+
* *shape* (badge geometry, candle bounds) and *feel* (fade/lerp speeds). Override
|
|
563
|
+
* via the `metrics` prop; only the namespaces/keys you set are replaced.
|
|
564
|
+
*/
|
|
565
|
+
export interface LiveChartMetrics {
|
|
566
|
+
/** Value-badge pill geometry. */
|
|
567
|
+
badge: BadgeMetrics;
|
|
568
|
+
/** Candlestick body/wick geometry. */
|
|
569
|
+
candle: CandleMetrics;
|
|
570
|
+
/** Grid + axis-label fade animation. */
|
|
571
|
+
grid: GridMetrics;
|
|
572
|
+
/** Per-frame lerp speeds for value/color transitions. */
|
|
573
|
+
motion: MotionMetrics;
|
|
574
|
+
/** Empty-state (no-data) layout. */
|
|
575
|
+
emptyState: EmptyStateMetrics;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/** Value-badge pill geometry (the "metrics" analogue of badge colors). */
|
|
579
|
+
export interface BadgeMetrics {
|
|
580
|
+
/** Horizontal padding inside the pill, each side of the label. Default `10`. */
|
|
581
|
+
padX: number;
|
|
582
|
+
/** Vertical padding above and below the label. Default `3`. */
|
|
583
|
+
padY: number;
|
|
584
|
+
/** Length of the pointed tail toward the live dot. Default `5`. */
|
|
585
|
+
tailLength: number;
|
|
586
|
+
/** Gap between the pill's outer edge and the canvas edge. Default `4`. */
|
|
587
|
+
marginEdge: number;
|
|
588
|
+
/** Gap between the live dot and the badge tail tip. Default `12`. */
|
|
589
|
+
dotGap: number;
|
|
590
|
+
/** Vertical spread of the tail curve's control points. Default `2.5`. */
|
|
591
|
+
tailSpread: number;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/** Candlestick body/wick geometry. */
|
|
595
|
+
export interface CandleMetrics {
|
|
596
|
+
/** Minimum candle body height in pixels (so dojis stay visible). Default `1`. */
|
|
597
|
+
minBodyPx: number;
|
|
598
|
+
/** Maximum candle body width in pixels. Default `40`. */
|
|
599
|
+
maxBodyPx: number;
|
|
600
|
+
/** Body width as a fraction of the per-candle slot width (0–1). Default `0.8`. */
|
|
601
|
+
bodyWidthRatio: number;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/** Grid-line and axis-label fade animation speeds. */
|
|
605
|
+
export interface GridMetrics {
|
|
606
|
+
/** Per-frame alpha lerp speed when a grid line / label fades in. Default `0.18`. */
|
|
607
|
+
fadeInSpeed: number;
|
|
608
|
+
/** Per-frame alpha lerp speed when a grid line / label fades out. Default `0.12`. */
|
|
609
|
+
fadeOutSpeed: number;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/** Per-frame lerp speeds for value/color transitions. */
|
|
613
|
+
export interface MotionMetrics {
|
|
614
|
+
/** Per-frame lerp speed for the badge background color transition. Default `0.08`. */
|
|
615
|
+
badgeColorSpeed: number;
|
|
616
|
+
/** Extra catch-up speed added to `smoothing` when the live value lags its target. Default `0.12`. */
|
|
617
|
+
adaptiveSpeedBoost: number;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** Empty-state (no-data) layout. */
|
|
621
|
+
export interface EmptyStateMetrics {
|
|
622
|
+
/** Opacity of the empty-state label. Default `0.35`. */
|
|
623
|
+
labelOpacity: number;
|
|
624
|
+
/** Half-padding (px) around the empty text for the squiggle "gap" erase band. Default `20`. */
|
|
625
|
+
gapPad: number;
|
|
626
|
+
/** Horizontal fade width (px) on each side of the empty-text gap. Default `30`. */
|
|
627
|
+
gapFadeWidth: number;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Caller-supplied `metrics` override — every namespace and field is optional, and
|
|
632
|
+
* only the keys you set replace the resolved default (per-namespace shallow merge,
|
|
633
|
+
* same model as `palette`).
|
|
634
|
+
*/
|
|
635
|
+
export interface LiveChartMetricsOverride {
|
|
636
|
+
badge?: Partial<BadgeMetrics>;
|
|
637
|
+
candle?: Partial<CandleMetrics>;
|
|
638
|
+
grid?: Partial<GridMetrics>;
|
|
639
|
+
motion?: Partial<MotionMetrics>;
|
|
640
|
+
emptyState?: Partial<EmptyStateMetrics>;
|
|
641
|
+
}
|
|
642
|
+
|
|
516
643
|
// ── Component Props ──────────────────────────────────────────────────────────
|
|
517
644
|
|
|
518
645
|
/** Props shared between `LiveChart` and `LiveChartSeries`. */
|
|
@@ -586,6 +713,12 @@ export interface LiveChartCoreProps {
|
|
|
586
713
|
* `accentColor` + `theme`. Only the keys you set are replaced.
|
|
587
714
|
*/
|
|
588
715
|
palette?: Partial<LiveChartPalette>;
|
|
716
|
+
/**
|
|
717
|
+
* Override sizing & motion tokens — the geometry/feel analogue of `palette`.
|
|
718
|
+
* Namespaced (`badge`, `candle`, `grid`, `motion`, `emptyState`); only the keys
|
|
719
|
+
* you set are replaced. See {@link LiveChartMetrics}.
|
|
720
|
+
*/
|
|
721
|
+
metrics?: LiveChartMetricsOverride;
|
|
589
722
|
/**
|
|
590
723
|
* Right-edge buffer as a fraction of `timeWindow`. Pushes the live edge past the
|
|
591
724
|
* current time so the latest point has breathing room; pass `0` to land the latest
|
|
@@ -629,6 +762,11 @@ export interface LiveChartProps extends LiveChartCoreProps {
|
|
|
629
762
|
momentum?: boolean | Momentum | MomentumConfig;
|
|
630
763
|
/** Pulsing ring animation on the live dot. `true` = defaults, or pass `PulseConfig`. Default `true`. */
|
|
631
764
|
pulse?: boolean | PulseConfig;
|
|
765
|
+
/**
|
|
766
|
+
* Live dot styling. `true`/omitted = shown defaults, `false` = hidden, or pass
|
|
767
|
+
* `DotConfig` (`radius`, `ring` halo, `color`). See {@link DotConfig}. Default `true`.
|
|
768
|
+
*/
|
|
769
|
+
dot?: boolean | DotConfig;
|
|
632
770
|
/** Horizontal dashed line at the current live value. `true` = defaults, or pass `ValueLineConfig`. */
|
|
633
771
|
valueLine?: boolean | ValueLineConfig;
|
|
634
772
|
/** Render the live value as a large text overlay in the top-left. Default `false`. */
|
|
@@ -673,8 +811,12 @@ export interface LiveChartSeriesProps extends LiveChartCoreProps {
|
|
|
673
811
|
series: SharedValue<SeriesConfig[]>;
|
|
674
812
|
/** Called when a series toggle chip is tapped. */
|
|
675
813
|
onSeriesToggle?: (id: string, visible: boolean) => void;
|
|
676
|
-
/**
|
|
677
|
-
|
|
814
|
+
/**
|
|
815
|
+
* Per-series live dot configuration. `true`/omitted = shown defaults, `false` =
|
|
816
|
+
* hidden, or pass `MultiSeriesDotConfig` (radius, pulse, value line, inline
|
|
817
|
+
* labels). Default `true`.
|
|
818
|
+
*/
|
|
819
|
+
dot?: boolean | MultiSeriesDotConfig;
|
|
678
820
|
/** Legend (toggle chips) configuration. `true` = defaults, `false` = hidden, or pass `LegendConfig`. Default `true`. */
|
|
679
821
|
legend?: boolean | LegendConfig;
|
|
680
822
|
/**
|