@coinbase/cds-mobile-visualization 3.4.0-beta.2 → 3.4.0-beta.21

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.
Files changed (211) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/dts/chart/CartesianChart.d.ts +92 -34
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/ChartContextBridge.d.ts +28 -0
  5. package/dts/chart/ChartContextBridge.d.ts.map +1 -0
  6. package/dts/chart/ChartProvider.d.ts +3 -0
  7. package/dts/chart/ChartProvider.d.ts.map +1 -1
  8. package/dts/chart/Path.d.ts +97 -32
  9. package/dts/chart/Path.d.ts.map +1 -1
  10. package/dts/chart/PeriodSelector.d.ts +6 -13
  11. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  12. package/dts/chart/area/Area.d.ts +39 -28
  13. package/dts/chart/area/Area.d.ts.map +1 -1
  14. package/dts/chart/area/AreaChart.d.ts +51 -10
  15. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  16. package/dts/chart/area/DottedArea.d.ts +21 -2
  17. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  18. package/dts/chart/area/GradientArea.d.ts +19 -13
  19. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  20. package/dts/chart/area/SolidArea.d.ts +17 -2
  21. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  22. package/dts/chart/axis/Axis.d.ts +86 -118
  23. package/dts/chart/axis/Axis.d.ts.map +1 -1
  24. package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
  25. package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
  26. package/dts/chart/axis/XAxis.d.ts +1 -1
  27. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  28. package/dts/chart/axis/YAxis.d.ts +2 -2
  29. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  30. package/dts/chart/axis/index.d.ts +1 -0
  31. package/dts/chart/axis/index.d.ts.map +1 -1
  32. package/dts/chart/bar/Bar.d.ts +49 -12
  33. package/dts/chart/bar/Bar.d.ts.map +1 -1
  34. package/dts/chart/bar/BarChart.d.ts +40 -19
  35. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  36. package/dts/chart/bar/BarPlot.d.ts +3 -1
  37. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  38. package/dts/chart/bar/BarStack.d.ts +41 -46
  39. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  40. package/dts/chart/bar/BarStackGroup.d.ts +2 -0
  41. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  42. package/dts/chart/bar/DefaultBar.d.ts +1 -1
  43. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  44. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  45. package/dts/chart/gradient/Gradient.d.ts +25 -0
  46. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  47. package/dts/chart/gradient/index.d.ts +2 -0
  48. package/dts/chart/gradient/index.d.ts.map +1 -0
  49. package/dts/chart/index.d.ts +4 -1
  50. package/dts/chart/index.d.ts.map +1 -1
  51. package/dts/chart/legend/DefaultLegendEntry.d.ts +5 -0
  52. package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
  53. package/dts/chart/legend/DefaultLegendShape.d.ts +5 -0
  54. package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
  55. package/dts/chart/legend/Legend.d.ts +168 -0
  56. package/dts/chart/legend/Legend.d.ts.map +1 -0
  57. package/dts/chart/legend/index.d.ts +4 -0
  58. package/dts/chart/legend/index.d.ts.map +1 -0
  59. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  60. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  61. package/dts/chart/line/DottedLine.d.ts +13 -5
  62. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  63. package/dts/chart/line/Line.d.ts +61 -27
  64. package/dts/chart/line/Line.d.ts.map +1 -1
  65. package/dts/chart/line/LineChart.d.ts +43 -9
  66. package/dts/chart/line/LineChart.d.ts.map +1 -1
  67. package/dts/chart/line/ReferenceLine.d.ts +68 -20
  68. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  69. package/dts/chart/line/SolidLine.d.ts +8 -5
  70. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  71. package/dts/chart/line/index.d.ts +1 -1
  72. package/dts/chart/line/index.d.ts.map +1 -1
  73. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  74. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  75. package/dts/chart/point/Point.d.ts +136 -0
  76. package/dts/chart/point/Point.d.ts.map +1 -0
  77. package/dts/chart/point/index.d.ts +3 -0
  78. package/dts/chart/point/index.d.ts.map +1 -0
  79. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +38 -0
  80. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  81. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  82. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  83. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +11 -0
  84. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  85. package/dts/chart/scrubber/Scrubber.d.ts +230 -42
  86. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  87. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +54 -0
  88. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  89. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +46 -0
  90. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  91. package/dts/chart/scrubber/ScrubberProvider.d.ts +6 -3
  92. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
  93. package/dts/chart/scrubber/index.d.ts +3 -0
  94. package/dts/chart/scrubber/index.d.ts.map +1 -1
  95. package/dts/chart/text/ChartText.d.ts +151 -77
  96. package/dts/chart/text/ChartText.d.ts.map +1 -1
  97. package/dts/chart/text/{SmartChartTextGroup.d.ts → ChartTextGroup.d.ts} +9 -3
  98. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  99. package/dts/chart/text/index.d.ts +1 -1
  100. package/dts/chart/text/index.d.ts.map +1 -1
  101. package/dts/chart/utils/axis.d.ts +25 -1
  102. package/dts/chart/utils/axis.d.ts.map +1 -1
  103. package/dts/chart/utils/bar.d.ts +34 -0
  104. package/dts/chart/utils/bar.d.ts.map +1 -1
  105. package/dts/chart/utils/chart.d.ts +52 -7
  106. package/dts/chart/utils/chart.d.ts.map +1 -1
  107. package/dts/chart/utils/context.d.ts +28 -7
  108. package/dts/chart/utils/context.d.ts.map +1 -1
  109. package/dts/chart/utils/gradient.d.ts +117 -0
  110. package/dts/chart/utils/gradient.d.ts.map +1 -0
  111. package/dts/chart/utils/index.d.ts +3 -0
  112. package/dts/chart/utils/index.d.ts.map +1 -1
  113. package/dts/chart/utils/path.d.ts +59 -0
  114. package/dts/chart/utils/path.d.ts.map +1 -1
  115. package/dts/chart/utils/point.d.ts +71 -7
  116. package/dts/chart/utils/point.d.ts.map +1 -1
  117. package/dts/chart/utils/scale.d.ts +102 -0
  118. package/dts/chart/utils/scale.d.ts.map +1 -1
  119. package/dts/chart/utils/scrubber.d.ts +40 -0
  120. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  121. package/dts/chart/utils/transition.d.ts +178 -0
  122. package/dts/chart/utils/transition.d.ts.map +1 -0
  123. package/esm/chart/CartesianChart.js +199 -75
  124. package/esm/chart/ChartContextBridge.js +159 -0
  125. package/esm/chart/ChartProvider.js +2 -2
  126. package/esm/chart/Path.js +200 -114
  127. package/esm/chart/PeriodSelector.js +7 -3
  128. package/esm/chart/__stories__/CartesianChart.stories.js +307 -134
  129. package/esm/chart/__stories__/ChartTransitions.stories.js +629 -0
  130. package/esm/chart/__stories__/PeriodSelector.stories.js +201 -75
  131. package/esm/chart/area/Area.js +27 -35
  132. package/esm/chart/area/AreaChart.js +17 -12
  133. package/esm/chart/area/DottedArea.js +64 -108
  134. package/esm/chart/area/GradientArea.js +37 -91
  135. package/esm/chart/area/SolidArea.js +24 -8
  136. package/esm/chart/area/__stories__/AreaChart.stories.js +1 -1
  137. package/esm/chart/axis/Axis.js +5 -39
  138. package/esm/chart/axis/DefaultAxisTickLabel.js +11 -0
  139. package/esm/chart/axis/XAxis.js +148 -66
  140. package/esm/chart/axis/YAxis.js +149 -65
  141. package/esm/chart/axis/__stories__/Axis.stories.js +259 -1
  142. package/esm/chart/axis/index.js +1 -0
  143. package/esm/chart/bar/Bar.js +7 -1
  144. package/esm/chart/bar/BarChart.js +17 -37
  145. package/esm/chart/bar/BarPlot.js +43 -35
  146. package/esm/chart/bar/BarStack.js +84 -37
  147. package/esm/chart/bar/BarStackGroup.js +7 -17
  148. package/esm/chart/bar/DefaultBar.js +29 -51
  149. package/esm/chart/bar/DefaultBarStack.js +34 -58
  150. package/esm/chart/bar/__stories__/BarChart.stories.js +948 -88
  151. package/esm/chart/gradient/Gradient.js +53 -0
  152. package/esm/chart/gradient/index.js +1 -0
  153. package/esm/chart/index.js +4 -1
  154. package/esm/chart/legend/DefaultLegendEntry.js +42 -0
  155. package/esm/chart/legend/DefaultLegendShape.js +64 -0
  156. package/esm/chart/legend/Legend.js +59 -0
  157. package/esm/chart/legend/__stories__/Legend.stories.js +574 -0
  158. package/esm/chart/legend/index.js +3 -0
  159. package/esm/chart/line/DefaultReferenceLineLabel.js +66 -0
  160. package/esm/chart/line/DottedLine.js +31 -14
  161. package/esm/chart/line/Line.js +96 -68
  162. package/esm/chart/line/LineChart.js +21 -14
  163. package/esm/chart/line/ReferenceLine.js +80 -63
  164. package/esm/chart/line/SolidLine.js +27 -10
  165. package/esm/chart/line/__stories__/LineChart.stories.js +1748 -2048
  166. package/esm/chart/line/__stories__/ReferenceLine.stories.js +177 -28
  167. package/esm/chart/line/index.js +1 -1
  168. package/esm/chart/point/DefaultPointLabel.js +39 -0
  169. package/esm/chart/point/Point.js +186 -0
  170. package/esm/chart/point/index.js +2 -0
  171. package/esm/chart/scrubber/DefaultScrubberBeacon.js +180 -0
  172. package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +43 -0
  173. package/esm/chart/scrubber/DefaultScrubberLabel.js +28 -0
  174. package/esm/chart/scrubber/Scrubber.js +130 -144
  175. package/esm/chart/scrubber/ScrubberBeaconGroup.js +165 -0
  176. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +208 -0
  177. package/esm/chart/scrubber/ScrubberProvider.js +46 -54
  178. package/esm/chart/scrubber/__stories__/Scrubber.stories.js +760 -0
  179. package/esm/chart/scrubber/index.js +3 -1
  180. package/esm/chart/text/ChartText.js +242 -174
  181. package/esm/chart/text/{SmartChartTextGroup.js → ChartTextGroup.js} +6 -5
  182. package/esm/chart/text/index.js +1 -1
  183. package/esm/chart/utils/axis.js +47 -31
  184. package/esm/chart/utils/bar.js +43 -0
  185. package/esm/chart/utils/chart.js +57 -3
  186. package/esm/chart/utils/gradient.js +305 -0
  187. package/esm/chart/utils/index.js +3 -0
  188. package/esm/chart/utils/path.js +84 -8
  189. package/esm/chart/utils/point.js +171 -17
  190. package/esm/chart/utils/scale.js +242 -2
  191. package/esm/chart/utils/scrubber.js +146 -0
  192. package/esm/chart/utils/transition.js +215 -0
  193. package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
  194. package/esm/sparkline/__stories__/Sparkline.stories.js +11 -7
  195. package/esm/sparkline/__stories__/SparklineGradient.stories.js +7 -4
  196. package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
  197. package/esm/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.js +51 -26
  198. package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
  199. package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +17 -9
  200. package/package.json +15 -10
  201. package/dts/chart/Point.d.ts +0 -103
  202. package/dts/chart/Point.d.ts.map +0 -1
  203. package/dts/chart/line/GradientLine.d.ts +0 -45
  204. package/dts/chart/line/GradientLine.d.ts.map +0 -1
  205. package/dts/chart/scrubber/ScrubberBeacon.d.ts +0 -75
  206. package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +0 -1
  207. package/dts/chart/text/SmartChartTextGroup.d.ts.map +0 -1
  208. package/esm/chart/Point.js +0 -111
  209. package/esm/chart/__stories__/Chart.stories.js +0 -79
  210. package/esm/chart/line/GradientLine.js +0 -62
  211. package/esm/chart/scrubber/ScrubberBeacon.js +0 -199
