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

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 +122 -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 +220 -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
@@ -2,9 +2,17 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
2
2
  import React, { memo, useMemo } from 'react';
3
3
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
4
4
  import { useCartesianChartContext } from '../ChartProvider';
5
+ import { evaluateGradientAtValue, getGradientStops } from '../utils/gradient';
6
+ import { convertToSerializableScale } from '../utils/scale';
5
7
  import { Bar } from './Bar';
6
8
  import { DefaultBarStack } from './DefaultBarStack';
7
9
  import { jsx as _jsx } from "react/jsx-runtime";
10
+ const EPSILON = 1e-4;
11
+
12
+ /**
13
+ * Extended series type that includes bar-specific properties.
14
+ */
15
+
8
16
  /**
9
17
  * BarStack component that renders a single stack of bars at a specific category index.
10
18
  * Handles the stacking logic for bars within a single category.
@@ -26,14 +34,18 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
26
34
  stackGap,
27
35
  barMinSize,
28
36
  stackMinSize,
29
- roundBaseline
37
+ roundBaseline,
38
+ transitions,
39
+ transition
30
40
  } = _ref;
31
41
  const theme = useTheme();
32
42
  const {
33
43
  getSeriesData,
34
- getXAxis
44
+ getXAxis,
45
+ getXScale
35
46
  } = useCartesianChartContext();
36
47
  const xAxis = getXAxis();
48
+ const xScale = getXScale();
37
49
  const baseline = useMemo(() => {
38
50
  var _yScale;
39
51
  const domain = yScale.domain();
@@ -42,6 +54,25 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
42
54
  const baseline = (_yScale = yScale(baselineValue)) != null ? _yScale : rect.y + rect.height;
43
55
  return Math.max(rect.y, Math.min(baseline, rect.y + rect.height));
44
56
  }, [rect.height, rect.y, yScale]);
57
+ const seriesGradients = useMemo(() => {
58
+ return series.map(s => {
59
+ if (!s.gradient || !xScale || !yScale) return;
60
+ const gradientScale = s.gradient.axis === 'x' ? xScale : yScale;
61
+ const serializableScale = convertToSerializableScale(gradientScale);
62
+ if (!serializableScale) return;
63
+ const domain = {
64
+ min: serializableScale.domain[0],
65
+ max: serializableScale.domain[1]
66
+ };
67
+ const stops = getGradientStops(s.gradient.stops, domain);
68
+ return {
69
+ seriesId: s.id,
70
+ gradient: s.gradient,
71
+ scale: serializableScale,
72
+ stops
73
+ };
74
+ });
75
+ }, [series, xScale, yScale]);
45
76
 
46
77
  // Calculate bars for this specific category
47
78
  const {
@@ -98,6 +129,24 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
98
129
  // Update stack bounds
99
130
  minY = Math.min(minY, y);
100
131
  maxY = Math.max(maxY, y + height);
132
+
133
+ // Determine fill color, respecting gradient if present
134
+ let barFill = s.color || theme.color.fgPrimary;
135
+
136
+ // Evaluate gradient if provided (using precomputed stops)
137
+ const seriesGradientConfig = seriesGradients.find(g => (g == null ? void 0 : g.seriesId) === s.id);
138
+ if (seriesGradientConfig) {
139
+ var _seriesGradientConfig;
140
+ const axis = (_seriesGradientConfig = seriesGradientConfig.gradient.axis) != null ? _seriesGradientConfig : 'y';
141
+ // For x-axis gradient, use the categoryIndex
142
+ // For y-axis gradient, use the actual data value
143
+ const dataValue = axis === 'x' ? categoryIndex : top;
144
+ const evaluatedColor = evaluateGradientAtValue(seriesGradientConfig.stops, dataValue, seriesGradientConfig.scale);
145
+ if (evaluatedColor) {
146
+ // Only apply gradient color if fill is not explicitly set
147
+ barFill = evaluatedColor;
148
+ }
149
+ }
101
150
  allBars.push({
102
151
  seriesId: s.id,
103
152
  x,
@@ -106,15 +155,11 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
106
155
  height,
107
156
  dataY: value,
108
157
  // Store the actual data value
109
- // Use series-specific properties, falling back to defaults
158
+ fill: barFill,
159
+ // Check if the bar should be rounded based on the baseline, with an epsilon to handle floating-point rounding
160
+ roundTop: roundBaseline || Math.abs(barTop - baseline) >= EPSILON,
161
+ roundBottom: roundBaseline || Math.abs(barBottom - baseline) >= EPSILON,
110
162
  BarComponent: s.BarComponent,
111
- fill: s.fill || s.color || theme.color.fgPrimary,
112
- fillOpacity: s.fillOpacity,
113
- stroke: s.stroke,
114
- strokeWidth: s.strokeWidth,
115
- // Pass context data for custom components
116
- roundTop: roundBaseline || barTop !== baseline,
117
- roundBottom: roundBaseline || barBottom !== baseline,
118
163
  shouldApplyGap
119
164
  });
120
165
  });
@@ -200,8 +245,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
200
245
  if (bar.height < barMinSize) {
201
246
  var _yScale4, _yScale5, _yScale6, _yScale7;
202
247
  const heightIncrease = barMinSize - bar.height;
203
- const bottom = 0;
204
- const top = 0;
248
+ const [bottom, top] = bar.dataY.sort((a, b) => a - b);
205
249
 
206
250
  // Determine how to expand the bar
207
251
  let newBottom = bottom;
@@ -344,8 +388,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
344
388
 
345
389
  const bar = allBars[0];
346
390
  const heightIncrease = stackMinSize - bar.height;
347
- const bottom = 0;
348
- const top = 0;
391
+ const [bottom, top] = bar.dataY.sort((a, b) => a - b);
349
392
 
350
393
  // Determine how to expand the bar (same logic as barMinSize)
351
394
  let newBottom = bottom;
@@ -474,37 +517,41 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
474
517
  bars: allBars,
475
518
  stackRect: stackBounds
476
519
  };
477
- }, [series, x, width, getSeriesData, categoryIndex, roundBaseline, baseline, stackGap, barMinSize, stackMinSize, yScale, theme.color.fgPrimary]);
520
+ }, [series, x, width, getSeriesData, categoryIndex, roundBaseline, baseline, stackGap, barMinSize, stackMinSize, yScale, seriesGradients, theme.color.fgPrimary]);
478
521
  const xData = xAxis != null && xAxis.data && Array.isArray(xAxis.data) && typeof xAxis.data[0] === 'number' ? xAxis.data : undefined;
479
522
  const dataX = xData ? xData[categoryIndex] : categoryIndex;
480
- const barElements = bars.map((bar, index) => {
481
- var _bar$fillOpacity, _bar$stroke, _bar$strokeWidth;
482
- return /*#__PURE__*/_jsx(Bar, {
483
- BarComponent: bar.BarComponent || defaultBarComponent,
484
- borderRadius: borderRadius,
485
- dataX: dataX,
486
- dataY: bar.dataY,
487
- fill: bar.fill,
488
- fillOpacity: (_bar$fillOpacity = bar.fillOpacity) != null ? _bar$fillOpacity : defaultFillOpacity,
489
- height: bar.height,
490
- originY: baseline,
491
- roundBottom: bar.roundBottom,
492
- roundTop: bar.roundTop,
493
- stroke: (_bar$stroke = bar.stroke) != null ? _bar$stroke : defaultStroke,
494
- strokeWidth: (_bar$strokeWidth = bar.strokeWidth) != null ? _bar$strokeWidth : defaultStrokeWidth,
495
- width: bar.width,
496
- x: bar.x,
497
- y: bar.y
498
- }, bar.seriesId + "-" + categoryIndex + "-" + index);
499
- });
500
- const stackRoundBottom = roundBaseline || stackRect.y + stackRect.height !== baseline;
501
- const stackRoundTop = roundBaseline || stackRect.y !== baseline;
523
+ const barElements = bars.map((bar, index) => /*#__PURE__*/_jsx(Bar, {
524
+ BarComponent: bar.BarComponent || defaultBarComponent,
525
+ borderRadius: borderRadius,
526
+ dataX: dataX,
527
+ dataY: bar.dataY,
528
+ fill: bar.fill,
529
+ fillOpacity: defaultFillOpacity,
530
+ height: bar.height,
531
+ originY: baseline,
532
+ roundBottom: bar.roundBottom,
533
+ roundTop: bar.roundTop,
534
+ seriesId: bar.seriesId,
535
+ stroke: defaultStroke,
536
+ strokeWidth: defaultStrokeWidth,
537
+ transition: transition,
538
+ transitions: transitions,
539
+ width: bar.width,
540
+ x: bar.x,
541
+ y: bar.y
542
+ }, bar.seriesId + "-" + categoryIndex + "-" + index));
543
+
544
+ // Check if the bar should be rounded based on the baseline, with an epsilon to handle floating-point rounding
545
+ const stackRoundBottom = roundBaseline || Math.abs(stackRect.y + stackRect.height - baseline) >= EPSILON;
546
+ const stackRoundTop = roundBaseline || Math.abs(stackRect.y - baseline) >= EPSILON;
502
547
  return /*#__PURE__*/_jsx(BarStackComponent, {
503
548
  borderRadius: borderRadius,
504
549
  categoryIndex: categoryIndex,
505
550
  height: stackRect.height,
506
551
  roundBottom: stackRoundBottom,
507
552
  roundTop: stackRoundTop,
553
+ transition: transition,
554
+ transitions: transitions,
508
555
  width: stackRect.width,
509
556
  x: stackRect.x,
510
557
  y: stackRect.y,
@@ -19,26 +19,15 @@ export const BarStackGroup = /*#__PURE__*/memo(_ref => {
19
19
  } = _ref,
20
20
  props = _objectWithoutPropertiesLoose(_ref, _excluded);
21
21
  const {
22
- getSeriesData,
23
22
  getXScale,
24
23
  getYScale,
25
- drawingArea
24
+ drawingArea,
25
+ dataLength
26
26
  } = useCartesianChartContext();
27
27
  const xScale = getXScale();
28
28
  const yScale = getYScale(yAxisId);
29
- const maxDataLength = useMemo(() => {
30
- if (!series || series.length === 0) return 0;
31
- let maxLength = 0;
32
- series.forEach(s => {
33
- const data = getSeriesData(s.id);
34
- if (data && data.length > maxLength) {
35
- maxLength = data.length;
36
- }
37
- });
38
- return maxLength;
39
- }, [series, getSeriesData]);
40
29
  const stackConfigs = useMemo(() => {
41
- if (!xScale || !yScale || !drawingArea || maxDataLength === 0) return [];
30
+ if (!xScale || !yScale || !drawingArea || dataLength === 0) return [];
42
31
  if (!isCategoricalScale(xScale)) {
43
32
  return [];
44
33
  }
@@ -51,7 +40,8 @@ export const BarStackGroup = /*#__PURE__*/memo(_ref => {
51
40
  const configs = [];
52
41
 
53
42
  // Calculate position for each category
54
- for (let categoryIndex = 0; categoryIndex < maxDataLength; categoryIndex++) {
43
+ // todo: look at using xDomain for this instead of dataLength
44
+ for (let categoryIndex = 0; categoryIndex < dataLength; categoryIndex++) {
55
45
  // Get x position for this category
56
46
  const categoryX = xScale(categoryIndex);
57
47
  if (categoryX !== undefined) {
@@ -65,9 +55,9 @@ export const BarStackGroup = /*#__PURE__*/memo(_ref => {
65
55
  }
66
56
  }
67
57
  return configs;
68
- }, [xScale, yScale, drawingArea, maxDataLength, stackIndex, totalStacks, barPadding]);
58
+ }, [xScale, yScale, drawingArea, dataLength, stackIndex, totalStacks, barPadding]);
69
59
  if (xScale && !isCategoricalScale(xScale)) {
70
- throw new Error('BarStackGroup requires a band scale for x-axis. See https://cds.coinbase.com/components/graphs/XAxis/#scale-type');
60
+ throw new Error('BarStackGroup requires a band scale for x-axis. See https://cds.coinbase.com/components/charts/XAxis/#scale-type');
71
61
  }
72
62
  if (!yScale || !drawingArea || stackConfigs.length === 0) return;
73
63
  return stackConfigs.map(_ref2 => {
@@ -1,14 +1,12 @@
1
- import { memo, useCallback, useEffect, useMemo, useRef } from 'react';
2
- import { runOnJS, useAnimatedReaction, useSharedValue, withTiming } from 'react-native-reanimated';
3
- import { Path } from 'react-native-svg';
4
- import { usePreviousValue } from '@coinbase/cds-common/hooks/usePreviousValue';
1
+ import { memo, useMemo } from 'react';
5
2
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
6
- import * as interpolate from 'd3-interpolate-path';
7
3
  import { useCartesianChartContext } from '../ChartProvider';
8
- import { getBarPath } from '../utils';
4
+ import { Path } from '../Path';
5
+ import { defaultBarEnterTransition, getBarPath, withStaggerDelayTransition } from '../utils';
6
+ import { defaultTransition, getTransition } from '../utils/transition';
9
7
  import { jsx as _jsx } from "react/jsx-runtime";
10
8
  /**
11
- * Default bar component that renders a solid bar with animation.
9
+ * Default bar component that renders a solid bar with animation support.
12
10
  */
13
11
  export const DefaultBar = /*#__PURE__*/memo(_ref => {
14
12
  let {
@@ -16,63 +14,43 @@ export const DefaultBar = /*#__PURE__*/memo(_ref => {
16
14
  y,
17
15
  width,
18
16
  height,
19
- borderRadius,
17
+ borderRadius = 4,
20
18
  roundTop,
21
19
  roundBottom,
22
20
  d,
23
21
  fill,
24
22
  fillOpacity = 1,
25
23
  stroke,
26
- strokeWidth
24
+ strokeWidth,
25
+ originY,
26
+ transitions,
27
+ transition
27
28
  } = _ref;
28
- const pathRef = useRef(null);
29
29
  const {
30
- animate
30
+ animate,
31
+ drawingArea
31
32
  } = useCartesianChartContext();
32
33
  const theme = useTheme();
33
- const animationProgress = useSharedValue(0);
34
- const targetPath = useMemo(() => {
35
- return d || getBarPath(x, y, width, height, borderRadius != null ? borderRadius : 0, roundTop != null ? roundTop : true, roundBottom != null ? roundBottom : true);
36
- }, [d, x, y, width, height, borderRadius, roundTop, roundBottom]);
37
- const previousPath = usePreviousValue(targetPath);
38
- const fromPath = useMemo(() => {
39
- if (!animate) return targetPath;
40
- return previousPath || targetPath;
41
- }, [animate, previousPath, targetPath]);
42
- const pathInterpolator = useMemo(() => interpolate.interpolatePath(fromPath, targetPath), [fromPath, targetPath]);
43
- const updatePath = useCallback(progress => {
44
- var _pathRef$current;
45
- const val = Number(progress.toFixed(4));
46
- (_pathRef$current = pathRef.current) == null || _pathRef$current.setNativeProps({
47
- d: pathInterpolator(val)
48
- });
49
- }, [pathInterpolator]);
50
34
  const defaultFill = fill || theme.color.fgPrimary;
51
- useAnimatedReaction(() => animationProgress.value, progress => {
52
- 'worklet';
53
-
54
- runOnJS(updatePath)(progress);
55
- }, [updatePath]);
56
- useEffect(() => {
57
- if (!pathRef.current) return;
58
- if (!animate) {
59
- pathRef.current.setNativeProps({
60
- d: targetPath
61
- });
62
- animationProgress.value = 1;
63
- return;
64
- }
65
- animationProgress.value = 0;
66
- animationProgress.value = withTiming(1, {
67
- duration: 200
68
- });
69
- }, [animate, animationProgress, targetPath]);
35
+ const normalizedX = useMemo(() => drawingArea.width > 0 ? (x - drawingArea.x) / drawingArea.width : 0, [x, drawingArea.x, drawingArea.width]);
36
+ const enterTransition = useMemo(() => withStaggerDelayTransition(getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultBarEnterTransition), normalizedX), [transitions == null ? void 0 : transitions.enter, animate, normalizedX]);
37
+ const updateTransition = useMemo(() => withStaggerDelayTransition(getTransition((transitions == null ? void 0 : transitions.update) !== undefined ? transitions.update : transition, animate, defaultTransition), normalizedX), [transitions == null ? void 0 : transitions.update, transition, animate, normalizedX]);
38
+ const initialPath = useMemo(() => {
39
+ const baselineY = originY != null ? originY : y + height;
40
+ return getBarPath(x, baselineY, width, 1, borderRadius, !!roundTop, !!roundBottom);
41
+ }, [x, originY, y, height, width, borderRadius, roundTop, roundBottom]);
70
42
  return /*#__PURE__*/_jsx(Path, {
71
- ref: pathRef,
72
- d: fromPath,
73
- fill: defaultFill,
43
+ animate: animate,
44
+ clipPath: null,
45
+ d: d,
46
+ fill: stroke ? 'none' : defaultFill,
74
47
  fillOpacity: fillOpacity,
48
+ initialPath: initialPath,
75
49
  stroke: stroke,
76
- strokeWidth: strokeWidth
50
+ strokeWidth: strokeWidth,
51
+ transitions: {
52
+ enter: enterTransition,
53
+ update: updateTransition
54
+ }
77
55
  });
78
56
  });
@@ -1,11 +1,10 @@
1
- import { memo, useCallback, useEffect, useId, useMemo, useRef } from 'react';
2
- import { runOnJS, useAnimatedReaction, useSharedValue, withTiming } from 'react-native-reanimated';
3
- import { ClipPath, Defs, G, Path } from 'react-native-svg';
4
- import { usePreviousValue } from '@coinbase/cds-common/hooks/usePreviousValue';
5
- import * as interpolate from 'd3-interpolate-path';
1
+ import { memo, useMemo } from 'react';
2
+ import { Group } from '@shopify/react-native-skia';
6
3
  import { useCartesianChartContext } from '../ChartProvider';
7
4
  import { getBarPath } from '../utils';
8
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { defaultBarEnterTransition, withStaggerDelayTransition } from '../utils/bar';
6
+ import { defaultTransition, getTransition, usePathTransition } from '../utils/transition';
7
+ import { jsx as _jsx } from "react/jsx-runtime";
9
8
  /**
10
9
  * Default stack component that renders children in a group with animated clip path.
11
10
  */
@@ -19,64 +18,41 @@ export const DefaultBarStack = /*#__PURE__*/memo(_ref => {
19
18
  borderRadius = 4,
20
19
  roundTop = true,
21
20
  roundBottom = true,
22
- yOrigin
21
+ yOrigin,
22
+ transitions,
23
+ transition
23
24
  } = _ref;
24
- const pathRef = useRef(null);
25
25
  const {
26
- animate
26
+ animate,
27
+ drawingArea
27
28
  } = useCartesianChartContext();
28
- const clipPathId = useId();
29
- const animationProgress = useSharedValue(0);
30
- const targetClipPath = useMemo(() => {
29
+
30
+ // Compute normalized x position for stagger delay calculation
31
+ const normalizedX = useMemo(() => drawingArea.width > 0 ? (x - drawingArea.x) / drawingArea.width : 0, [x, drawingArea.x, drawingArea.width]);
32
+ const enterTransition = useMemo(() => withStaggerDelayTransition(getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultBarEnterTransition), normalizedX), [animate, transitions == null ? void 0 : transitions.enter, normalizedX]);
33
+ const updateTransition = useMemo(() => withStaggerDelayTransition(getTransition((transitions == null ? void 0 : transitions.update) !== undefined ? transitions.update : transition, animate, defaultTransition), normalizedX), [animate, transitions == null ? void 0 : transitions.update, transition, normalizedX]);
34
+
35
+ // Generate target clip path (full bar)
36
+ const targetPath = useMemo(() => {
31
37
  return getBarPath(x, y, width, height, borderRadius, roundTop, roundBottom);
32
38
  }, [x, y, width, height, borderRadius, roundTop, roundBottom]);
33
- const previousClipPath = usePreviousValue(targetClipPath);
34
- const initialClipPath = useMemo(() => {
35
- return getBarPath(x, yOrigin != null ? yOrigin : y + height, width, 1, borderRadius, roundTop, roundBottom);
36
- }, [x, yOrigin, y, height, width, borderRadius, roundTop, roundBottom]);
37
- const fromClipPath = useMemo(() => {
38
- if (!animate) return targetClipPath;
39
- return previousClipPath || initialClipPath;
40
- }, [animate, previousClipPath, initialClipPath, targetClipPath]);
41
- const clipPathInterpolator = useMemo(() => interpolate.interpolatePath(fromClipPath, targetClipPath), [fromClipPath, targetClipPath]);
42
- const updateClipPath = useCallback(progress => {
43
- var _pathRef$current;
44
- const val = Number(progress.toFixed(4));
45
- (_pathRef$current = pathRef.current) == null || _pathRef$current.setNativeProps({
46
- d: clipPathInterpolator(val)
47
- });
48
- }, [clipPathInterpolator]);
49
- useAnimatedReaction(() => animationProgress.value, progress => {
50
- 'worklet';
51
39
 
52
- runOnJS(updateClipPath)(progress);
53
- }, [updateClipPath]);
54
- useEffect(() => {
55
- if (!pathRef.current) return;
56
- if (!animate) {
57
- pathRef.current.setNativeProps({
58
- d: targetClipPath
59
- });
60
- animationProgress.value = 1;
61
- return;
40
+ // Initial clip path for entry animation (bar at baseline with minimal height)
41
+ const initialPath = useMemo(() => {
42
+ const baselineY = yOrigin != null ? yOrigin : y + height;
43
+ return getBarPath(x, baselineY, width, 1, borderRadius, roundTop, roundBottom);
44
+ }, [x, yOrigin, y, height, width, borderRadius, roundTop, roundBottom]);
45
+ const animatedClipPath = usePathTransition({
46
+ currentPath: targetPath,
47
+ initialPath,
48
+ transitions: {
49
+ enter: enterTransition,
50
+ update: updateTransition
62
51
  }
63
- animationProgress.value = 0;
64
- animationProgress.value = withTiming(1, {
65
- duration: 300
66
- });
67
- }, [animate, animationProgress, targetClipPath, initialClipPath]);
68
- return /*#__PURE__*/_jsxs(_Fragment, {
69
- children: [/*#__PURE__*/_jsx(Defs, {
70
- children: /*#__PURE__*/_jsx(ClipPath, {
71
- id: clipPathId,
72
- children: /*#__PURE__*/_jsx(Path, {
73
- ref: pathRef,
74
- d: fromClipPath
75
- })
76
- })
77
- }), /*#__PURE__*/_jsx(G, {
78
- clipPath: "url(#" + clipPathId + ")",
79
- children: children
80
- })]
52
+ });
53
+ const clipPath = animate ? animatedClipPath : targetPath;
54
+ return /*#__PURE__*/_jsx(Group, {
55
+ clip: clipPath,
56
+ children: children
81
57
  });
82
58
  });