@coinbase/cds-mobile-visualization 3.4.0-beta.1 → 3.4.0-beta.11

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 (187) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dts/chart/CartesianChart.d.ts +57 -33
  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/Path.d.ts +77 -34
  7. package/dts/chart/Path.d.ts.map +1 -1
  8. package/dts/chart/PeriodSelector.d.ts +2 -2
  9. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  10. package/dts/chart/area/Area.d.ts +42 -27
  11. package/dts/chart/area/Area.d.ts.map +1 -1
  12. package/dts/chart/area/AreaChart.d.ts +51 -10
  13. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  14. package/dts/chart/area/DottedArea.d.ts +21 -2
  15. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  16. package/dts/chart/area/GradientArea.d.ts +19 -13
  17. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  18. package/dts/chart/area/SolidArea.d.ts +17 -2
  19. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  20. package/dts/chart/axis/Axis.d.ts +86 -118
  21. package/dts/chart/axis/Axis.d.ts.map +1 -1
  22. package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
  23. package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
  24. package/dts/chart/axis/XAxis.d.ts +1 -1
  25. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  26. package/dts/chart/axis/YAxis.d.ts +2 -2
  27. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  28. package/dts/chart/axis/index.d.ts +1 -0
  29. package/dts/chart/axis/index.d.ts.map +1 -1
  30. package/dts/chart/bar/Bar.d.ts +16 -13
  31. package/dts/chart/bar/Bar.d.ts.map +1 -1
  32. package/dts/chart/bar/BarChart.d.ts +36 -20
  33. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  34. package/dts/chart/bar/BarPlot.d.ts +2 -1
  35. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  36. package/dts/chart/bar/BarStack.d.ts +39 -48
  37. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  38. package/dts/chart/bar/BarStackGroup.d.ts +1 -0
  39. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  40. package/dts/chart/bar/DefaultBar.d.ts +1 -1
  41. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  42. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  43. package/dts/chart/gradient/Gradient.d.ts +25 -0
  44. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  45. package/dts/chart/gradient/index.d.ts +2 -0
  46. package/dts/chart/gradient/index.d.ts.map +1 -0
  47. package/dts/chart/index.d.ts +3 -1
  48. package/dts/chart/index.d.ts.map +1 -1
  49. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  50. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  51. package/dts/chart/line/DottedLine.d.ts +13 -5
  52. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  53. package/dts/chart/line/Line.d.ts +64 -25
  54. package/dts/chart/line/Line.d.ts.map +1 -1
  55. package/dts/chart/line/LineChart.d.ts +43 -9
  56. package/dts/chart/line/LineChart.d.ts.map +1 -1
  57. package/dts/chart/line/ReferenceLine.d.ts +68 -20
  58. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  59. package/dts/chart/line/SolidLine.d.ts +8 -5
  60. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  61. package/dts/chart/line/index.d.ts +1 -1
  62. package/dts/chart/line/index.d.ts.map +1 -1
  63. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  64. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  65. package/dts/chart/point/Point.d.ts +120 -0
  66. package/dts/chart/point/Point.d.ts.map +1 -0
  67. package/dts/chart/point/index.d.ts +3 -0
  68. package/dts/chart/point/index.d.ts.map +1 -0
  69. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +8 -0
  70. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  71. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  72. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  73. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +11 -0
  74. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  75. package/dts/chart/scrubber/Scrubber.d.ts +172 -43
  76. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  77. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +44 -0
  78. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  79. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +31 -0
  80. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  81. package/dts/chart/scrubber/ScrubberProvider.d.ts +6 -3
  82. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
  83. package/dts/chart/scrubber/index.d.ts +3 -0
  84. package/dts/chart/scrubber/index.d.ts.map +1 -1
  85. package/dts/chart/text/ChartText.d.ts +151 -77
  86. package/dts/chart/text/ChartText.d.ts.map +1 -1
  87. package/dts/chart/text/{SmartChartTextGroup.d.ts → ChartTextGroup.d.ts} +9 -3
  88. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  89. package/dts/chart/text/index.d.ts +1 -1
  90. package/dts/chart/text/index.d.ts.map +1 -1
  91. package/dts/chart/utils/axis.d.ts +25 -1
  92. package/dts/chart/utils/axis.d.ts.map +1 -1
  93. package/dts/chart/utils/chart.d.ts +34 -7
  94. package/dts/chart/utils/chart.d.ts.map +1 -1
  95. package/dts/chart/utils/context.d.ts +28 -7
  96. package/dts/chart/utils/context.d.ts.map +1 -1
  97. package/dts/chart/utils/gradient.d.ts +117 -0
  98. package/dts/chart/utils/gradient.d.ts.map +1 -0
  99. package/dts/chart/utils/index.d.ts +3 -0
  100. package/dts/chart/utils/index.d.ts.map +1 -1
  101. package/dts/chart/utils/path.d.ts +53 -0
  102. package/dts/chart/utils/path.d.ts.map +1 -1
  103. package/dts/chart/utils/point.d.ts +71 -7
  104. package/dts/chart/utils/point.d.ts.map +1 -1
  105. package/dts/chart/utils/scale.d.ts +102 -0
  106. package/dts/chart/utils/scale.d.ts.map +1 -1
  107. package/dts/chart/utils/scrubber.d.ts +39 -0
  108. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  109. package/dts/chart/utils/transition.d.ts +140 -0
  110. package/dts/chart/utils/transition.d.ts.map +1 -0
  111. package/esm/chart/CartesianChart.js +164 -70
  112. package/esm/chart/ChartContextBridge.js +148 -0
  113. package/esm/chart/Path.js +198 -113
  114. package/esm/chart/PeriodSelector.js +2 -2
  115. package/esm/chart/__stories__/CartesianChart.stories.js +378 -131
  116. package/esm/chart/__stories__/Chart.stories.js +2 -4
  117. package/esm/chart/__stories__/PeriodSelector.stories.js +103 -75
  118. package/esm/chart/area/Area.js +25 -35
  119. package/esm/chart/area/AreaChart.js +17 -12
  120. package/esm/chart/area/DottedArea.js +61 -109
  121. package/esm/chart/area/GradientArea.js +35 -91
  122. package/esm/chart/area/SolidArea.js +22 -8
  123. package/esm/chart/area/__stories__/AreaChart.stories.js +1 -1
  124. package/esm/chart/axis/Axis.js +5 -39
  125. package/esm/chart/axis/DefaultAxisTickLabel.js +11 -0
  126. package/esm/chart/axis/XAxis.js +148 -66
  127. package/esm/chart/axis/YAxis.js +149 -65
  128. package/esm/chart/axis/__stories__/Axis.stories.js +259 -1
  129. package/esm/chart/axis/index.js +1 -0
  130. package/esm/chart/bar/Bar.js +3 -1
  131. package/esm/chart/bar/BarChart.js +15 -37
  132. package/esm/chart/bar/BarPlot.js +41 -35
  133. package/esm/chart/bar/BarStack.js +75 -38
  134. package/esm/chart/bar/BarStackGroup.js +6 -16
  135. package/esm/chart/bar/DefaultBar.js +26 -48
  136. package/esm/chart/bar/DefaultBarStack.js +23 -58
  137. package/esm/chart/bar/__stories__/BarChart.stories.js +502 -77
  138. package/esm/chart/gradient/Gradient.js +53 -0
  139. package/esm/chart/gradient/index.js +1 -0
  140. package/esm/chart/index.js +3 -1
  141. package/esm/chart/line/DefaultReferenceLineLabel.js +66 -0
  142. package/esm/chart/line/DottedLine.js +29 -14
  143. package/esm/chart/line/Line.js +106 -67
  144. package/esm/chart/line/LineChart.js +20 -14
  145. package/esm/chart/line/ReferenceLine.js +80 -63
  146. package/esm/chart/line/SolidLine.js +25 -10
  147. package/esm/chart/line/__stories__/LineChart.stories.js +2101 -1977
  148. package/esm/chart/line/__stories__/ReferenceLine.stories.js +83 -28
  149. package/esm/chart/line/index.js +1 -1
  150. package/esm/chart/point/DefaultPointLabel.js +39 -0
  151. package/esm/chart/point/Point.js +188 -0
  152. package/esm/chart/point/index.js +2 -0
  153. package/esm/chart/scrubber/DefaultScrubberBeacon.js +179 -0
  154. package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +43 -0
  155. package/esm/chart/scrubber/DefaultScrubberLabel.js +28 -0
  156. package/esm/chart/scrubber/Scrubber.js +126 -146
  157. package/esm/chart/scrubber/ScrubberBeaconGroup.js +161 -0
  158. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +185 -0
  159. package/esm/chart/scrubber/ScrubberProvider.js +46 -54
  160. package/esm/chart/scrubber/index.js +3 -1
  161. package/esm/chart/text/ChartText.js +242 -174
  162. package/esm/chart/text/{SmartChartTextGroup.js → ChartTextGroup.js} +6 -5
  163. package/esm/chart/text/index.js +1 -1
  164. package/esm/chart/utils/axis.js +45 -29
  165. package/esm/chart/utils/chart.js +44 -3
  166. package/esm/chart/utils/gradient.js +305 -0
  167. package/esm/chart/utils/index.js +3 -0
  168. package/esm/chart/utils/path.js +76 -8
  169. package/esm/chart/utils/point.js +171 -17
  170. package/esm/chart/utils/scale.js +242 -2
  171. package/esm/chart/utils/scrubber.js +139 -0
  172. package/esm/chart/utils/transition.js +185 -0
  173. package/esm/sparkline/__stories__/Sparkline.stories.js +11 -7
  174. package/esm/sparkline/__stories__/SparklineGradient.stories.js +7 -4
  175. package/esm/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.js +51 -26
  176. package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +17 -9
  177. package/package.json +15 -9
  178. package/dts/chart/Point.d.ts +0 -103
  179. package/dts/chart/Point.d.ts.map +0 -1
  180. package/dts/chart/line/GradientLine.d.ts +0 -45
  181. package/dts/chart/line/GradientLine.d.ts.map +0 -1
  182. package/dts/chart/scrubber/ScrubberBeacon.d.ts +0 -75
  183. package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +0 -1
  184. package/dts/chart/text/SmartChartTextGroup.d.ts.map +0 -1
  185. package/esm/chart/Point.js +0 -111
  186. package/esm/chart/line/GradientLine.js +0 -62
  187. package/esm/chart/scrubber/ScrubberBeacon.js +0 -199
