react-native-livechart 4.11.0 → 4.13.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/components/CrosshairLine.d.ts +12 -2
- package/dist/components/CrosshairLine.d.ts.map +1 -1
- package/dist/components/CrosshairOverlay.d.ts +12 -2
- package/dist/components/CrosshairOverlay.d.ts.map +1 -1
- package/dist/components/CustomReferenceLineOverlay.d.ts +15 -10
- package/dist/components/CustomReferenceLineOverlay.d.ts.map +1 -1
- package/dist/components/CustomTooltipOverlay.d.ts +5 -2
- package/dist/components/CustomTooltipOverlay.d.ts.map +1 -1
- package/dist/components/DotOverlay.d.ts +3 -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/PerSeriesTooltipOverlay.d.ts +21 -0
- package/dist/components/PerSeriesTooltipOverlay.d.ts.map +1 -0
- package/dist/components/ReferenceLineOverlay.d.ts +27 -6
- package/dist/components/ReferenceLineOverlay.d.ts.map +1 -1
- package/dist/components/YAxisOverlay.d.ts +8 -2
- package/dist/components/YAxisOverlay.d.ts.map +1 -1
- package/dist/core/liveIndicatorVisibility.d.ts +9 -0
- package/dist/core/liveIndicatorVisibility.d.ts.map +1 -0
- package/dist/core/resolveConfig.d.ts +45 -0
- package/dist/core/resolveConfig.d.ts.map +1 -1
- package/dist/draw/grid.d.ts +13 -0
- package/dist/draw/grid.d.ts.map +1 -1
- package/dist/hooks/crosshairSeries.d.ts +10 -2
- package/dist/hooks/crosshairSeries.d.ts.map +1 -1
- package/dist/hooks/crosshairShared.d.ts +55 -5
- package/dist/hooks/crosshairShared.d.ts.map +1 -1
- package/dist/hooks/delayedPanGuard.d.ts +66 -8
- package/dist/hooks/delayedPanGuard.d.ts.map +1 -1
- package/dist/hooks/useChartPaths.d.ts +5 -9
- package/dist/hooks/useChartPaths.d.ts.map +1 -1
- package/dist/hooks/useCrosshair.d.ts +13 -1
- package/dist/hooks/useCrosshair.d.ts.map +1 -1
- package/dist/hooks/useCrosshairSeries.d.ts +27 -3
- package/dist/hooks/useCrosshairSeries.d.ts.map +1 -1
- package/dist/hooks/useCrosshairVisibleOpacity.d.ts +8 -0
- package/dist/hooks/useCrosshairVisibleOpacity.d.ts.map +1 -0
- package/dist/hooks/useMomentum.d.ts +1 -1
- package/dist/hooks/useMomentum.d.ts.map +1 -1
- package/dist/hooks/usePanScroll.d.ts +15 -1
- package/dist/hooks/usePanScroll.d.ts.map +1 -1
- package/dist/hooks/useReferenceLine.d.ts +10 -1
- package/dist/hooks/useReferenceLine.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/math/lerp.d.ts.map +1 -1
- package/dist/math/momentum.d.ts +1 -1
- package/dist/math/momentum.d.ts.map +1 -1
- package/dist/math/referenceLines.d.ts +7 -0
- package/dist/math/referenceLines.d.ts.map +1 -1
- package/dist/types.d.ts +207 -18
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/CrosshairLine.tsx +39 -8
- package/src/components/CrosshairOverlay.tsx +49 -15
- package/src/components/CustomReferenceLineOverlay.tsx +42 -13
- package/src/components/CustomTooltipOverlay.tsx +15 -3
- package/src/components/DotOverlay.tsx +24 -8
- package/src/components/LiveChart.tsx +182 -39
- package/src/components/LiveChartSeries.tsx +195 -16
- package/src/components/PerSeriesTooltipOverlay.tsx +290 -0
- package/src/components/ReferenceLineOverlay.tsx +288 -141
- package/src/components/YAxisOverlay.tsx +39 -10
- package/src/core/liveIndicatorVisibility.ts +25 -0
- package/src/core/resolveConfig.ts +115 -0
- package/src/draw/grid.ts +33 -0
- package/src/hooks/crosshairSeries.ts +262 -33
- package/src/hooks/crosshairShared.ts +117 -6
- package/src/hooks/delayedPanGuard.ts +111 -6
- package/src/hooks/useChartPaths.ts +25 -15
- package/src/hooks/useCrosshair.ts +103 -32
- package/src/hooks/useCrosshairSeries.ts +126 -11
- package/src/hooks/useCrosshairVisibleOpacity.ts +57 -0
- package/src/hooks/useMomentum.ts +8 -1
- package/src/hooks/usePanScroll.ts +46 -5
- package/src/hooks/useReferenceLine.ts +26 -1
- package/src/index.ts +1 -0
- package/src/math/lerp.ts +7 -0
- package/src/math/momentum.ts +22 -5
- package/src/math/referenceLines.ts +38 -0
- package/src/types.ts +215 -18
|
@@ -58,7 +58,11 @@ import {
|
|
|
58
58
|
resolveYAxis,
|
|
59
59
|
} from "../core/resolveConfig";
|
|
60
60
|
import type { ResolvedThresholdConfig } from "../core/resolveConfig";
|
|
61
|
-
import {
|
|
61
|
+
import {
|
|
62
|
+
liveIndicatorScrollOpacity,
|
|
63
|
+
resolveHideLiveOnScrollBack,
|
|
64
|
+
} from "../core/liveIndicatorVisibility";
|
|
65
|
+
import { resolveSegment, type ResolvedSegment } from "../core/resolveSegment";
|
|
62
66
|
import { useLiveChartEngine } from "../core/useLiveChartEngine";
|
|
63
67
|
import { pulseRadialOutset } from "../draw/line";
|
|
64
68
|
import { resolveChartLayout } from "../hooks/resolveChartLayout";
|
|
@@ -78,7 +82,10 @@ import { useMarkers } from "../hooks/useMarkers";
|
|
|
78
82
|
import { useReferenceDrag } from "../hooks/useReferenceDrag";
|
|
79
83
|
import { useReferenceLinePress } from "../hooks/useReferenceLinePress";
|
|
80
84
|
import { useModeBlend } from "../hooks/useModeBlend";
|
|
81
|
-
import {
|
|
85
|
+
import {
|
|
86
|
+
resolveMomentumProp,
|
|
87
|
+
useMomentum,
|
|
88
|
+
} from "../hooks/useMomentum";
|
|
82
89
|
import { AXIS_GRAB_MIN_PX, usePanScroll } from "../hooks/usePanScroll";
|
|
83
90
|
import { usePinchZoom } from "../hooks/usePinchZoom";
|
|
84
91
|
import { useVisibleRange } from "../hooks/useVisibleRange";
|
|
@@ -105,6 +112,7 @@ import {
|
|
|
105
112
|
import {
|
|
106
113
|
collectReferenceValues,
|
|
107
114
|
referenceLineForm,
|
|
115
|
+
referenceLineReactKeys,
|
|
108
116
|
resolveReferenceGroupBadge,
|
|
109
117
|
} from "../math/referenceLines";
|
|
110
118
|
import {
|
|
@@ -311,6 +319,7 @@ function useLiveChartController({
|
|
|
311
319
|
renderTooltip,
|
|
312
320
|
renderOverlay,
|
|
313
321
|
renderReferenceLine,
|
|
322
|
+
renderOffAxisReferenceLine,
|
|
314
323
|
referenceLineGrouping,
|
|
315
324
|
leftEdgeFade = true,
|
|
316
325
|
|
|
@@ -387,18 +396,18 @@ function useLiveChartController({
|
|
|
387
396
|
const seededRef = useRef<(number | undefined)[]>([]);
|
|
388
397
|
const refValueSig = allRefLines.map((l) => l.value ?? "_").join(",");
|
|
389
398
|
useEffect(() => {
|
|
390
|
-
const active = dragActive.
|
|
391
|
-
const cur = dragValues.
|
|
399
|
+
const active = dragActive.get();
|
|
400
|
+
const cur = dragValues.get();
|
|
392
401
|
const seeded = seededRef.current;
|
|
393
|
-
dragValues.
|
|
402
|
+
dragValues.set(allRefLines.map((l, i) => {
|
|
394
403
|
const prop = l.value ?? 0;
|
|
395
404
|
if (active[i]) return cur[i] ?? prop; // mid-drag → keep the dragged value
|
|
396
405
|
if (l.value !== seeded[i]) return prop; // prop changed → adopt (controlled)
|
|
397
406
|
return cur[i] ?? prop; // unchanged → keep current (uncontrolled persist)
|
|
398
|
-
});
|
|
407
|
+
}));
|
|
399
408
|
seededRef.current = allRefLines.map((l) => l.value);
|
|
400
|
-
if (dragActive.
|
|
401
|
-
dragActive.
|
|
409
|
+
if (dragActive.get().length !== allRefLines.length) {
|
|
410
|
+
dragActive.set(allRefLines.map((_, i) => active[i] ?? false));
|
|
402
411
|
}
|
|
403
412
|
// allRefLines is rebuilt every render; key off the value signature + length.
|
|
404
413
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -410,6 +419,17 @@ function useLiveChartController({
|
|
|
410
419
|
allRefLines,
|
|
411
420
|
renderReferenceLine,
|
|
412
421
|
);
|
|
422
|
+
// An off-axis renderer replaces only the edge-pinned tag. A full custom tag
|
|
423
|
+
// takes precedence for the same line to avoid mounting two native tags.
|
|
424
|
+
const refLineOffAxisCustom = customReferenceLineFlags(
|
|
425
|
+
allRefLines,
|
|
426
|
+
renderOffAxisReferenceLine,
|
|
427
|
+
"off-axis",
|
|
428
|
+
).map((custom, index) => custom && !refLineCustom[index]);
|
|
429
|
+
const refLineKeys = referenceLineReactKeys(allRefLines);
|
|
430
|
+
// RN custom tags report their measured widths here so the Skia connector can
|
|
431
|
+
// start after the native badge instead of the hidden built-in pill.
|
|
432
|
+
const refLineCustomTagWidths = useSharedValue<number[]>([]);
|
|
413
433
|
|
|
414
434
|
// Live Y values of the *draggable* Form-A lines, folded into the engine's
|
|
415
435
|
// axis-range fit so dragging a line toward / past the visible edge expands the
|
|
@@ -421,13 +441,17 @@ function useLiveChartController({
|
|
|
421
441
|
// contributes its live value; a series contributes its window min/max
|
|
422
442
|
// (respecting `extendToNow`), so an off-range break-even expands the axis and
|
|
423
443
|
// stays on-plot like a reference line would.
|
|
424
|
-
const draggableRefIdx =
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
444
|
+
const draggableRefIdx: number[] = [];
|
|
445
|
+
for (let i = 0; i < allRefLines.length; i++) {
|
|
446
|
+
const line = allRefLines[i];
|
|
447
|
+
if (
|
|
448
|
+
line.draggable &&
|
|
449
|
+
!line.excludeFromRange &&
|
|
450
|
+
referenceLineForm(line) === "line"
|
|
451
|
+
) {
|
|
452
|
+
draggableRefIdx.push(i);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
431
455
|
const thresholdInRange = thresholdCfg?.includeInRange === true;
|
|
432
456
|
// Constant benchmark → its live value rides this channel. A series threshold
|
|
433
457
|
// is folded inside the engine tick instead (`thresholdRangePoints`), which
|
|
@@ -572,6 +596,7 @@ function useLiveChartController({
|
|
|
572
596
|
// react-doctor-disable-next-line react-doctor/no-derived-state-effect -- Reanimated: must read the SharedValue off the render path
|
|
573
597
|
useLayoutEffect(() => {
|
|
574
598
|
// react-doctor-disable-next-line react-hooks-js/set-state-in-effect -- Reanimated: seeding from a SharedValue off render is the warning-free path
|
|
599
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect -- Reanimated: seed from the SharedValue outside render to avoid strict-mode access warnings
|
|
575
600
|
setValueLayoutSample(value.get());
|
|
576
601
|
}, [value]);
|
|
577
602
|
|
|
@@ -721,7 +746,7 @@ function useLiveChartController({
|
|
|
721
746
|
if (
|
|
722
747
|
referenceLineForm(l) !== "line" ||
|
|
723
748
|
l.value === undefined ||
|
|
724
|
-
refLineCustom[i]
|
|
749
|
+
refLineCustom[i] || refLineOffAxisCustom[i]
|
|
725
750
|
) {
|
|
726
751
|
ys.push(-1);
|
|
727
752
|
continue;
|
|
@@ -866,11 +891,8 @@ function useLiveChartController({
|
|
|
866
891
|
// polyline passed as the last arg below).
|
|
867
892
|
thresholdCfg?.fill && !thresholdIsSeries ? thresholdGeom.lineY : undefined,
|
|
868
893
|
lineIsLinear,
|
|
869
|
-
//
|
|
870
|
-
//
|
|
871
|
-
// live value when `followViewEdge` tracks the scrolled-back window.
|
|
872
|
-
engine.edgeValue,
|
|
873
|
-
badgeCfg?.followViewEdge ?? false,
|
|
894
|
+
// The plotted line independently selects the visible edge while historical;
|
|
895
|
+
// badge/live-indicator options affect overlays only.
|
|
874
896
|
// Match the standalone loading squiggle's wave during the reveal morph.
|
|
875
897
|
loadingCfg?.amplitude,
|
|
876
898
|
loadingCfg?.speed,
|
|
@@ -906,6 +928,15 @@ function useLiveChartController({
|
|
|
906
928
|
);
|
|
907
929
|
|
|
908
930
|
const momentumSV = useMomentum(engine, momentum);
|
|
931
|
+
// A follow-edge badge must derive its color from the same historical point
|
|
932
|
+
// as its value and position. While following live, reuse the normal momentum
|
|
933
|
+
// result; while scrolled back, detect against the data prefix ending at the
|
|
934
|
+
// visible right-edge timestamp so incoming live ticks cannot change the pill.
|
|
935
|
+
const badgeMomentumSV = useDerivedValue(() =>
|
|
936
|
+
badgeCfg?.followViewEdge && engine.viewEnd.value !== null
|
|
937
|
+
? resolveMomentumProp(momentum, engine.data.value, engine.timestamp.value)
|
|
938
|
+
: momentumSV.value,
|
|
939
|
+
);
|
|
909
940
|
|
|
910
941
|
// ── Overlay hooks ─────────────────────────────────────────────────────
|
|
911
942
|
// Scrub/crosshair must see the same stash-backed candles as the engine.
|
|
@@ -997,6 +1028,14 @@ function useLiveChartController({
|
|
|
997
1028
|
? (timeScrollHoldMs ?? (scrubCfg?.panGestureDelay || HOLD_TO_SCRUB_MS))
|
|
998
1029
|
: (scrubCfg?.panGestureDelay ?? 0);
|
|
999
1030
|
|
|
1031
|
+
// Cross-gesture arbitration for the one-finger touch. `Gesture.Race` below is
|
|
1032
|
+
// NOT arbitration — RNGH's Race adds no relation between its children, so both
|
|
1033
|
+
// pans recognize independently and each can activate while the other already
|
|
1034
|
+
// owns the touch. This latch (written by the scroll pan, read by the scrub's
|
|
1035
|
+
// long-press guard) makes "the scroll already won" a hard fact; `scrubActive`
|
|
1036
|
+
// (written by the crosshair, read by the scroll pan) is the mirror image.
|
|
1037
|
+
const scrollActive = useSharedValue(false);
|
|
1038
|
+
|
|
1000
1039
|
const crosshair = useCrosshair(
|
|
1001
1040
|
engine,
|
|
1002
1041
|
effectivePadding,
|
|
@@ -1026,6 +1065,8 @@ function useLiveChartController({
|
|
|
1026
1065
|
timeScrollEnabled && scrollGestureMode === "axisDrag"
|
|
1027
1066
|
? Math.max(effectivePadding.bottom, AXIS_GRAB_MIN_PX)
|
|
1028
1067
|
: 0,
|
|
1068
|
+
scrollActive,
|
|
1069
|
+
scrubCfg?.clampToPlot ?? false,
|
|
1029
1070
|
);
|
|
1030
1071
|
|
|
1031
1072
|
// Capture only the shared value in the worklets below. Referencing
|
|
@@ -1048,6 +1089,10 @@ function useLiveChartController({
|
|
|
1048
1089
|
minTime: scrollMinTime,
|
|
1049
1090
|
enabled: timeScrollEnabled,
|
|
1050
1091
|
mode: scrollGestureMode,
|
|
1092
|
+
scrollActive,
|
|
1093
|
+
// Once a scrub is engaged the chart is locked: scrolling goes inert so the
|
|
1094
|
+
// finger only moves the price indicator across a fixed window.
|
|
1095
|
+
scrubActive: crosshairScrubActive,
|
|
1051
1096
|
// Clear any live crosshair when a scroll drag takes over.
|
|
1052
1097
|
onScrollStart: () => {
|
|
1053
1098
|
"worklet";
|
|
@@ -1172,10 +1217,40 @@ function useLiveChartController({
|
|
|
1172
1217
|
// point instead — otherwise both dots show at once. Applies on static charts
|
|
1173
1218
|
// too, now that they're scrubbable.
|
|
1174
1219
|
const selectionDotDuringScrub = scrubCfg !== null && selectionDotCfg !== null;
|
|
1220
|
+
// While scrolled back, the badge, dot, and value line still point at the live
|
|
1221
|
+
// price even though the window is showing history. Hide that live-priced group
|
|
1222
|
+
// together. `badge.followViewEdge` opts back in because all three then track
|
|
1223
|
+
// the visible edge price; `hideLiveOnScrollBack: false` keeps the legacy group.
|
|
1224
|
+
const hideLiveOnScrollBack = resolveHideLiveOnScrollBack(
|
|
1225
|
+
timeScroll,
|
|
1226
|
+
badgeCfg?.followViewEdge ?? false,
|
|
1227
|
+
);
|
|
1175
1228
|
const liveDotOpacity = useDerivedValue(
|
|
1176
1229
|
() =>
|
|
1177
1230
|
reveal.dotOpacity.value *
|
|
1178
|
-
(selectionDotDuringScrub && crosshairScrubActive.value ? 0 : 1)
|
|
1231
|
+
(selectionDotDuringScrub && crosshairScrubActive.value ? 0 : 1) *
|
|
1232
|
+
liveIndicatorScrollOpacity(
|
|
1233
|
+
hideLiveOnScrollBack,
|
|
1234
|
+
engine.viewEnd.value,
|
|
1235
|
+
),
|
|
1236
|
+
);
|
|
1237
|
+
// Same scrolled-back gating for the value line: it would draw a dashed line
|
|
1238
|
+
// at the live value's Y — a price that isn't in the scrolled-back view.
|
|
1239
|
+
const valueLineOpacity = useDerivedValue(
|
|
1240
|
+
() =>
|
|
1241
|
+
reveal.lineOpacity.value *
|
|
1242
|
+
liveIndicatorScrollOpacity(
|
|
1243
|
+
hideLiveOnScrollBack,
|
|
1244
|
+
engine.viewEnd.value,
|
|
1245
|
+
),
|
|
1246
|
+
);
|
|
1247
|
+
const liveBadgeOpacity = useDerivedValue(
|
|
1248
|
+
() =>
|
|
1249
|
+
reveal.badgeOpacity.value *
|
|
1250
|
+
liveIndicatorScrollOpacity(
|
|
1251
|
+
hideLiveOnScrollBack,
|
|
1252
|
+
engine.viewEnd.value,
|
|
1253
|
+
),
|
|
1179
1254
|
);
|
|
1180
1255
|
|
|
1181
1256
|
// Fade the annotation overlays (markers + reference lines) out while scrubbing
|
|
@@ -1235,10 +1310,14 @@ function useLiveChartController({
|
|
|
1235
1310
|
leftEdgeFadeCfg,
|
|
1236
1311
|
metricsCfg,
|
|
1237
1312
|
allRefLines,
|
|
1313
|
+
refLineKeys,
|
|
1238
1314
|
refLineCustom,
|
|
1315
|
+
refLineOffAxisCustom,
|
|
1316
|
+
refLineCustomTagWidths,
|
|
1239
1317
|
dragValues,
|
|
1240
1318
|
dragActive,
|
|
1241
1319
|
renderReferenceLine,
|
|
1320
|
+
renderOffAxisReferenceLine,
|
|
1242
1321
|
refGroupingActive: refGroupingRadius != null,
|
|
1243
1322
|
refGroupResult,
|
|
1244
1323
|
groupHidden,
|
|
@@ -1311,9 +1390,12 @@ function useLiveChartController({
|
|
|
1311
1390
|
dotX,
|
|
1312
1391
|
dotY,
|
|
1313
1392
|
liveDotOpacity,
|
|
1393
|
+
valueLineOpacity,
|
|
1394
|
+
liveBadgeOpacity,
|
|
1314
1395
|
overlayScrubFade,
|
|
1315
1396
|
markerGroupOpacity,
|
|
1316
1397
|
momentumSV,
|
|
1398
|
+
badgeMomentumSV,
|
|
1317
1399
|
onDegenShake,
|
|
1318
1400
|
crosshair,
|
|
1319
1401
|
rootGesture,
|
|
@@ -1414,6 +1496,8 @@ function ChartYAxisLayer({
|
|
|
1414
1496
|
metricsCfg,
|
|
1415
1497
|
gridStyleCfg,
|
|
1416
1498
|
yAxisFloat,
|
|
1499
|
+
yAxisCfg,
|
|
1500
|
+
liveBadgeOpacity,
|
|
1417
1501
|
} = model;
|
|
1418
1502
|
return (
|
|
1419
1503
|
<Group opacity={reveal.yAxisOpacity}>
|
|
@@ -1431,7 +1515,10 @@ function ChartYAxisLayer({
|
|
|
1431
1515
|
badgeCenterY={badgeUsesRightGutter ? dotY : undefined}
|
|
1432
1516
|
badgeFontSize={badgeUsesRightGutter ? badgeFont.getSize() : undefined}
|
|
1433
1517
|
badgeOffsetY={badgeCfg?.offsetY ?? 0}
|
|
1518
|
+
badgeOpacity={badgeUsesRightGutter ? liveBadgeOpacity : undefined}
|
|
1434
1519
|
gridStyle={gridStyleCfg}
|
|
1520
|
+
labelRightMargin={yAxisCfg?.labelRightMargin}
|
|
1521
|
+
gridEndGap={yAxisCfg?.gridEndGap}
|
|
1435
1522
|
/>
|
|
1436
1523
|
</Group>
|
|
1437
1524
|
);
|
|
@@ -1670,8 +1757,10 @@ function ChartStack({
|
|
|
1670
1757
|
fontProp,
|
|
1671
1758
|
badgeCfg,
|
|
1672
1759
|
valueLineCfg,
|
|
1760
|
+
valueLineOpacity,
|
|
1673
1761
|
dotY,
|
|
1674
1762
|
allRefLines,
|
|
1763
|
+
refLineKeys,
|
|
1675
1764
|
dragValues,
|
|
1676
1765
|
resolvedSegments,
|
|
1677
1766
|
hasRecolorSegments,
|
|
@@ -1722,9 +1811,9 @@ function ChartStack({
|
|
|
1722
1811
|
<Group transform={degen?.shakeTransform}>
|
|
1723
1812
|
{/* Segment dividers + labels (behind the line). The scrub-focus emphasis is
|
|
1724
1813
|
painted on the line stroke itself, below — this overlay draws no fill. */}
|
|
1725
|
-
{resolvedSegments.map((seg
|
|
1814
|
+
{resolvedSegments.map((seg) => (
|
|
1726
1815
|
<SegmentDividerOverlay
|
|
1727
|
-
key={
|
|
1816
|
+
key={segmentReactKey(seg)}
|
|
1728
1817
|
engine={engine}
|
|
1729
1818
|
padding={effectivePadding}
|
|
1730
1819
|
segment={seg}
|
|
@@ -1734,7 +1823,7 @@ function ChartStack({
|
|
|
1734
1823
|
|
|
1735
1824
|
{/* Value line + reference line (behind chart line) */}
|
|
1736
1825
|
{valueLineCfg && (
|
|
1737
|
-
<Group opacity={
|
|
1826
|
+
<Group opacity={valueLineOpacity}>
|
|
1738
1827
|
<ValueLineOverlay
|
|
1739
1828
|
dotY={dotY}
|
|
1740
1829
|
engine={engine}
|
|
@@ -1746,14 +1835,12 @@ function ChartStack({
|
|
|
1746
1835
|
</Group>
|
|
1747
1836
|
)}
|
|
1748
1837
|
|
|
1749
|
-
{/*
|
|
1750
|
-
|
|
1751
|
-
content-derived key would collapse to one. Wrapped in a fade group so
|
|
1752
|
-
`scrub.hideOverlaysOnScrub` can ease the lines out while scrubbing. */}
|
|
1838
|
+
{/* Wrapped in a fade group so `scrub.hideOverlaysOnScrub` can ease lines
|
|
1839
|
+
out while scrubbing. Explicit ids keep lines stable when reordered. */}
|
|
1753
1840
|
<Group opacity={overlayScrubFade}>
|
|
1754
1841
|
{allRefLines.map((rl, i) => (
|
|
1755
1842
|
<ReferenceLineOverlay
|
|
1756
|
-
key={i}
|
|
1843
|
+
key={refLineKeys[i]}
|
|
1757
1844
|
engine={engine}
|
|
1758
1845
|
padding={effectivePadding}
|
|
1759
1846
|
line={rl}
|
|
@@ -1763,6 +1850,9 @@ function ChartStack({
|
|
|
1763
1850
|
fontProp={fontProp}
|
|
1764
1851
|
dragValues={dragValues}
|
|
1765
1852
|
index={i}
|
|
1853
|
+
yAxisEntries={yAxisEntries}
|
|
1854
|
+
labelRightMargin={yAxisCfg?.labelRightMargin}
|
|
1855
|
+
gridEndGap={yAxisCfg?.gridEndGap}
|
|
1766
1856
|
/>
|
|
1767
1857
|
))}
|
|
1768
1858
|
</Group>
|
|
@@ -1857,7 +1947,6 @@ function ChartStack({
|
|
|
1857
1947
|
dotX={dotX}
|
|
1858
1948
|
dotY={dotY}
|
|
1859
1949
|
palette={palette}
|
|
1860
|
-
engine={engine}
|
|
1861
1950
|
pulse={pulseCfg}
|
|
1862
1951
|
radius={dotCfg.radius}
|
|
1863
1952
|
ring={dotCfg.ring}
|
|
@@ -2037,6 +2126,11 @@ function ChartScrubLayer({
|
|
|
2037
2126
|
dimOpacity={scrubCfg.dimOpacity}
|
|
2038
2127
|
liveDotExtent={liveDotExtent}
|
|
2039
2128
|
crosshairLineColor={scrubCfg.crosshairLineColor}
|
|
2129
|
+
crosshairStrokeWidth={scrubCfg.crosshairStrokeWidth}
|
|
2130
|
+
crosshairOvershoot={scrubCfg.crosshairOvershoot}
|
|
2131
|
+
crosshairFade={scrubCfg.crosshairFade}
|
|
2132
|
+
crosshairFadeDistance={scrubCfg.crosshairFadeDistance}
|
|
2133
|
+
crosshairLineCap={scrubCfg.crosshairLineCap}
|
|
2040
2134
|
crosshairDash={scrubCfg.crosshairDash}
|
|
2041
2135
|
crosshairDimColor={scrubCfg.crosshairDimColor}
|
|
2042
2136
|
tooltipBackground={scrubCfg.tooltipBackground}
|
|
@@ -2064,6 +2158,19 @@ function ChartScrubLayer({
|
|
|
2064
2158
|
);
|
|
2065
2159
|
}
|
|
2066
2160
|
|
|
2161
|
+
/** A segment's time range and presentation uniquely identify its divider view. */
|
|
2162
|
+
function segmentReactKey(segment: ResolvedSegment): string {
|
|
2163
|
+
return [
|
|
2164
|
+
"seg",
|
|
2165
|
+
segment.from ?? "start",
|
|
2166
|
+
segment.to ?? "end",
|
|
2167
|
+
segment.divider,
|
|
2168
|
+
segment.dividerColor,
|
|
2169
|
+
segment.label ?? "",
|
|
2170
|
+
segment.labelPosition,
|
|
2171
|
+
].join(":");
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2067
2174
|
/** Live-value text drawn as its own canvas layer, above both the area gradient
|
|
2068
2175
|
* and the left-edge fade, so the large number stays crisp at the left edge
|
|
2069
2176
|
* instead of being washed out by the fade's `dstOut` blend. */
|
|
@@ -2115,14 +2222,14 @@ function ChartBadgeLayer({
|
|
|
2115
2222
|
}) {
|
|
2116
2223
|
const {
|
|
2117
2224
|
badgeFont,
|
|
2118
|
-
reveal,
|
|
2119
2225
|
engine,
|
|
2120
2226
|
effectivePadding,
|
|
2121
2227
|
palette,
|
|
2122
2228
|
formatValue,
|
|
2123
|
-
|
|
2229
|
+
badgeMomentumSV,
|
|
2124
2230
|
metricsCfg,
|
|
2125
2231
|
yAxisFloat,
|
|
2232
|
+
liveBadgeOpacity,
|
|
2126
2233
|
} = model;
|
|
2127
2234
|
const badgeCfg = model.badgeCfg!;
|
|
2128
2235
|
const badgeData = useBadge(
|
|
@@ -2133,7 +2240,7 @@ function ChartBadgeLayer({
|
|
|
2133
2240
|
badgeFont,
|
|
2134
2241
|
badgeCfg.variant,
|
|
2135
2242
|
badgeCfg.tail,
|
|
2136
|
-
|
|
2243
|
+
badgeMomentumSV,
|
|
2137
2244
|
badgeCfg.position,
|
|
2138
2245
|
badgeCfg.background,
|
|
2139
2246
|
metricsCfg.badge,
|
|
@@ -2146,7 +2253,7 @@ function ChartBadgeLayer({
|
|
|
2146
2253
|
);
|
|
2147
2254
|
return (
|
|
2148
2255
|
<Group transform={degen?.shakeTransform}>
|
|
2149
|
-
<Group opacity={
|
|
2256
|
+
<Group opacity={liveBadgeOpacity}>
|
|
2150
2257
|
<BadgeOverlay
|
|
2151
2258
|
badge={badgeData}
|
|
2152
2259
|
font={badgeFont}
|
|
@@ -2167,13 +2274,18 @@ function ChartBadgeLayer({
|
|
|
2167
2274
|
function ChartRefBadgeLayer({
|
|
2168
2275
|
model,
|
|
2169
2276
|
degen,
|
|
2277
|
+
yAxisEntries,
|
|
2170
2278
|
}: {
|
|
2171
2279
|
model: LiveChartModel;
|
|
2172
2280
|
degen: DegenState | null;
|
|
2281
|
+
yAxisEntries: YAxisEntries | null;
|
|
2173
2282
|
}) {
|
|
2174
2283
|
const {
|
|
2175
2284
|
allRefLines,
|
|
2285
|
+
refLineKeys,
|
|
2176
2286
|
refLineCustom,
|
|
2287
|
+
refLineOffAxisCustom,
|
|
2288
|
+
refLineCustomTagWidths,
|
|
2177
2289
|
dragValues,
|
|
2178
2290
|
groupHidden,
|
|
2179
2291
|
refGroupResult,
|
|
@@ -2187,6 +2299,7 @@ function ChartRefBadgeLayer({
|
|
|
2187
2299
|
formatValue,
|
|
2188
2300
|
skiaFont,
|
|
2189
2301
|
fontProp,
|
|
2302
|
+
yAxisCfg,
|
|
2190
2303
|
overlayScrubFade,
|
|
2191
2304
|
} = model;
|
|
2192
2305
|
if (allRefLines.length === 0) return null;
|
|
@@ -2194,7 +2307,7 @@ function ChartRefBadgeLayer({
|
|
|
2194
2307
|
<Group transform={degen?.shakeTransform} opacity={overlayScrubFade}>
|
|
2195
2308
|
{allRefLines.map((rl, i) => (
|
|
2196
2309
|
<ReferenceLineOverlay
|
|
2197
|
-
key={i}
|
|
2310
|
+
key={refLineKeys[i]}
|
|
2198
2311
|
engine={engine}
|
|
2199
2312
|
padding={effectivePadding}
|
|
2200
2313
|
line={rl}
|
|
@@ -2206,7 +2319,12 @@ function ChartRefBadgeLayer({
|
|
|
2206
2319
|
dragValues={dragValues}
|
|
2207
2320
|
index={i}
|
|
2208
2321
|
suppressTag={refLineCustom[i]}
|
|
2322
|
+
suppressTagWhenOffAxis={refLineOffAxisCustom[i]}
|
|
2323
|
+
customTagWidths={refLineCustomTagWidths}
|
|
2209
2324
|
groupHidden={refGroupingActive ? groupHidden : undefined}
|
|
2325
|
+
yAxisEntries={yAxisEntries}
|
|
2326
|
+
labelRightMargin={yAxisCfg?.labelRightMargin}
|
|
2327
|
+
gridEndGap={yAxisCfg?.gridEndGap}
|
|
2210
2328
|
/>
|
|
2211
2329
|
))}
|
|
2212
2330
|
{/* Collapsed count handles for grouped (near-value) lines. */}
|
|
@@ -2276,8 +2394,11 @@ function ChartCustomAnnotations({ model }: { model: LiveChartModel }) {
|
|
|
2276
2394
|
markerClusterCfg,
|
|
2277
2395
|
renderMarker,
|
|
2278
2396
|
renderReferenceLine,
|
|
2397
|
+
renderOffAxisReferenceLine,
|
|
2279
2398
|
allRefLines,
|
|
2280
2399
|
refLineCustom,
|
|
2400
|
+
refLineOffAxisCustom,
|
|
2401
|
+
refLineCustomTagWidths,
|
|
2281
2402
|
dragValues,
|
|
2282
2403
|
dragActive,
|
|
2283
2404
|
engine,
|
|
@@ -2316,6 +2437,21 @@ function ChartCustomAnnotations({ model }: { model: LiveChartModel }) {
|
|
|
2316
2437
|
formatValue={formatValue}
|
|
2317
2438
|
dragValues={dragValues}
|
|
2318
2439
|
dragActive={dragActive}
|
|
2440
|
+
tagWidths={refLineCustomTagWidths}
|
|
2441
|
+
/>
|
|
2442
|
+
)}
|
|
2443
|
+
{renderOffAxisReferenceLine && allRefLines.length > 0 && (
|
|
2444
|
+
<CustomReferenceLineOverlay
|
|
2445
|
+
lines={allRefLines}
|
|
2446
|
+
renderReferenceLine={renderOffAxisReferenceLine}
|
|
2447
|
+
custom={refLineOffAxisCustom}
|
|
2448
|
+
engine={engine}
|
|
2449
|
+
padding={effectivePadding}
|
|
2450
|
+
formatValue={formatValue}
|
|
2451
|
+
dragValues={dragValues}
|
|
2452
|
+
dragActive={dragActive}
|
|
2453
|
+
tagWidths={refLineCustomTagWidths}
|
|
2454
|
+
offAxisOnly
|
|
2319
2455
|
/>
|
|
2320
2456
|
)}
|
|
2321
2457
|
</Animated.View>
|
|
@@ -2357,6 +2493,7 @@ function ChartView({
|
|
|
2357
2493
|
renderTooltip,
|
|
2358
2494
|
renderOverlay,
|
|
2359
2495
|
renderReferenceLine,
|
|
2496
|
+
renderOffAxisReferenceLine,
|
|
2360
2497
|
allRefLines,
|
|
2361
2498
|
scrubCfg,
|
|
2362
2499
|
crosshair,
|
|
@@ -2428,7 +2565,11 @@ function ChartView({
|
|
|
2428
2565
|
)}
|
|
2429
2566
|
|
|
2430
2567
|
{/* Reference-line badges + labels above the fade so they stay crisp. */}
|
|
2431
|
-
<ChartRefBadgeLayer
|
|
2568
|
+
<ChartRefBadgeLayer
|
|
2569
|
+
model={model}
|
|
2570
|
+
degen={degen}
|
|
2571
|
+
yAxisEntries={yAxisEntries}
|
|
2572
|
+
/>
|
|
2432
2573
|
|
|
2433
2574
|
<ChartValueOverlay model={model} degen={degen} />
|
|
2434
2575
|
|
|
@@ -2466,7 +2607,8 @@ function ChartView({
|
|
|
2466
2607
|
Skia markers (the wrapper is full-bleed; children keep their own
|
|
2467
2608
|
absolute positions). */}
|
|
2468
2609
|
{((markersActive && renderMarker) ||
|
|
2469
|
-
(renderReferenceLine
|
|
2610
|
+
((renderReferenceLine || renderOffAxisReferenceLine) &&
|
|
2611
|
+
allRefLines.length > 0)) && (
|
|
2470
2612
|
<ChartCustomAnnotations model={model} />
|
|
2471
2613
|
)}
|
|
2472
2614
|
|
|
@@ -2481,12 +2623,13 @@ function ChartView({
|
|
|
2481
2623
|
scrubTime={crosshair.scrubTime}
|
|
2482
2624
|
scrubActive={crosshair.scrubActive}
|
|
2483
2625
|
scrubCandle={crosshair.scrubCandle}
|
|
2484
|
-
crosshairOpacity={crosshair.crosshairOpacity}
|
|
2485
2626
|
tooltipLayout={crosshair.tooltipLayout}
|
|
2486
2627
|
engine={engine}
|
|
2487
2628
|
padding={effectivePadding}
|
|
2488
2629
|
placement={scrubCfg.tooltipPlacement}
|
|
2489
2630
|
margin={scrubCfg.tooltipMargin}
|
|
2631
|
+
crosshairFade={scrubCfg.crosshairFade}
|
|
2632
|
+
crosshairFadeDistance={scrubCfg.crosshairFadeDistance}
|
|
2490
2633
|
lineTop={crosshair.tooltipLineTop}
|
|
2491
2634
|
scrubDotY={crosshair.scrubDotY}
|
|
2492
2635
|
/>
|