@@ -0,0 +1,208 @@
1
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
+ import { memo, useCallback, useMemo, useState } from 'react';
3
+ import { useAnimatedReaction, useDerivedValue, useSharedValue } from 'react-native-reanimated';
4
+ import { useCartesianChartContext } from '../ChartProvider';
5
+ import { applySerializableScale, unwrapAnimatedValue, useScrubberContext } from '../utils';
6
+ import { calculateLabelYPositions, getLabelPosition } from '../utils/scrubber';
7
+ import { buildTransition, defaultTransition, getTransition } from '../utils/transition';
8
+ import { DefaultScrubberBeaconLabel } from './DefaultScrubberBeaconLabel';
9
+ import { jsx as _jsx } from "react/jsx-runtime";
10
+ const PositionedLabel = /*#__PURE__*/memo(_ref => {
11
+ let {
12
+ index,
13
+ positions,
14
+ position,
15
+ isIdle,
16
+ updateTransition,
17
+ label,
18
+ color,
19
+ seriesId,
20
+ onDimensionsChange,
21
+ BeaconLabelComponent,
22
+ labelHorizontalOffset,
23
+ labelFont
24
+ } = _ref;
25
+ const opacity = useDerivedValue(() => positions.value[index] !== null ? 1 : 0, [positions, index]);
26
+ const x = useDerivedValue(() => {
27
+ var _positions$value$inde, _positions$value$inde2;
28
+ return (_positions$value$inde = (_positions$value$inde2 = positions.value[index]) == null ? void 0 : _positions$value$inde2.x) != null ? _positions$value$inde : 0;
29
+ }, [positions, index]);
30
+ const targetY = useDerivedValue(() => {
31
+ var _positions$value$inde3, _positions$value$inde4;
32
+ return (_positions$value$inde3 = (_positions$value$inde4 = positions.value[index]) == null ? void 0 : _positions$value$inde4.y) != null ? _positions$value$inde3 : 0;
33
+ }, [positions, index]);
34
+ const animatedY = useSharedValue(0);
35
+ useAnimatedReaction(() => ({
36
+ y: targetY.value,
37
+ idle: unwrapAnimatedValue(isIdle)
38
+ }), (current, previous) => {
39
+ if (previous === null || !previous.idle || !current.idle) {
40
+ animatedY.value = current.y;
41
+ } else {
42
+ animatedY.value = buildTransition(current.y, updateTransition);
43
+ }
44
+ }, [updateTransition]);
45
+ const dx = useDerivedValue(() => {
46
+ return position.value === 'right' ? labelHorizontalOffset : -labelHorizontalOffset;
47
+ }, [position, labelHorizontalOffset]);
48
+ const horizontalAlignment = useDerivedValue(() => position.value === 'right' ? 'left' : 'right', [position]);
49
+ return /*#__PURE__*/_jsx(BeaconLabelComponent, {
50
+ color: color,
51
+ dx: dx,
52
+ font: labelFont,
53
+ horizontalAlignment: horizontalAlignment,
54
+ label: label,
55
+ onDimensionsChange: d => onDimensionsChange(seriesId, d),
56
+ opacity: opacity,
57
+ seriesId: seriesId,
58
+ x: x,
59
+ y: animatedY
60
+ });
61
+ });
62
+ export const ScrubberBeaconLabelGroup = /*#__PURE__*/memo(_ref2 => {
63
+ let {
64
+ labels,
65
+ labelMinGap = 4,
66
+ labelHorizontalOffset = 16,
67
+ labelFont,
68
+ labelPreferredSide = 'right',
69
+ BeaconLabelComponent = DefaultScrubberBeaconLabel,
70
+ transitions
71
+ } = _ref2;
72
+ const {
73
+ getSeries,
74
+ getSeriesData,
75
+ getXSerializableScale,
76
+ getYSerializableScale,
77
+ getXAxis,
78
+ drawingArea,
79
+ dataLength,
80
+ animate
81
+ } = useCartesianChartContext();
82
+ const {
83
+ scrubberPosition
84
+ } = useScrubberContext();
85
+ const isIdle = useDerivedValue(() => {
86
+ return scrubberPosition.value === undefined;
87
+ }, [scrubberPosition]);
88
+ const updateTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.update, animate, defaultTransition), [transitions == null ? void 0 : transitions.update, animate]);
89
+ const [labelDimensions, setLabelDimensions] = useState({});
90
+ const handleDimensionsChange = useCallback((id, dimensions) => {
91
+ setLabelDimensions(prev => {
92
+ const existing = prev[id];
93
+ if (existing && existing.width === dimensions.width && existing.height === dimensions.height) {
94
+ return prev;
95
+ }
96
+ return _extends({}, prev, {
97
+ [id]: dimensions
98
+ });
99
+ });
100
+ }, []);
101
+ const seriesInfo = useMemo(() => {
102
+ return labels.map(label => {
103
+ const series = getSeries(label.seriesId);
104
+ if (!series) return null;
105
+ const sourceData = getSeriesData(label.seriesId);
106
+ const yScale = getYSerializableScale(series.yAxisId);
107
+ return {
108
+ seriesId: label.seriesId,
109
+ sourceData,
110
+ yScale
111
+ };
112
+ }).filter(info => info !== null);
113
+ }, [labels, getSeries, getSeriesData, getYSerializableScale]);
114
+ const xScale = getXSerializableScale();
115
+ const xAxis = getXAxis();
116
+ const dataIndex = useDerivedValue(() => {
117
+ var _scrubberPosition$val;
118
+ return (_scrubberPosition$val = scrubberPosition.value) != null ? _scrubberPosition$val : Math.max(0, dataLength - 1);
119
+ }, [scrubberPosition, dataLength]);
120
+ const dataX = useDerivedValue(() => {
121
+ if (xAxis != null && xAxis.data && Array.isArray(xAxis.data) && xAxis.data[dataIndex.value] !== undefined) {
122
+ const dataValue = xAxis.data[dataIndex.value];
123
+ return typeof dataValue === 'string' ? dataIndex.value : dataValue;
124
+ }
125
+ return dataIndex.value;
126
+ }, [xAxis, dataIndex]);
127
+ const allLabelPositions = useDerivedValue(() => {
128
+ const sharedPixelX = dataX.value !== undefined && xScale ? applySerializableScale(dataX.value, xScale) : 0;
129
+ const desiredPositions = seriesInfo.map(info => {
130
+ let dataY;
131
+ if (xScale && info.yScale) {
132
+ if (info.sourceData && dataIndex.value !== undefined && dataIndex.value >= 0 && dataIndex.value < info.sourceData.length) {
133
+ const dataValue = info.sourceData[dataIndex.value];
134
+ if (Array.isArray(dataValue)) {
135
+ const validValues = dataValue.filter(val => val !== null);
136
+ if (validValues.length >= 1) {
137
+ dataY = validValues[validValues.length - 1];
138
+ }
139
+ }
140
+ }
141
+ }
142
+ if (dataY !== undefined && info.yScale) {
143
+ return {
144
+ seriesId: info.seriesId,
145
+ x: sharedPixelX,
146
+ desiredY: applySerializableScale(dataY, info.yScale)
147
+ };
148
+ }
149
+
150
+ // Return null for invalid data
151
+ return null;
152
+ });
153
+ const maxLabelHeight = Math.max(...Object.values(labelDimensions).map(dim => dim.height));
154
+ const maxLabelWidth = Math.max(...Object.values(labelDimensions).map(dim => dim.width));
155
+ const validPositions = desiredPositions.filter(pos => pos !== null);
156
+
157
+ // Convert to LabelDimension format expected by utility
158
+ const dimensions = validPositions.map(pos => {
159
+ var _trackedDimensions$wi, _trackedDimensions$he;
160
+ const trackedDimensions = labelDimensions[pos.seriesId];
161
+ return {
162
+ seriesId: pos.seriesId,
163
+ width: (_trackedDimensions$wi = trackedDimensions == null ? void 0 : trackedDimensions.width) != null ? _trackedDimensions$wi : maxLabelWidth,
164
+ height: (_trackedDimensions$he = trackedDimensions == null ? void 0 : trackedDimensions.height) != null ? _trackedDimensions$he : maxLabelHeight,
165
+ preferredX: pos.x,
166
+ preferredY: pos.desiredY
167
+ };
168
+ });
169
+
170
+ // Calculate Y positions with collision resolution for valid positions only
171
+ const yPositions = calculateLabelYPositions(dimensions, drawingArea, maxLabelHeight, labelMinGap);
172
+
173
+ // Return all positions (including null ones)
174
+ return desiredPositions.map(pos => {
175
+ var _yPositions$get;
176
+ if (!pos) return null;
177
+ return {
178
+ seriesId: pos.seriesId,
179
+ x: pos.x,
180
+ y: (_yPositions$get = yPositions.get(pos.seriesId)) != null ? _yPositions$get : pos.desiredY
181
+ };
182
+ });
183
+ }, [seriesInfo, dataIndex, dataX, xScale, labelDimensions, labelMinGap]);
184
+ const currentPosition = useDerivedValue(() => {
185
+ const pixelX = dataX.value !== undefined && xScale ? applySerializableScale(dataX.value, xScale) : 0;
186
+ const maxWidth = Math.max(...Object.values(labelDimensions).map(dim => dim.width));
187
+ const position = getLabelPosition(pixelX, maxWidth, drawingArea, labelHorizontalOffset, labelPreferredSide);
188
+ return position;
189
+ }, [dataX, xScale, labelDimensions, drawingArea, labelHorizontalOffset, labelPreferredSide]);
190
+ return seriesInfo.map((info, index) => {
191
+ const labelInfo = labels.find(label => label.seriesId === info.seriesId);
192
+ if (!labelInfo) return;
193
+ return /*#__PURE__*/_jsx(PositionedLabel, {
194
+ BeaconLabelComponent: BeaconLabelComponent,
195
+ color: labelInfo.color,
196
+ index: index,
197
+ isIdle: isIdle,
198
+ label: labelInfo.label,
199
+ labelFont: labelFont,
200
+ labelHorizontalOffset: labelHorizontalOffset,
201
+ onDimensionsChange: handleDimensionsChange,
202
+ position: currentPosition,
203
+ positions: allLabelPositions,
204
+ seriesId: info.seriesId,
205
+ updateTransition: updateTransition
206
+ }, info.seriesId);
207
+ });
208
+ });
@@ -1,10 +1,11 @@
1
- import React, { useCallback, useMemo, useState } from 'react';
1
+ import React, { useCallback, useMemo } from 'react';
2
2
  import { Platform } from 'react-native';