@@ -1,4 +1,4 @@
1
- const _excluded = ["series", "stacked", "showXAxis", "showYAxis", "xAxis", "yAxis", "inset", "children", "barPadding", "BarComponent", "fillOpacity", "stroke", "strokeWidth", "borderRadius", "roundBaseline", "BarStackComponent", "stackGap", "barMinSize", "stackMinSize"],
1
+ const _excluded = ["series", "stacked", "showXAxis", "showYAxis", "xAxis", "yAxis", "inset", "children", "barPadding", "BarComponent", "fillOpacity", "stroke", "strokeWidth", "borderRadius", "roundBaseline", "BarStackComponent", "stackGap", "barMinSize", "stackMinSize", "transition"],
2
2
  _excluded2 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range"],
3
3
  _excluded3 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "id"];
4
4
  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); }
@@ -8,11 +8,6 @@ import { XAxis, YAxis } from '../axis';
8
8
  import { CartesianChart } from '../CartesianChart';
9
9
  import { defaultChartInset, defaultStackId, getChartInset } from '../utils';
10
10
  import { BarPlot } from './BarPlot';
11
-
12
- /**
13
- * Series type specifically for bar charts - supports both single numbers and tuples,
14
- * and allows individual customization of Bar props per series.
15
- */
16
11
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
12
  export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
