react-native-livechart 4.15.1 → 4.17.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/LiveChart.d.ts.map +1 -1
- package/dist/components/LiveChartSeries.d.ts.map +1 -1
- package/dist/core/liveChartEngineTick.d.ts.map +1 -1
- package/dist/core/liveChartSeriesEngineTick.d.ts.map +1 -1
- package/dist/core/resolveConfig.d.ts +6 -0
- package/dist/core/resolveConfig.d.ts.map +1 -1
- package/dist/core/useLiveChartEngine.d.ts.map +1 -1
- package/dist/core/useLiveChartSeriesEngine.d.ts.map +1 -1
- package/dist/hooks/useCandlePaths.d.ts +22 -11
- package/dist/hooks/useCandlePaths.d.ts.map +1 -1
- package/dist/hooks/useChartReveal.d.ts +3 -2
- package/dist/hooks/useChartReveal.d.ts.map +1 -1
- package/dist/hooks/useMarkers.d.ts.map +1 -1
- package/dist/hooks/usePanScroll.d.ts +8 -1
- package/dist/hooks/usePanScroll.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/math/markerCluster.d.ts +3 -0
- package/dist/math/markerCluster.d.ts.map +1 -1
- package/dist/types.d.ts +50 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/LiveChart.tsx +236 -123
- package/src/components/LiveChartSeries.tsx +60 -44
- package/src/core/liveChartEngineTick.ts +5 -1
- package/src/core/liveChartSeriesEngineTick.ts +5 -1
- package/src/core/resolveConfig.ts +14 -0
- package/src/core/useLiveChartEngine.ts +6 -2
- package/src/core/useLiveChartSeriesEngine.ts +4 -2
- package/src/hooks/useCandlePaths.ts +41 -27
- package/src/hooks/useChartReveal.ts +11 -3
- package/src/hooks/useMarkers.ts +1 -0
- package/src/hooks/usePanScroll.ts +12 -1
- package/src/index.ts +1 -0
- package/src/math/markerCluster.ts +14 -0
- package/src/types.ts +51 -1
|
@@ -2,10 +2,13 @@ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
|
2
2
|
import { StyleSheet, View } from "react-native";
|
|
3
3
|
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
4
4
|
import Animated, {
|
|
5
|
+
cancelAnimation,
|
|
5
6
|
useAnimatedReaction,
|
|
6
7
|
useAnimatedStyle,
|
|
7
8
|
useDerivedValue,
|
|
8
9
|
useSharedValue,
|
|
10
|
+
withDelay,
|
|
11
|
+
withSequence,
|
|
9
12
|
withTiming,
|
|
10
13
|
} from "react-native-reanimated";
|
|
11
14
|
import { scheduleOnRN } from "react-native-worklets";
|
|
@@ -46,6 +49,7 @@ import {
|
|
|
46
49
|
resolveScrub,
|
|
47
50
|
resolveScrubAction,
|
|
48
51
|
resolveTransitions,
|
|
52
|
+
resolveFling,
|
|
49
53
|
resolveOverscroll,
|
|
50
54
|
resolveReturnToLiveMs,
|
|
51
55
|
resolveSelectionDot,
|
|
@@ -68,7 +72,7 @@ import { useLiveChartEngine } from "../core/useLiveChartEngine";
|
|
|
68
72
|
import { pulseRadialOutset } from "../draw/line";
|
|
69
73
|
import { resolveChartLayout } from "../hooks/resolveChartLayout";
|
|
70
74
|
import { useBadge } from "../hooks/useBadge";
|
|
71
|
-
import { useCandlePaths } from "../hooks/useCandlePaths";
|
|
75
|
+
import { useCandlePaths, useCandleWidthLerp } from "../hooks/useCandlePaths";
|
|
72
76
|
import { useCanvasLayout } from "../hooks/useCanvasLayout";
|
|
73
77
|
import { useChartColors } from "../hooks/useChartColors";
|
|
74
78
|
import { useChartOverlayContext } from "../hooks/useChartOverlayContext";
|
|
@@ -257,6 +261,7 @@ function useLiveChartController({
|
|
|
257
261
|
font: fontProp,
|
|
258
262
|
insets,
|
|
259
263
|
style,
|
|
264
|
+
seriesOpacity,
|
|
260
265
|
canvasMode = "transparent",
|
|
261
266
|
|
|
262
267
|
// ── Candlestick ─────────────────────────────────────────────────────────
|
|
@@ -293,6 +298,7 @@ function useLiveChartController({
|
|
|
293
298
|
// ── Overlays ────────────────────────────────────────────────────────────
|
|
294
299
|
yAxis = true,
|
|
295
300
|
xAxis = true,
|
|
301
|
+
axisAutoHide = false,
|
|
296
302
|
topLabel,
|
|
297
303
|
bottomLabel,
|
|
298
304
|
badge = true,
|
|
@@ -335,6 +341,8 @@ function useLiveChartController({
|
|
|
335
341
|
onReachStart,
|
|
336
342
|
onDegenShake,
|
|
337
343
|
}: LiveChartProps) {
|
|
344
|
+
const fullSeriesOpacity = useSharedValue(1);
|
|
345
|
+
const resolvedSeriesOpacity = seriesOpacity ?? fullSeriesOpacity;
|
|
338
346
|
const emptyMarkers = useSharedValue<Marker[]>([]);
|
|
339
347
|
const markersSV = markers ?? emptyMarkers;
|
|
340
348
|
// Stand-in threshold value so `useThreshold` can be called unconditionally
|
|
@@ -614,6 +622,8 @@ function useLiveChartController({
|
|
|
614
622
|
const timeScrollOverscroll = timeScrollEnabled
|
|
615
623
|
? resolveOverscroll(timeScroll)
|
|
616
624
|
: 0;
|
|
625
|
+
// Release inertia (fling) — `timeScroll.fling: false` stops the pan dead.
|
|
626
|
+
const timeScrollFling = resolveFling(timeScroll);
|
|
617
627
|
const zoomCfg = resolveZoom(zoom);
|
|
618
628
|
const zoomEnabled = zoomCfg !== null && !isStatic;
|
|
619
629
|
|
|
@@ -947,6 +957,15 @@ function useLiveChartController({
|
|
|
947
957
|
: momentumSV.value,
|
|
948
958
|
);
|
|
949
959
|
|
|
960
|
+
// Width bridge lives here (outer tree), not in ChartCandleLayer: canvas
|
|
961
|
+
// children commit one frame behind, which would lag the width target behind
|
|
962
|
+
// the engine's framing snap on a timeframe switch. See useCandleWidthLerp.
|
|
963
|
+
const displayCandleWidth = useCandleWidthLerp(
|
|
964
|
+
candleWidth,
|
|
965
|
+
transitionsCfg.candleLerpSpeed,
|
|
966
|
+
!isStatic && isCandle,
|
|
967
|
+
);
|
|
968
|
+
|
|
950
969
|
// ── Overlay hooks ─────────────────────────────────────────────────────
|
|
951
970
|
// Scrub/crosshair must see the same stash-backed candles as the engine.
|
|
952
971
|
const candleOpts = isCandle
|
|
@@ -1099,6 +1118,7 @@ function useLiveChartController({
|
|
|
1099
1118
|
enabled: timeScrollEnabled,
|
|
1100
1119
|
mode: scrollGestureMode,
|
|
1101
1120
|
overscroll: timeScrollOverscroll,
|
|
1121
|
+
fling: timeScrollFling,
|
|
1102
1122
|
scrollActive,
|
|
1103
1123
|
// Once a scrub is engaged the chart is locked: scrolling goes inert so the
|
|
1104
1124
|
// finger only moves the price indicator across a fixed window.
|
|
@@ -1128,6 +1148,78 @@ function useLiveChartController({
|
|
|
1128
1148
|
},
|
|
1129
1149
|
});
|
|
1130
1150
|
|
|
1151
|
+
// Axis auto-hide: fade both axes out at rest and back in while the user
|
|
1152
|
+
// interacts. Any movement — scrub / time-scroll touch, or a viewEnd /
|
|
1153
|
+
// viewWindow change (fling momentum, pinch-zoom) — shows the axes; once the
|
|
1154
|
+
// touch lifts and the view settles, they fade back out after `hideAfterMs`.
|
|
1155
|
+
// Only the axis groups' opacity animates; the axis worklets keep running
|
|
1156
|
+
// underneath so a fade-in shows current labels.
|
|
1157
|
+
const axisAutoHideCfg =
|
|
1158
|
+
axisAutoHide === true ? {} : axisAutoHide === false ? null : axisAutoHide;
|
|
1159
|
+
const axisIdleOpacity = axisAutoHideCfg?.idleOpacity ?? 0;
|
|
1160
|
+
const axisFadeInMs = axisAutoHideCfg?.fadeInMs ?? 60;
|
|
1161
|
+
const axisFadeOutMs = axisAutoHideCfg?.fadeOutMs ?? 250;
|
|
1162
|
+
const axisHideAfterMs = axisAutoHideCfg?.hideAfterMs ?? 3000;
|
|
1163
|
+
const axisAutoHideOpacity = useSharedValue(
|
|
1164
|
+
axisAutoHideCfg ? axisIdleOpacity : 1,
|
|
1165
|
+
);
|
|
1166
|
+
const axisAutoHideEnabled = axisAutoHideCfg !== null;
|
|
1167
|
+
const lastAxisAutoHide = useRef({
|
|
1168
|
+
enabled: axisAutoHideEnabled,
|
|
1169
|
+
idleOpacity: axisIdleOpacity,
|
|
1170
|
+
});
|
|
1171
|
+
// `useSharedValue` only reads its initial value on mount. Keep prop changes
|
|
1172
|
+
// in sync as well: turning auto-hide off must immediately restore the axes,
|
|
1173
|
+
// and turning it on starts from the configured idle opacity.
|
|
1174
|
+
useEffect(() => {
|
|
1175
|
+
if (
|
|
1176
|
+
lastAxisAutoHide.current.enabled === axisAutoHideEnabled &&
|
|
1177
|
+
lastAxisAutoHide.current.idleOpacity === axisIdleOpacity
|
|
1178
|
+
) {
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
lastAxisAutoHide.current = {
|
|
1182
|
+
enabled: axisAutoHideEnabled,
|
|
1183
|
+
idleOpacity: axisIdleOpacity,
|
|
1184
|
+
};
|
|
1185
|
+
cancelAnimation(axisAutoHideOpacity);
|
|
1186
|
+
axisAutoHideOpacity.value = axisAutoHideEnabled ? axisIdleOpacity : 1;
|
|
1187
|
+
}, [axisAutoHideEnabled, axisAutoHideOpacity, axisIdleOpacity]);
|
|
1188
|
+
useAnimatedReaction(
|
|
1189
|
+
() => ({
|
|
1190
|
+
gesture: scrollActive.value || crosshairScrubActive.value,
|
|
1191
|
+
viewEnd: engine.viewEnd.value,
|
|
1192
|
+
viewWindow: engine.viewWindow.value,
|
|
1193
|
+
}),
|
|
1194
|
+
(curr, prev) => {
|
|
1195
|
+
if (!axisAutoHideEnabled || prev === null) return;
|
|
1196
|
+
const moved =
|
|
1197
|
+
curr.gesture !== prev.gesture ||
|
|
1198
|
+
curr.viewEnd !== prev.viewEnd ||
|
|
1199
|
+
curr.viewWindow !== prev.viewWindow;
|
|
1200
|
+
if (!moved) return;
|
|
1201
|
+
cancelAnimation(axisAutoHideOpacity);
|
|
1202
|
+
axisAutoHideOpacity.value = curr.gesture
|
|
1203
|
+
? withTiming(1, { duration: axisFadeInMs })
|
|
1204
|
+
: // Movement without a held touch (gesture end, fling, pinch): show,
|
|
1205
|
+
// then fade back out once the chart goes untouched for a while.
|
|
1206
|
+
withSequence(
|
|
1207
|
+
withTiming(1, { duration: axisFadeInMs }),
|
|
1208
|
+
withDelay(
|
|
1209
|
+
axisHideAfterMs,
|
|
1210
|
+
withTiming(axisIdleOpacity, { duration: axisFadeOutMs }),
|
|
1211
|
+
),
|
|
1212
|
+
);
|
|
1213
|
+
},
|
|
1214
|
+
[
|
|
1215
|
+
axisAutoHideEnabled,
|
|
1216
|
+
axisIdleOpacity,
|
|
1217
|
+
axisFadeInMs,
|
|
1218
|
+
axisFadeOutMs,
|
|
1219
|
+
axisHideAfterMs,
|
|
1220
|
+
],
|
|
1221
|
+
);
|
|
1222
|
+
|
|
1131
1223
|
// Paging callbacks: report the visible range / proximity to the oldest data so
|
|
1132
1224
|
// a host can lazily load history. Inert unless a callback is supplied.
|
|
1133
1225
|
useVisibleRange({
|
|
@@ -1230,7 +1322,8 @@ function useLiveChartController({
|
|
|
1230
1322
|
liveIndicatorScrollOpacity(
|
|
1231
1323
|
hideLiveOnScrollBack,
|
|
1232
1324
|
engine.viewEnd.value,
|
|
1233
|
-
)
|
|
1325
|
+
) *
|
|
1326
|
+
resolvedSeriesOpacity.value,
|
|
1234
1327
|
);
|
|
1235
1328
|
// Same scrolled-back gating for the value line: it would draw a dashed line
|
|
1236
1329
|
// at the live value's Y — a price that isn't in the scrolled-back view.
|
|
@@ -1240,7 +1333,8 @@ function useLiveChartController({
|
|
|
1240
1333
|
liveIndicatorScrollOpacity(
|
|
1241
1334
|
hideLiveOnScrollBack,
|
|
1242
1335
|
engine.viewEnd.value,
|
|
1243
|
-
)
|
|
1336
|
+
) *
|
|
1337
|
+
resolvedSeriesOpacity.value,
|
|
1244
1338
|
);
|
|
1245
1339
|
const liveBadgeOpacity = useDerivedValue(
|
|
1246
1340
|
() =>
|
|
@@ -1248,7 +1342,8 @@ function useLiveChartController({
|
|
|
1248
1342
|
liveIndicatorScrollOpacity(
|
|
1249
1343
|
hideLiveOnScrollBack,
|
|
1250
1344
|
engine.viewEnd.value,
|
|
1251
|
-
)
|
|
1345
|
+
) *
|
|
1346
|
+
resolvedSeriesOpacity.value,
|
|
1252
1347
|
);
|
|
1253
1348
|
|
|
1254
1349
|
// Fade the annotation overlays (markers + reference lines) out while scrubbing
|
|
@@ -1280,6 +1375,7 @@ function useLiveChartController({
|
|
|
1280
1375
|
showValue,
|
|
1281
1376
|
valueMomentumColor,
|
|
1282
1377
|
lineProp,
|
|
1378
|
+
seriesOpacity: resolvedSeriesOpacity,
|
|
1283
1379
|
formatValue,
|
|
1284
1380
|
formatTime,
|
|
1285
1381
|
isCandle,
|
|
@@ -1351,6 +1447,7 @@ function useLiveChartController({
|
|
|
1351
1447
|
// engine + reveal
|
|
1352
1448
|
engine,
|
|
1353
1449
|
reveal,
|
|
1450
|
+
axisAutoHideOpacity,
|
|
1354
1451
|
// loading shell styling (null → not loading)
|
|
1355
1452
|
loadingLineColor: loadingCfg?.color,
|
|
1356
1453
|
loadingStrokeWidth: loadingCfg?.strokeWidth,
|
|
@@ -1369,6 +1466,7 @@ function useLiveChartController({
|
|
|
1369
1466
|
candlesEngine,
|
|
1370
1467
|
liveEngine,
|
|
1371
1468
|
candleWidth,
|
|
1469
|
+
displayCandleWidth,
|
|
1372
1470
|
transitionsCfg,
|
|
1373
1471
|
layoutWidth,
|
|
1374
1472
|
onLayout,
|
|
@@ -1495,9 +1593,15 @@ function ChartYAxisLayer({
|
|
|
1495
1593
|
yAxisFloat,
|
|
1496
1594
|
yAxisCfg,
|
|
1497
1595
|
liveBadgeOpacity,
|
|
1596
|
+
axisAutoHideOpacity,
|
|
1498
1597
|
} = model;
|
|
1598
|
+
// Fold the axis auto-hide fade into the reveal opacity (1 when the feature
|
|
1599
|
+
// is off).
|
|
1600
|
+
const yAxisGroupOpacity = useDerivedValue(
|
|
1601
|
+
() => reveal.yAxisOpacity.value * axisAutoHideOpacity.value,
|
|
1602
|
+
);
|
|
1499
1603
|
return (
|
|
1500
|
-
<Group opacity={
|
|
1604
|
+
<Group opacity={yAxisGroupOpacity}>
|
|
1501
1605
|
<YAxisOverlay
|
|
1502
1606
|
variant={variant}
|
|
1503
1607
|
float={variant === "labels" && yAxisFloat}
|
|
@@ -1538,14 +1642,17 @@ function ChartXAxisLayer({ model }: { model: LiveChartModel }) {
|
|
|
1538
1642
|
skiaFont,
|
|
1539
1643
|
);
|
|
1540
1644
|
return (
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1645
|
+
// Axis auto-hide fade (1 when the feature is off).
|
|
1646
|
+
<Group opacity={model.axisAutoHideOpacity}>
|
|
1647
|
+
<XAxisOverlay
|
|
1648
|
+
entries={xAxisEntries}
|
|
1649
|
+
engine={engine}
|
|
1650
|
+
padding={effectivePadding}
|
|
1651
|
+
palette={palette}
|
|
1652
|
+
font={skiaFont}
|
|
1653
|
+
volumeBandHeight={volumeBandHeight}
|
|
1654
|
+
/>
|
|
1655
|
+
</Group>
|
|
1549
1656
|
);
|
|
1550
1657
|
}
|
|
1551
1658
|
|
|
@@ -1583,6 +1690,7 @@ function ChartFillLayer({
|
|
|
1583
1690
|
thresholdIsSeries,
|
|
1584
1691
|
thresholdSeriesHasPoints,
|
|
1585
1692
|
thresholdFillUniforms,
|
|
1693
|
+
seriesOpacity,
|
|
1586
1694
|
} = model;
|
|
1587
1695
|
return (
|
|
1588
1696
|
<Group transform={degen?.shakeTransform}>
|
|
@@ -1597,62 +1705,64 @@ function ChartFillLayer({
|
|
|
1597
1705
|
/>
|
|
1598
1706
|
)}
|
|
1599
1707
|
|
|
1600
|
-
{
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
<
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
</Group>
|
|
1612
|
-
)}
|
|
1613
|
-
|
|
1614
|
-
{/* Area gradient fill */}
|
|
1615
|
-
{gradientCfg && (
|
|
1616
|
-
<Group opacity={reveal.fillOpacity}>
|
|
1617
|
-
<Path path={fillPath} style="fill">
|
|
1618
|
-
<LinearGradient
|
|
1619
|
-
start={vec(0, effectivePadding.top)}
|
|
1620
|
-
end={vec(0, gradientEnd)}
|
|
1621
|
-
colors={gradientColors}
|
|
1622
|
-
positions={gradientPositions}
|
|
1708
|
+
<Group opacity={seriesOpacity}>
|
|
1709
|
+
{/* Dot-lattice area fill (the under-line `fillPath` painted with a dot
|
|
1710
|
+
shader). Drawn before the gradient so a gradient (if also enabled)
|
|
1711
|
+
composites on top. */}
|
|
1712
|
+
{areaDotsCfg && (
|
|
1713
|
+
<Group opacity={reveal.fillOpacity}>
|
|
1714
|
+
<AreaDotsOverlay
|
|
1715
|
+
fillPath={fillPath}
|
|
1716
|
+
color={areaDotColorVec}
|
|
1717
|
+
spacing={areaDotsCfg.spacing}
|
|
1718
|
+
size={areaDotsCfg.size}
|
|
1623
1719
|
/>
|
|
1624
|
-
</
|
|
1625
|
-
|
|
1626
|
-
)}
|
|
1720
|
+
</Group>
|
|
1721
|
+
)}
|
|
1627
1722
|
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
above (set `gradient={false}` for the band alone). A time-varying series
|
|
1631
|
-
paints with the per-fragment split shader; a constant value with the
|
|
1632
|
-
vertical hard-stop gradient. */}
|
|
1633
|
-
{thresholdCfg?.fill &&
|
|
1634
|
-
(thresholdIsSeries ? (
|
|
1635
|
-
// The availability gate keeps a failed shader compile from filling the
|
|
1636
|
-
// band with the default paint (opaque black) — see THRESHOLD_SPLIT_AVAILABLE.
|
|
1637
|
-
thresholdSeriesHasPoints && THRESHOLD_SPLIT_AVAILABLE ? (
|
|
1638
|
-
<Group opacity={reveal.fillOpacity}>
|
|
1639
|
-
<Path path={thresholdFillPath} style="fill">
|
|
1640
|
-
<ThresholdSplitShader uniforms={thresholdFillUniforms} />
|
|
1641
|
-
</Path>
|
|
1642
|
-
</Group>
|
|
1643
|
-
) : null
|
|
1644
|
-
) : thresholdFillColors ? (
|
|
1723
|
+
{/* Area gradient fill */}
|
|
1724
|
+
{gradientCfg && (
|
|
1645
1725
|
<Group opacity={reveal.fillOpacity}>
|
|
1646
|
-
<Path path={
|
|
1726
|
+
<Path path={fillPath} style="fill">
|
|
1647
1727
|
<LinearGradient
|
|
1648
|
-
start={vec(0,
|
|
1649
|
-
end={
|
|
1650
|
-
colors={
|
|
1651
|
-
positions={
|
|
1728
|
+
start={vec(0, effectivePadding.top)}
|
|
1729
|
+
end={vec(0, gradientEnd)}
|
|
1730
|
+
colors={gradientColors}
|
|
1731
|
+
positions={gradientPositions}
|
|
1652
1732
|
/>
|
|
1653
1733
|
</Path>
|
|
1654
1734
|
</Group>
|
|
1655
|
-
)
|
|
1735
|
+
)}
|
|
1736
|
+
|
|
1737
|
+
{/* Threshold profit/loss band — the area between the line and the threshold,
|
|
1738
|
+
split into the above/below colors. Independent of the baseline area fill
|
|
1739
|
+
above (set `gradient={false}` for the band alone). A time-varying series
|
|
1740
|
+
paints with the per-fragment split shader; a constant value with the
|
|
1741
|
+
vertical hard-stop gradient. */}
|
|
1742
|
+
{thresholdCfg?.fill &&
|
|
1743
|
+
(thresholdIsSeries ? (
|
|
1744
|
+
// The availability gate keeps a failed shader compile from filling the
|
|
1745
|
+
// band with the default paint (opaque black) — see THRESHOLD_SPLIT_AVAILABLE.
|
|
1746
|
+
thresholdSeriesHasPoints && THRESHOLD_SPLIT_AVAILABLE ? (
|
|
1747
|
+
<Group opacity={reveal.fillOpacity}>
|
|
1748
|
+
<Path path={thresholdFillPath} style="fill">
|
|
1749
|
+
<ThresholdSplitShader uniforms={thresholdFillUniforms} />
|
|
1750
|
+
</Path>
|
|
1751
|
+
</Group>
|
|
1752
|
+
) : null
|
|
1753
|
+
) : thresholdFillColors ? (
|
|
1754
|
+
<Group opacity={reveal.fillOpacity}>
|
|
1755
|
+
<Path path={thresholdFillPath} style="fill">
|
|
1756
|
+
<LinearGradient
|
|
1757
|
+
start={vec(0, 0)}
|
|
1758
|
+
end={thresholdGeom.gradientEnd}
|
|
1759
|
+
colors={thresholdFillColors}
|
|
1760
|
+
positions={thresholdGeom.splitPositions}
|
|
1761
|
+
/>
|
|
1762
|
+
</Path>
|
|
1763
|
+
</Group>
|
|
1764
|
+
) : null)}
|
|
1765
|
+
</Group>
|
|
1656
1766
|
</Group>
|
|
1657
1767
|
);
|
|
1658
1768
|
}
|
|
@@ -1668,13 +1778,12 @@ function ChartCandleLayer({ model }: { model: LiveChartModel }) {
|
|
|
1668
1778
|
effectivePadding,
|
|
1669
1779
|
candlesEngine,
|
|
1670
1780
|
liveEngine,
|
|
1671
|
-
|
|
1781
|
+
displayCandleWidth,
|
|
1672
1782
|
metricsCfg,
|
|
1673
1783
|
volumeBandHeight,
|
|
1674
1784
|
volumeCfg,
|
|
1675
|
-
isStatic,
|
|
1676
|
-
transitionsCfg,
|
|
1677
1785
|
candleGroupOpacity,
|
|
1786
|
+
seriesOpacity,
|
|
1678
1787
|
palette,
|
|
1679
1788
|
volumeOpacity,
|
|
1680
1789
|
volumeUpColor,
|
|
@@ -1692,17 +1801,15 @@ function ChartCandleLayer({ model }: { model: LiveChartModel }) {
|
|
|
1692
1801
|
effectivePadding,
|
|
1693
1802
|
candlesEngine,
|
|
1694
1803
|
liveEngine,
|
|
1695
|
-
|
|
1804
|
+
displayCandleWidth,
|
|
1696
1805
|
true,
|
|
1697
1806
|
metricsCfg.candle,
|
|
1698
1807
|
volumeBandHeight,
|
|
1699
1808
|
volumeCfg?.radius ?? 0,
|
|
1700
|
-
!isStatic,
|
|
1701
|
-
transitionsCfg.candleLerpSpeed,
|
|
1702
1809
|
);
|
|
1703
1810
|
|
|
1704
1811
|
return (
|
|
1705
|
-
|
|
1812
|
+
<Group opacity={seriesOpacity}>
|
|
1706
1813
|
<Group opacity={candleGroupOpacity}>
|
|
1707
1814
|
<Path
|
|
1708
1815
|
path={upWicksPath}
|
|
@@ -1728,7 +1835,7 @@ function ChartCandleLayer({ model }: { model: LiveChartModel }) {
|
|
|
1728
1835
|
</Group>
|
|
1729
1836
|
</Group>
|
|
1730
1837
|
)}
|
|
1731
|
-
|
|
1838
|
+
</Group>
|
|
1732
1839
|
);
|
|
1733
1840
|
}
|
|
1734
1841
|
|
|
@@ -1775,6 +1882,7 @@ function ChartStack({
|
|
|
1775
1882
|
thresholdSeriesPts,
|
|
1776
1883
|
formatValue,
|
|
1777
1884
|
lineGroupOpacity,
|
|
1885
|
+
seriesOpacity,
|
|
1778
1886
|
linePath,
|
|
1779
1887
|
lineIsLinear,
|
|
1780
1888
|
strokeWidth,
|
|
@@ -1875,49 +1983,51 @@ function ChartStack({
|
|
|
1875
1983
|
full-width gradient paints the base color outside segments and each
|
|
1876
1984
|
segment's color within — so the line itself is recolored/faded (alpha in
|
|
1877
1985
|
the segment color reduces the line's opacity), not covered by an overlay. */}
|
|
1878
|
-
<Group opacity={
|
|
1879
|
-
<
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1986
|
+
<Group opacity={seriesOpacity}>
|
|
1987
|
+
<Group opacity={lineGroupOpacity}>
|
|
1988
|
+
<Path
|
|
1989
|
+
path={linePath}
|
|
1990
|
+
style="stroke"
|
|
1991
|
+
strokeWidth={strokeWidth}
|
|
1992
|
+
strokeCap={lineProp?.cap ?? "round"}
|
|
1993
|
+
strokeJoin={lineProp?.join ?? "round"}
|
|
1994
|
+
color={lineProp?.color ?? palette.line}
|
|
1995
|
+
>
|
|
1996
|
+
{thresholdIsSeries ? (
|
|
1997
|
+
// Time-varying split: a per-fragment shader colors the stroke above/
|
|
1998
|
+
// below the threshold polyline. Supersedes line.colors + segments.
|
|
1999
|
+
// An empty series renders no paint child → the plain stroke color
|
|
2000
|
+
// (null here keeps the empty case out of the NaN constant gradient).
|
|
2001
|
+
thresholdSeriesHasPoints ? (
|
|
2002
|
+
<ThresholdSplitShader uniforms={thresholdStrokeUniforms} />
|
|
2003
|
+
) : null
|
|
2004
|
+
) : thresholdCfg && thresholdStrokeColors ? (
|
|
2005
|
+
// Vertical hard split at the threshold Y — supersedes line.colors and
|
|
2006
|
+
// segment recoloring for the stroke while a threshold is set.
|
|
2007
|
+
<LinearGradient
|
|
2008
|
+
start={vec(0, 0)}
|
|
2009
|
+
end={thresholdGeom.gradientEnd}
|
|
2010
|
+
colors={thresholdStrokeColors}
|
|
2011
|
+
positions={thresholdGeom.splitPositions}
|
|
2012
|
+
/>
|
|
2013
|
+
) : hasRecolorSegments ? (
|
|
2014
|
+
<SegmentLineGradient
|
|
2015
|
+
engine={engine}
|
|
2016
|
+
segments={resolvedSegments}
|
|
2017
|
+
padding={effectivePadding}
|
|
2018
|
+
baseColor={lineProp?.color ?? palette.line}
|
|
2019
|
+
scrubX={crosshair.scrubX}
|
|
2020
|
+
scrubActive={crosshair.scrubActive}
|
|
2021
|
+
/>
|
|
2022
|
+
) : lineProp?.colors?.length ? (
|
|
2023
|
+
<LinearGradient
|
|
2024
|
+
start={vec(0, 0)}
|
|
2025
|
+
end={vec(layoutWidth, 0)}
|
|
2026
|
+
colors={lineProp.colors}
|
|
2027
|
+
/>
|
|
2028
|
+
) : null}
|
|
2029
|
+
</Path>
|
|
2030
|
+
</Group>
|
|
1921
2031
|
</Group>
|
|
1922
2032
|
|
|
1923
2033
|
{isCandle && <ChartCandleLayer model={model} />}
|
|
@@ -2190,21 +2300,24 @@ function ChartValueOverlay({
|
|
|
2190
2300
|
momentumSV,
|
|
2191
2301
|
valueMomentumColor,
|
|
2192
2302
|
reveal,
|
|
2303
|
+
seriesOpacity,
|
|
2193
2304
|
} = model;
|
|
2194
2305
|
if (!showValue) return null;
|
|
2195
2306
|
|
|
2196
2307
|
return (
|
|
2197
2308
|
<Group transform={degen?.shakeTransform}>
|
|
2198
|
-
<Group opacity={
|
|
2199
|
-
<
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2309
|
+
<Group opacity={seriesOpacity}>
|
|
2310
|
+
<Group opacity={reveal.lineOpacity}>
|
|
2311
|
+
<ValueTextOverlay
|
|
2312
|
+
engine={engine}
|
|
2313
|
+
padding={effectivePadding}
|
|
2314
|
+
palette={palette}
|
|
2315
|
+
font={valueFont}
|
|
2316
|
+
formatValue={formatValue}
|
|
2317
|
+
momentum={momentumSV}
|
|
2318
|
+
momentumColor={valueMomentumColor}
|
|
2319
|
+
/>
|
|
2320
|
+
</Group>
|
|
2208
2321
|
</Group>
|
|
2209
2322
|
</Group>
|
|
2210
2323
|
);
|