3
3
  import { Gesture, GestureDetector } from 'react-native-gesture-handler';
4
- import { runOnJS } from 'react-native-reanimated';
4
+ import { runOnJS, useAnimatedReaction, useSharedValue } from 'react-native-reanimated';
5
5
  import { Haptics } from '@coinbase/cds-mobile/utils/haptics';
6
6
  import { useCartesianChartContext } from '../ChartProvider';
7
- import { isCategoricalScale, ScrubberContext } from '../utils';
7
+ import { invertSerializableScale, ScrubberContext } from '../utils';
8
+ import { getPointOnSerializableScale } from '../utils/point';
8
9
  import { jsx as _jsx } from "react/jsx-runtime";
9
10
  /**
10
11
  * A component which encapsulates the ScrubberContext.
@@ -22,25 +23,25 @@ export const ScrubberProvider = _ref => {
22
23
  throw new Error('ScrubberProvider must be used within a ChartContext');
23
24
  }
24
25
  const {
25
- getXScale,
26
- getXAxis,
27
- series
26
+ getXSerializableScale,
27
+ getXAxis
28
28
  } = chartContext;
29
- const [scrubberPosition, setScrubberPosition] = useState(undefined);
29
+ const scrubberPosition = useSharedValue(undefined);
30
+ const xAxis = useMemo(() => getXAxis(), [getXAxis]);
31
+ const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
30
32
  const getDataIndexFromX = useCallback(touchX => {
31
- const xScale = getXScale();
32
- const xAxis = getXAxis();
33
+ 'worklet';
34
+
33
35
  if (!xScale || !xAxis) return 0;
34
- if (isCategoricalScale(xScale)) {
35
- var _ref2, _xScale$domain, _xScale$bandwidth;
36
- const categories = (_ref2 = (_xScale$domain = xScale.domain == null ? void 0 : xScale.domain()) != null ? _xScale$domain : xAxis.data) != null ? _ref2 : [];
37
- const bandwidth = (_xScale$bandwidth = xScale.bandwidth == null ? void 0 : xScale.bandwidth()) != null ? _xScale$bandwidth : 0;
36
+ if (xScale.type === 'band') {
37
+ const [domainMin, domainMax] = xScale.domain;
38
+ const categoryCount = domainMax - domainMin + 1;
38
39
  let closestIndex = 0;
39
40
  let closestDistance = Infinity;
40
- for (let i = 0; i < categories.length; i++) {
41
- const xPos = xScale(i);
41
+ for (let i = 0; i < categoryCount; i++) {
42
+ const xPos = getPointOnSerializableScale(i, xScale);
42
43
  if (xPos !== undefined) {
43
- const distance = Math.abs(touchX - (xPos + bandwidth / 2));
44
+ const distance = Math.abs(touchX - xPos);
44
45
  if (distance < closestDistance) {
45
46
  closestDistance = distance;
46
47
  closestIndex = i;
@@ -58,7 +59,7 @@ export const ScrubberProvider = _ref => {
58
59
  let closestDistance = Infinity;
59
60
  for (let i = 0; i < numericData.length; i++) {
60
61
  const xValue = numericData[i];
61
- const xPos = xScale(xValue);
62
+ const xPos = getPointOnSerializableScale(xValue, xScale);
62
63
  if (xPos !== undefined) {
63
64
  const distance = Math.abs(touchX - xPos);
64
65
  if (distance < closestDistance) {
@@ -70,62 +71,53 @@ export const ScrubberProvider = _ref => {
70
71
  return closestIndex;
71
72
  } else {
72
73
  var _domain$min, _domain$max;
73
- const xValue = xScale.invert(touchX);
74
+ const xValue = invertSerializableScale(touchX, xScale);
74
75
  const dataIndex = Math.round(xValue);
75
76
  const domain = xAxis.domain;
76
77
  return Math.max((_domain$min = domain.min) != null ? _domain$min : 0, Math.min(dataIndex, (_domain$max = domain.max) != null ? _domain$max : 0));
77
78
  }
78
79
  }
79
- }, [getXScale, getXAxis]);
80
- const handlePositionUpdate = useCallback(x => {
81
- if (!enableScrubbing || !series || series.length === 0) return;
82
- const dataIndex = getDataIndexFromX(x);
83
- if (dataIndex !== scrubberPosition) {
84
- setScrubberPosition(dataIndex);
85
- onScrubberPositionChange == null || onScrubberPositionChange(dataIndex);
86
- }
87
- }, [enableScrubbing, series, getDataIndexFromX, scrubberPosition, onScrubberPositionChange]);
88
- const handleInteractionEnd = useCallback(() => {
89
- if (!enableScrubbing) return;
90
- setScrubberPosition(undefined);
91
- onScrubberPositionChange == null || onScrubberPositionChange(undefined);
92
- }, [enableScrubbing, onScrubberPositionChange]);
93
-
94
- // Gesture handler callbacks
95
- const handleOnStartJsThread = useCallback(() => {
80
+ }, [xAxis, xScale]);
81
+ const handleStartEndHaptics = useCallback(() => {
96
82
  void Haptics.lightImpact();
97
- // Could add onScrubStart callback here if needed
98
83
  }, []);
99
- const handleOnEndOrCancelledJsThread = useCallback(() => {
100
- handleInteractionEnd();
101
- }, [handleInteractionEnd]);
102
- const handleOnUpdateJsThread = useCallback(x => {
103
- handlePositionUpdate(x);
104
- }, [handlePositionUpdate]);
105
- const handleOnEndJsThread = useCallback(() => {
106
- void Haptics.lightImpact();
107
- handleOnEndOrCancelledJsThread();
108
- }, [handleOnEndOrCancelledJsThread]);
84
+ useAnimatedReaction(() => scrubberPosition.value, (currentValue, previousValue) => {
85
+ // Confirm changes here and inside of our gesture handler before calling JS thread
86
+ // To prevent any rerenders
87
+ if (onScrubberPositionChange !== undefined && currentValue !== previousValue) {
88
+ runOnJS(onScrubberPositionChange)(currentValue);
89
+ }
90
+ }, [onScrubberPositionChange]);
109
91
 
110
92
  // Create the long press pan gesture
111
93
  const longPressGesture = useMemo(() => Gesture.Pan().activateAfterLongPress(110).shouldCancelWhenOutside(!allowOverflowGestures).onStart(function onStart(event) {
112
- runOnJS(handleOnStartJsThread)();
94
+ runOnJS(handleStartEndHaptics)();
113
95
 
114
96
  // Android does not trigger onUpdate when the gesture starts. This achieves consistent behavior across both iOS and Android
115
97
  if (Platform.OS === 'android') {
116
- runOnJS(handleOnUpdateJsThread)(event.x);
98
+ const newScrubberPosition = getDataIndexFromX(event.x);
99
+ if (newScrubberPosition !== scrubberPosition.value) {
100
+ scrubberPosition.value = newScrubberPosition;
101
+ }
117
102
  }
118
103
  }).onUpdate(function onUpdate(event) {
119
- runOnJS(handleOnUpdateJsThread)(event.x);
104
+ const newScrubberPosition = getDataIndexFromX(event.x);
105
+ if (newScrubberPosition !== scrubberPosition.value) {
106
+ scrubberPosition.value = newScrubberPosition;
107
+ }
120
108
  }).onEnd(function onEnd() {
121
- runOnJS(handleOnEndJsThread)();
109
+ if (enableScrubbing) {
110
+ runOnJS(handleStartEndHaptics)();
111
+ scrubberPosition.value = undefined;
112
+ }
122
113
  }).onTouchesCancelled(function onTouchesCancelled() {
123
- runOnJS(handleOnEndOrCancelledJsThread)();
124
- }), [allowOverflowGestures, handleOnStartJsThread, handleOnUpdateJsThread, handleOnEndJsThread, handleOnEndOrCancelledJsThread]);
114
+ if (enableScrubbing) {
115
+ scrubberPosition.value = undefined;
116
+ }
117
+ }), [allowOverflowGestures, handleStartEndHaptics, getDataIndexFromX, scrubberPosition, enableScrubbing]);
125
118
  const contextValue = useMemo(() => ({
126
119
  enableScrubbing: !!enableScrubbing,
127
- scrubberPosition,
128
- onScrubberPositionChange: setScrubberPosition
120
+ scrubberPosition
129
121
  }), [enableScrubbing, scrubberPosition]);
130
122
  const content = /*#__PURE__*/_jsx(ScrubberContext.Provider, {
131
123
  value: contextValue,