18
13
  let {
@@ -22,7 +17,7 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
22
17
  showYAxis,
23
18
  xAxis,
24
19
  yAxis,
25
- inset: userInset,
20
+ inset,
26
21
  children,
27
22
  barPadding,
28
23
  BarComponent,
@@ -34,44 +29,26 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
34
29
  BarStackComponent,
35
30
  stackGap,
36
31
  barMinSize,
37
- stackMinSize
32
+ stackMinSize,
33
+ transition
38
34
  } = _ref,
39
35
  chartProps = _objectWithoutPropertiesLoose(_ref, _excluded);
40
- const calculatedInset = useMemo(() => getChartInset(userInset, defaultChartInset), [userInset]);
41
-
42
- // Convert BarSeries to Series for Chart context
43
- const chartSeries = useMemo(() => {
44
- return series == null ? void 0 : series.map(s => ({
45
- id: s.id,
46
- data: s.data,
47
- label: s.label,
48
- color: s.color,
49
- yAxisId: s.yAxisId,
50
- stackId: s.stackId
51
- }));
52
- }, [series]);
36
+ const calculatedInset = useMemo(() => getChartInset(inset, defaultChartInset), [inset]);
53
37
  const transformedSeries = useMemo(() => {
54
- if (!stacked || !chartSeries) return chartSeries;
55
- return chartSeries.map(s => {
56
- var _s$stackId;
57
- return _extends({}, s, {
58
- stackId: (_s$stackId = s.stackId) != null ? _s$stackId : defaultStackId
59
- });
60
- });
61
- }, [chartSeries, stacked]);
62
- const seriesToRender = transformedSeries != null ? transformedSeries : chartSeries;
63
-
64
- // Keep the original series with bar-specific props for BarPlot
65
- const barSeriesToRender = useMemo(() => {
66
38
  if (!stacked || !series) return series;
67
39
  return series.map(s => {
68
- var _s$stackId2;
40
+ var _s$stackId;
69
41
  return _extends({}, s, {
70
- stackId: (_s$stackId2 = s.stackId) != null ? _s$stackId2 : defaultStackId
42
+ stackId: (_s$stackId = s.stackId) != null ? _s$stackId : defaultStackId
71
43
  });
72
44
  });
73
45
  }, [series, stacked]);
74
46
 
47
+ // Unlike other charts with custom props per series, we do not need to pick out
48
+ // the props from each series that shouldn't be passed to CartesianChart
49
+ const seriesToRender = transformedSeries != null ? transformedSeries : series;
50
+ const seriesIds = seriesToRender == null ? void 0 : seriesToRender.map(s => s.id);
51
+
75
52
  // Split axis props into config props for Chart and visual props for axis components
76
53
  const _ref2 = xAxis || {},
77
54
  {
@@ -137,11 +114,12 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
137
114
  borderRadius: borderRadius,
138
115
  fillOpacity: fillOpacity,
139
116
  roundBaseline: roundBaseline,
140
- seriesIds: barSeriesToRender == null ? void 0 : barSeriesToRender.map(s => s.id),
117
+ seriesIds: seriesIds,
141
118
  stackGap: stackGap,
142
119
  stackMinSize: stackMinSize,
143
120
  stroke: stroke,
144
- strokeWidth: strokeWidth
121
+ strokeWidth: strokeWidth,
122
+ transition: transition
145
123
  }), children]
146
124
  }));
147
125
  }));
@@ -1,9 +1,9 @@
1
1
  import { memo, useId, useMemo } from 'react';
2
- import { ClipPath, Defs, G, Rect } from 'react-native-svg';
2
+ import { Group, Skia } from '@shopify/react-native-skia';
3
3
  import { useCartesianChartContext } from '../ChartProvider';
4
4
  import { defaultAxisId } from '../utils';
5
5
  import { BarStackGroup } from './BarStackGroup';
6
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
7
  /**
8
8
  * BarPlot component that handles multiple series with proper stacking coordination.
9
9
  * Groups series by stack ID + y-axis ID combination and renders BarStackGroup for each group.
@@ -23,7 +23,8 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
23
23
  BarStackComponent,
24
24
  stackGap,
25
25
  barMinSize,
26
- stackMinSize
26
+ stackMinSize,
27
+ transition
27
28
  } = _ref;
28
29
  const {
29
30
  series: allSeries,
@@ -58,39 +59,44 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
58
59
  });
59
60
  return Array.from(groups.values());
60
61
  }, [targetSeries]);
61
- if (!drawingArea) {
62
+
63
+ // Create clip path for the entire chart area (shared by all bars)
64
+ const clipPath = useMemo(() => {
65
+ if (!drawingArea) return null;
66
+ const clip = Skia.Path.Make();
67
+ clip.addRect({
68
+ x: drawingArea.x,
69
+ y: drawingArea.y,
70
+ width: drawingArea.width,
71
+ height: drawingArea.height
72
+ });
73
+ return clip;
74
+ }, [drawingArea]);
75
+ if (!clipPath) {
62
76
  return null;
63
77
  }
64
- return /*#__PURE__*/_jsxs(_Fragment, {
65
- children: [/*#__PURE__*/_jsx(Defs, {
66
- children: /*#__PURE__*/_jsx(ClipPath, {
67
- id: clipPathId,
68
- children: /*#__PURE__*/_jsx(Rect, {
69
- height: drawingArea.height,
70
- width: drawingArea.width,
71
- x: drawingArea.x,
72
- y: drawingArea.y
73
- })
74
- })
75
- }), /*#__PURE__*/_jsx(G, {
76
- clipPath: "url(#" + clipPathId + ")",
77
- children: stackGroups.map((group, stackIndex) => /*#__PURE__*/_jsx(BarStackGroup, {
78
- BarComponent: defaultBarComponent,
79
- BarStackComponent: BarStackComponent,
80
- barMinSize: barMinSize,
81
- barPadding: barPadding,
82
- borderRadius: defaultBorderRadius,
83
- fillOpacity: defaultFillOpacity,
84
- roundBaseline: roundBaseline,
85
- series: group.series,
86
- stackGap: stackGap,
87
- stackIndex: stackIndex,
88
- stackMinSize: stackMinSize,
89
- stroke: defaultStroke,
90
- strokeWidth: defaultStrokeWidth,
91
- totalStacks: stackGroups.length,
92
- yAxisId: group.yAxisId
93
- }, group.stackId))
94
- })]
78
+
79
+ // Note: Clipping is now handled here at the BarPlot level (one clip path for all bars!)
80
+ // This is much more efficient than creating a clip path for each individual bar
81
+ return /*#__PURE__*/_jsx(Group, {
82
+ clip: clipPath,
83
+ children: stackGroups.map((group, stackIndex) => /*#__PURE__*/_jsx(BarStackGroup, {
84
+ BarComponent: defaultBarComponent,
85
+ BarStackComponent: BarStackComponent,
86
+ barMinSize: barMinSize,
87
+ barPadding: barPadding,
88
+ borderRadius: defaultBorderRadius,
89
+ fillOpacity: defaultFillOpacity,
90
+ roundBaseline: roundBaseline,
91
+ series: group.series,
92
+ stackGap: stackGap,
93
+ stackIndex: stackIndex,
94
+ stackMinSize: stackMinSize,
95
+ stroke: defaultStroke,
96
+ strokeWidth: defaultStrokeWidth,
97
+ totalStacks: stackGroups.length,
98
+ transition: transition,
99
+ yAxisId: group.yAxisId
100
+ }, group.stackId))
95
101
  });
96
102
  });
@@ -2,9 +2,12 @@ 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;
8
11
  /**
9
12
  * BarStack component that renders a single stack of bars at a specific category index.
10
13
  * Handles the stacking logic for bars within a single category.
@@ -26,14 +29,17 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
26
29
  stackGap,
27
30
  barMinSize,
28
31
  stackMinSize,
29
- roundBaseline
32
+ roundBaseline,
33
+ transition
30
34
  } = _ref;
31
35
  const theme = useTheme();
32
36
  const {
33
37
  getSeriesData,
34
- getXAxis
38
+ getXAxis,
39
+ getXScale
35
40
  } = useCartesianChartContext();
36
41
  const xAxis = getXAxis();
42
+ const xScale = getXScale();
37
43
  const baseline = useMemo(() => {
38
44
  var _yScale;
39
45
  const domain = yScale.domain();
@@ -42,6 +48,25 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
42
48
  const baseline = (_yScale = yScale(baselineValue)) != null ? _yScale : rect.y + rect.height;
43
49
  return Math.max(rect.y, Math.min(baseline, rect.y + rect.height));
44
50
  }, [rect.height, rect.y, yScale]);
51
+ const seriesGradients = useMemo(() => {
52
+ return series.map(s => {
53
+ if (!s.gradient || !xScale || !yScale) return;
54
+ const gradientScale = s.gradient.axis === 'x' ? xScale : yScale;
55
+ const serializableScale = convertToSerializableScale(gradientScale);
56
+ if (!serializableScale) return;
57
+ const domain = {
58
+ min: serializableScale.domain[0],
59
+ max: serializableScale.domain[1]
60
+ };
61
+ const stops = getGradientStops(s.gradient.stops, domain);
62
+ return {
63
+ seriesId: s.id,
64
+ gradient: s.gradient,
65
+ scale: serializableScale,
66
+ stops
67
+ };
68
+ });
69
+ }, [series, xScale, yScale]);
45
70
 
46
71
  // Calculate bars for this specific category
47
72
  const {
@@ -98,6 +123,24 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
98
123
  // Update stack bounds
99
124
  minY = Math.min(minY, y);
100
125
  maxY = Math.max(maxY, y + height);
126
+
127
+ // Determine fill color, respecting gradient if present
128
+ let barFill = s.color || theme.color.fgPrimary;
129
+
130
+ // Evaluate gradient if provided (using precomputed stops)
131
+ const seriesGradientConfig = seriesGradients.find(g => (g == null ? void 0 : g.seriesId) === s.id);
132
+ if (seriesGradientConfig) {
133
+ var _seriesGradientConfig;
134
+ const axis = (_seriesGradientConfig = seriesGradientConfig.gradient.axis) != null ? _seriesGradientConfig : 'y';
135
+ // For x-axis gradient, use the categoryIndex
136
+ // For y-axis gradient, use the actual data value
137
+ const dataValue = axis === 'x' ? categoryIndex : top;
138
+ const evaluatedColor = evaluateGradientAtValue(seriesGradientConfig.stops, dataValue, seriesGradientConfig.scale);
139
+ if (evaluatedColor) {
140
+ // Only apply gradient color if fill is not explicitly set
141
+ barFill = evaluatedColor;
142
+ }
143
+ }
101
144
  allBars.push({
102
145
  seriesId: s.id,
103
146
  x,
@@ -106,15 +149,10 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
106
149
  height,
107
150
  dataY: value,
108
151
  // Store the actual data value
109
- // Use series-specific properties, falling back to defaults
110
- 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,
152
+ fill: barFill,
153
+ // Check if the bar should be rounded based on the baseline, with an epsilon to handle floating-point rounding
154
+ roundTop: roundBaseline || Math.abs(barTop - baseline) >= EPSILON,
155
+ roundBottom: roundBaseline || Math.abs(barBottom - baseline) >= EPSILON,
118
156
  shouldApplyGap
119
157
  });
120
158
  });
@@ -200,8 +238,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
200
238
  if (bar.height < barMinSize) {
201
239
  var _yScale4, _yScale5, _yScale6, _yScale7;
202
240
  const heightIncrease = barMinSize - bar.height;
203
- const bottom = 0;
204
- const top = 0;
241
+ const [bottom, top] = bar.dataY.sort((a, b) => a - b);
205
242
 
206
243
  // Determine how to expand the bar
207
244
  let newBottom = bottom;
@@ -344,8 +381,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
344
381
 
345
382
  const bar = allBars[0];
346
383
  const heightIncrease = stackMinSize - bar.height;
347
- const bottom = 0;
348
- const top = 0;
384
+ const [bottom, top] = bar.dataY.sort((a, b) => a - b);
349
385
 
350
386
  // Determine how to expand the bar (same logic as barMinSize)
351
387
  let newBottom = bottom;
@@ -474,37 +510,38 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
474
510
  bars: allBars,
475
511
  stackRect: stackBounds
476
512
  };
477
- }, [series, x, width, getSeriesData, categoryIndex, roundBaseline, baseline, stackGap, barMinSize, stackMinSize, yScale, theme.color.fgPrimary]);
513
+ }, [series, x, width, getSeriesData, categoryIndex, roundBaseline, baseline, stackGap, barMinSize, stackMinSize, yScale, seriesGradients, theme.color.fgPrimary]);
478
514
  const xData = xAxis != null && xAxis.data && Array.isArray(xAxis.data) && typeof xAxis.data[0] === 'number' ? xAxis.data : undefined;
479
515
  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;
516
+ const barElements = bars.map((bar, index) => /*#__PURE__*/_jsx(Bar, {
517
+ BarComponent: defaultBarComponent,
518
+ borderRadius: borderRadius,
519
+ dataX: dataX,
520
+ dataY: bar.dataY,
521
+ fill: bar.fill,
522
+ fillOpacity: defaultFillOpacity,
523
+ height: bar.height,
524
+ originY: baseline,
525
+ roundBottom: bar.roundBottom,
526
+ roundTop: bar.roundTop,
527
+ stroke: defaultStroke,
528
+ strokeWidth: defaultStrokeWidth,
529
+ transition: transition,
530
+ width: bar.width,
531
+ x: bar.x,
532
+ y: bar.y
533
+ }, bar.seriesId + "-" + categoryIndex + "-" + index));
534
+
535
+ // Check if the bar should be rounded based on the baseline, with an epsilon to handle floating-point rounding
536
+ const stackRoundBottom = roundBaseline || Math.abs(stackRect.y + stackRect.height - baseline) >= EPSILON;
537
+ const stackRoundTop = roundBaseline || Math.abs(stackRect.y - baseline) >= EPSILON;
502
538
  return /*#__PURE__*/_jsx(BarStackComponent, {
503
539
  borderRadius: borderRadius,
504
540
  categoryIndex: categoryIndex,
505
541
  height: stackRect.height,
506
542
  roundBottom: stackRoundBottom,
507
543
  roundTop: stackRoundTop,
544
+ transition: transition,
508
545
  width: stackRect.width,
509
546
  x: stackRect.x,
510
547
  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,7 +55,7 @@ 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
60
  throw new Error('BarStackGroup requires a band scale for x-axis. See https://cds.coinbase.com/components/graphs/XAxis/#scale-type');
71
61
  }
@@ -1,14 +1,11 @@
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';
4
+ import { Path } from '../Path';
8
5
  import { getBarPath } from '../utils';
9
6
  import { jsx as _jsx } from "react/jsx-runtime";
10
7
  /**
11
- * Default bar component that renders a solid bar with animation.
8
+ * Default bar component that renders a solid bar with animation support.
12
9
  */
13
10
  export const DefaultBar = /*#__PURE__*/memo(_ref => {
14
11
  let {
@@ -23,56 +20,37 @@ export const DefaultBar = /*#__PURE__*/memo(_ref => {
23
20
  fill,
24
21
  fillOpacity = 1,
25
22
  stroke,
26
- strokeWidth
23
+ strokeWidth,
24
+ originY,
25
+ transition
27
26
  } = _ref;
28
- const pathRef = useRef(null);
29
27
  const {
30
28
  animate
31
29
  } = useCartesianChartContext();
32
30
  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
31
  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]);
32
+ const targetPath = useMemo(() => {
33
+ const effectiveBorderRadius = borderRadius != null ? borderRadius : 0;
34
+ const effectiveRoundTop = roundTop != null ? roundTop : true;
35
+ const effectiveRoundBottom = roundBottom != null ? roundBottom : true;
36
+ return d || getBarPath(x, y, width, height, effectiveBorderRadius, effectiveRoundTop, effectiveRoundBottom);
37
+ }, [x, y, width, height, borderRadius, roundTop, roundBottom, d]);
38
+ const initialPath = useMemo(() => {
39
+ const effectiveBorderRadius = borderRadius != null ? borderRadius : 0;
40
+ const effectiveRoundTop = roundTop != null ? roundTop : true;
41
+ const effectiveRoundBottom = roundBottom != null ? roundBottom : true;
42
+ const baselineY = originY != null ? originY : y + height;
43
+ return getBarPath(x, baselineY, width, 1, effectiveBorderRadius, effectiveRoundTop, effectiveRoundBottom);
44
+ }, [x, originY, y, height, width, borderRadius, roundTop, roundBottom]);
70
45
  return /*#__PURE__*/_jsx(Path, {
71
- ref: pathRef,
72
- d: fromPath,
73
- fill: defaultFill,
46
+ animate: animate,
47
+ clipPath: null,
48
+ d: targetPath,
49
+ fill: stroke ? 'none' : defaultFill,
74
50
  fillOpacity: fillOpacity,
51
+ initialPath: initialPath,
75
52
  stroke: stroke,
76
- strokeWidth: strokeWidth
53
+ strokeWidth: strokeWidth,
54
+ transition: transition
77
55
  });
78
56
  });
@@ -1,11 +1,9 @@
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 { usePathTransition } from '../utils/transition';
6
+ import { jsx as _jsx } from "react/jsx-runtime";
9
7
  /**
10
8
  * Default stack component that renders children in a group with animated clip path.
11
9
  */
@@ -19,64 +17,31 @@ export const DefaultBarStack = /*#__PURE__*/memo(_ref => {
19
17
  borderRadius = 4,
20
18
  roundTop = true,
21
19
  roundBottom = true,
22
- yOrigin
20
+ yOrigin,
21
+ transition
23
22
  } = _ref;
24
- const pathRef = useRef(null);
25
23
  const {
26
24
  animate
27
25
  } = useCartesianChartContext();
28
- const clipPathId = useId();
29
- const animationProgress = useSharedValue(0);
30
- const targetClipPath = useMemo(() => {
26
+
27
+ // Generate target clip path (full bar)
28
+ const targetPath = useMemo(() => {
31
29
  return getBarPath(x, y, width, height, borderRadius, roundTop, roundBottom);
32
30
  }, [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
31
 
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;
62
- }
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
- })]
32
+ // Initial clip path for entry animation (bar at baseline with minimal height)
33
+ const initialPath = useMemo(() => {
34
+ const baselineY = yOrigin != null ? yOrigin : y + height;
35
+ return getBarPath(x, baselineY, width, 1, borderRadius, roundTop, roundBottom);
36
+ }, [x, yOrigin, y, height, width, borderRadius, roundTop, roundBottom]);
37
+ const animatedClipPath = usePathTransition({
38
+ currentPath: targetPath,
39
+ initialPath,
40
+ transition
41
+ });
42
+ const clipPath = animate ? animatedClipPath : targetPath;
43
+ return /*#__PURE__*/_jsx(Group, {
44
+ clip: clipPath,
45
+ children: children
81
46
  });
82
47
  });