@coinbase/cds-web-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 (198) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dts/chart/CartesianChart.d.ts +56 -3
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/ChartProvider.d.ts +3 -0
  5. package/dts/chart/ChartProvider.d.ts.map +1 -1
  6. package/dts/chart/Path.d.ts +64 -7
  7. package/dts/chart/Path.d.ts.map +1 -1
  8. package/dts/chart/PeriodSelector.d.ts +5 -15
  9. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  10. package/dts/chart/area/Area.d.ts +50 -25
  11. package/dts/chart/area/Area.d.ts.map +1 -1
  12. package/dts/chart/area/AreaChart.d.ts +46 -6
  13. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  14. package/dts/chart/area/DottedArea.d.ts +21 -44
  15. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  16. package/dts/chart/area/GradientArea.d.ts +21 -12
  17. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  18. package/dts/chart/area/SolidArea.d.ts +16 -1
  19. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  20. package/dts/chart/axis/Axis.d.ts +109 -63
  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 +50 -12
  31. package/dts/chart/bar/Bar.d.ts.map +1 -1
  32. package/dts/chart/bar/BarChart.d.ts +20 -8
  33. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  34. package/dts/chart/bar/BarPlot.d.ts +3 -1
  35. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  36. package/dts/chart/bar/BarStack.d.ts +41 -46
  37. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  38. package/dts/chart/bar/BarStackGroup.d.ts +2 -0
  39. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  40. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  41. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  42. package/dts/chart/gradient/Gradient.d.ts +35 -0
  43. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  44. package/dts/chart/gradient/index.d.ts +2 -0
  45. package/dts/chart/gradient/index.d.ts.map +1 -0
  46. package/dts/chart/index.d.ts +3 -1
  47. package/dts/chart/index.d.ts.map +1 -1
  48. package/dts/chart/legend/DefaultLegendEntry.d.ts +21 -0
  49. package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
  50. package/dts/chart/legend/DefaultLegendShape.d.ts +7 -0
  51. package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
  52. package/dts/chart/legend/Legend.d.ts +169 -0
  53. package/dts/chart/legend/Legend.d.ts.map +1 -0
  54. package/dts/chart/legend/index.d.ts +4 -0
  55. package/dts/chart/legend/index.d.ts.map +1 -0
  56. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  57. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  58. package/dts/chart/line/DottedLine.d.ts +15 -3
  59. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  60. package/dts/chart/line/Line.d.ts +84 -28
  61. package/dts/chart/line/Line.d.ts.map +1 -1
  62. package/dts/chart/line/LineChart.d.ts +28 -8
  63. package/dts/chart/line/LineChart.d.ts.map +1 -1
  64. package/dts/chart/line/ReferenceLine.d.ts +91 -44
  65. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  66. package/dts/chart/line/SolidLine.d.ts +14 -3
  67. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  68. package/dts/chart/line/index.d.ts +1 -1
  69. package/dts/chart/line/index.d.ts.map +1 -1
  70. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  71. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  72. package/dts/chart/point/Point.d.ts +217 -0
  73. package/dts/chart/point/Point.d.ts.map +1 -0
  74. package/dts/chart/point/index.d.ts +3 -0
  75. package/dts/chart/point/index.d.ts.map +1 -0
  76. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +41 -0
  77. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  78. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  79. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  80. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +10 -0
  81. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  82. package/dts/chart/scrubber/Scrubber.d.ts +287 -70
  83. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  84. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +80 -0
  85. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  86. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +47 -0
  87. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  88. package/dts/chart/scrubber/index.d.ts +3 -0
  89. package/dts/chart/scrubber/index.d.ts.map +1 -1
  90. package/dts/chart/text/ChartText.d.ts +46 -43
  91. package/dts/chart/text/ChartText.d.ts.map +1 -1
  92. package/dts/chart/text/{SmartChartTextGroup.d.ts → ChartTextGroup.d.ts} +9 -3
  93. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  94. package/dts/chart/text/index.d.ts +1 -1
  95. package/dts/chart/text/index.d.ts.map +1 -1
  96. package/dts/chart/utils/axis.d.ts +25 -1
  97. package/dts/chart/utils/axis.d.ts.map +1 -1
  98. package/dts/chart/utils/bar.d.ts +34 -0
  99. package/dts/chart/utils/bar.d.ts.map +1 -1
  100. package/dts/chart/utils/chart.d.ts +45 -7
  101. package/dts/chart/utils/chart.d.ts.map +1 -1
  102. package/dts/chart/utils/context.d.ts +6 -0
  103. package/dts/chart/utils/context.d.ts.map +1 -1
  104. package/dts/chart/utils/gradient.d.ts +104 -0
  105. package/dts/chart/utils/gradient.d.ts.map +1 -0
  106. package/dts/chart/utils/index.d.ts +4 -0
  107. package/dts/chart/utils/index.d.ts.map +1 -1
  108. package/dts/chart/utils/interpolate.d.ts +112 -0
  109. package/dts/chart/utils/interpolate.d.ts.map +1 -0
  110. package/dts/chart/utils/path.d.ts +30 -1
  111. package/dts/chart/utils/path.d.ts.map +1 -1
  112. package/dts/chart/utils/point.d.ts +40 -7
  113. package/dts/chart/utils/point.d.ts.map +1 -1
  114. package/dts/chart/utils/scale.d.ts +11 -0
  115. package/dts/chart/utils/scale.d.ts.map +1 -1
  116. package/dts/chart/utils/scrubber.d.ts +40 -0
  117. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  118. package/dts/chart/utils/transition.d.ts +101 -0
  119. package/dts/chart/utils/transition.d.ts.map +1 -0
  120. package/dts/sparkline/sparkline-interactive/SparklineInteractiveMarkerDates.d.ts.map +1 -1
  121. package/esm/chart/CartesianChart.js +170 -83
  122. package/esm/chart/ChartProvider.js +2 -2
  123. package/esm/chart/Path.js +59 -54
  124. package/esm/chart/PeriodSelector.js +36 -32
  125. package/esm/chart/area/Area.js +26 -34
  126. package/esm/chart/area/AreaChart.js +29 -15
  127. package/esm/chart/area/DottedArea.js +39 -89
  128. package/esm/chart/area/GradientArea.js +37 -80
  129. package/esm/chart/area/SolidArea.js +32 -11
  130. package/esm/chart/axis/Axis.js +4 -39
  131. package/esm/chart/axis/DefaultAxisTickLabel.js +15 -0
  132. package/esm/chart/axis/XAxis.js +184 -63
  133. package/esm/chart/axis/YAxis.js +190 -57
  134. package/esm/chart/axis/index.js +1 -0
  135. package/esm/chart/bar/Bar.js +7 -1
  136. package/esm/chart/bar/BarChart.js +17 -32
  137. package/esm/chart/bar/BarPlot.js +5 -2
  138. package/esm/chart/bar/BarStack.js +74 -22
  139. package/esm/chart/bar/BarStackGroup.js +8 -18
  140. package/esm/chart/bar/DefaultBar.js +23 -28
  141. package/esm/chart/bar/DefaultBarStack.js +24 -20
  142. package/esm/chart/gradient/Gradient.js +104 -0
  143. package/esm/chart/gradient/index.js +1 -0
  144. package/esm/chart/index.js +3 -1
  145. package/esm/chart/legend/DefaultLegendEntry.css +1 -0
  146. package/esm/chart/legend/DefaultLegendEntry.js +50 -0
  147. package/esm/chart/legend/DefaultLegendShape.css +5 -0
  148. package/esm/chart/legend/DefaultLegendShape.js +47 -0
  149. package/esm/chart/legend/Legend.js +76 -0
  150. package/esm/chart/legend/index.js +3 -0
  151. package/esm/chart/line/DefaultReferenceLineLabel.js +81 -0
  152. package/esm/chart/line/DottedLine.js +41 -17
  153. package/esm/chart/line/Line.js +87 -75
  154. package/esm/chart/line/LineChart.js +24 -8
  155. package/esm/chart/line/ReferenceLine.js +41 -43
  156. package/esm/chart/line/SolidLine.js +39 -15
  157. package/esm/chart/line/index.js +1 -1
  158. package/esm/chart/{line/GradientLine.js → point/DefaultPointLabel.js} +31 -45
  159. package/esm/chart/point/Point.css +2 -0
  160. package/esm/chart/{Point.js → point/Point.js} +87 -62
  161. package/esm/chart/point/index.js +2 -0
  162. package/esm/chart/scrubber/DefaultScrubberBeacon.js +154 -0
  163. package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +57 -0
  164. package/esm/chart/scrubber/{ScrubberBeaconLabel.js → DefaultScrubberLabel.js} +15 -18
  165. package/esm/chart/scrubber/Scrubber.js +97 -392
  166. package/esm/chart/scrubber/ScrubberBeaconGroup.js +174 -0
  167. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +201 -0
  168. package/esm/chart/scrubber/index.js +3 -1
  169. package/esm/chart/text/ChartText.js +15 -20
  170. package/esm/chart/text/{SmartChartTextGroup.js → ChartTextGroup.js} +4 -3
  171. package/esm/chart/text/index.js +1 -1
  172. package/esm/chart/utils/axis.js +47 -31
  173. package/esm/chart/utils/bar.js +48 -0
  174. package/esm/chart/utils/chart.js +42 -3
  175. package/esm/chart/utils/gradient.js +257 -0
  176. package/esm/chart/utils/index.js +4 -0
  177. package/esm/chart/utils/interpolate.js +644 -0
  178. package/esm/chart/utils/path.js +41 -9
  179. package/esm/chart/utils/point.js +99 -12
  180. package/esm/chart/utils/scale.js +13 -2
  181. package/esm/chart/utils/scrubber.js +137 -0
  182. package/esm/chart/utils/transition.js +133 -0
  183. package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
  184. package/esm/sparkline/sparkline-interactive/SparklineInteractiveMarkerDates.js +8 -4
  185. package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
  186. package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
  187. package/package.json +12 -11
  188. package/dts/chart/Point.d.ts +0 -153
  189. package/dts/chart/Point.d.ts.map +0 -1
  190. package/dts/chart/line/GradientLine.d.ts +0 -42
  191. package/dts/chart/line/GradientLine.d.ts.map +0 -1
  192. package/dts/chart/scrubber/ScrubberBeacon.d.ts +0 -93
  193. package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +0 -1
  194. package/dts/chart/scrubber/ScrubberBeaconLabel.d.ts +0 -7
  195. package/dts/chart/scrubber/ScrubberBeaconLabel.d.ts.map +0 -1
  196. package/dts/chart/text/SmartChartTextGroup.d.ts.map +0 -1
  197. package/esm/chart/Point.css +0 -2
  198. package/esm/chart/scrubber/ScrubberBeacon.js +0 -195
@@ -1,4 +1,4 @@
1
- const _excluded = ["series", "children", "animate", "xAxis", "yAxis", "inset", "enableScrubbing", "onScrubberPositionChange", "width", "height", "className", "style"];
1
+ const _excluded = ["series", "children", "animate", "xAxis", "yAxis", "inset", "enableScrubbing", "onScrubberPositionChange", "legend", "legendPosition", "legendAccessibilityLabel", "width", "height", "className", "classNames", "style", "styles", "accessibilityLabel"];
2
2
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
3
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
4
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
@@ -12,23 +12,30 @@ import { useDimensions } from '@coinbase/cds-web/hooks/useDimensions';
12
12
  import { Box } from '@coinbase/cds-web/layout';
13
13
  import { ScrubberProvider } from './scrubber/ScrubberProvider';
14
14
  import { CartesianChartProvider } from './ChartProvider';
15
+ import { Legend } from './legend';
15
16
  import { defaultAxisId, defaultChartInset, getAxisConfig, getAxisDomain, getAxisRange, getAxisScale, getChartInset, getStackedSeriesData as calculateStackedSeriesData, useTotalAxisPadding } from './utils';
16
- import { jsx as _jsx } from "react/jsx-runtime";
17
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
18
  const focusStylesCss = "cds-focusStylesCss-f4oy7ru";
18
19
  export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
19
20
  let {
20
21
  series,
21
22
  children,
22
23
  animate = true,
23
- xAxis: xAxisConfigInput,
24
- yAxis: yAxisConfigInput,
25
- inset: insetInput,
24
+ xAxis: xAxisConfigProp,
25
+ yAxis: yAxisConfigProp,
26
+ inset,
26
27
  enableScrubbing,
27
28
  onScrubberPositionChange,
29
+ legend,
30
+ legendPosition = 'bottom',
31
+ legendAccessibilityLabel,
28
32
  width = '100%',
29
33
  height = '100%',
30
34
  className,
31
- style
35
+ classNames,
36
+ style,
37
+ styles,
38
+ accessibilityLabel
32
39
  } = _ref,
33
40
  props = _objectWithoutProperties(_ref, _excluded);
34
41
  const {
@@ -36,15 +43,13 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
36
43
  width: chartWidth,
37
44
  height: chartHeight
38
45
  } = useDimensions();
39
- const internalSvgRef = useRef(null);
40
- const userInset = useMemo(() => {
41
- return getChartInset(insetInput, defaultChartInset);
42
- }, [insetInput]);
46
+ const svgRef = useRef(null);
47
+ const calculatedInset = useMemo(() => getChartInset(inset, defaultChartInset), [inset]);
43
48
 
44
49
  // Axis configs store the properties of each axis, such as id, scale type, domain limit, etc.
45
50
  // We only support 1 x axis but allow for multiple y axes.
46
- const xAxisConfig = useMemo(() => getAxisConfig('x', xAxisConfigInput)[0], [xAxisConfigInput]);
47
- const yAxisConfig = useMemo(() => getAxisConfig('y', yAxisConfigInput), [yAxisConfigInput]);
51
+ const xAxisConfig = useMemo(() => getAxisConfig('x', xAxisConfigProp)[0], [xAxisConfigProp]);
52
+ const yAxisConfig = useMemo(() => getAxisConfig('y', yAxisConfigProp), [yAxisConfigProp]);
48
53
  const {
49
54
  renderedAxes,
50
55
  registerAxis,
@@ -59,10 +64,10 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
59
64
  height: 0
60
65
  };
61
66
  const totalInset = {
62
- top: userInset.top + axisPadding.top,
63
- right: userInset.right + axisPadding.right,
64
- bottom: userInset.bottom + axisPadding.bottom,
65
- left: userInset.left + axisPadding.left
67
+ top: calculatedInset.top + axisPadding.top,
68
+ right: calculatedInset.right + axisPadding.right,
69
+ bottom: calculatedInset.bottom + axisPadding.bottom,
70
+ left: calculatedInset.left + axisPadding.left
66
71
  };
67
72
  const availableWidth = chartWidth - totalInset.left - totalInset.right;
68
73
  const availableHeight = chartHeight - totalInset.top - totalInset.bottom;
@@ -72,11 +77,15 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
72
77
  width: availableWidth > 0 ? availableWidth : 0,
73
78
  height: availableHeight > 0 ? availableHeight : 0
74
79
  };
75
- }, [chartHeight, chartWidth, userInset, axisPadding]);
76
-
77
- // Axes contain the config along with domain and range, which get calculated here.
78
- const xAxis = useMemo(() => {
79
- if (!chartRect || chartRect.width <= 0 || chartRect.height <= 0) return undefined;
80
+ }, [chartHeight, chartWidth, calculatedInset, axisPadding]);
81
+ const {
82
+ xAxis,
83
+ xScale
84
+ } = useMemo(() => {
85
+ if (!chartRect || chartRect.width <= 0 || chartRect.height <= 0) return {
86
+ xAxis: undefined,
87
+ xScale: undefined
88
+ };
80
89
  const domain = getAxisDomain(xAxisConfig, series !== null && series !== void 0 ? series : [], 'x');
81
90
  const range = getAxisRange(xAxisConfig, chartRect, 'x');
82
91
  const axisConfig = {
@@ -87,11 +96,43 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
87
96
  categoryPadding: xAxisConfig.categoryPadding,
88
97
  domainLimit: xAxisConfig.domainLimit
89
98
  };
90
- return axisConfig;
99
+
100
+ // Create the scale
101
+ const scale = getAxisScale({
102
+ config: axisConfig,
103
+ type: 'x',
104
+ range: axisConfig.range,
105
+ dataDomain: axisConfig.domain
106
+ });
107
+ if (!scale) return {
108
+ xAxis: undefined,
109
+ xScale: undefined
110
+ };
111
+
112
+ // Update axis config with actual scale domain (after .nice() or other adjustments)
113
+ const scaleDomain = scale.domain();
114
+ const actualDomain = Array.isArray(scaleDomain) && scaleDomain.length === 2 ? {
115
+ min: scaleDomain[0],
116
+ max: scaleDomain[1]
117
+ } : axisConfig.domain;
118
+ const finalAxisConfig = _objectSpread(_objectSpread({}, axisConfig), {}, {
119
+ domain: actualDomain
120
+ });
121
+ return {
122
+ xAxis: finalAxisConfig,
123
+ xScale: scale
124
+ };
91
125
  }, [xAxisConfig, series, chartRect]);
92
- const yAxes = useMemo(() => {
126
+ const {
127
+ yAxes,
128
+ yScales
129
+ } = useMemo(() => {
93
130
  const axes = new Map();
94
- if (!chartRect || chartRect.width <= 0 || chartRect.height <= 0) return axes;
131
+ const scales = new Map();
132
+ if (!chartRect || chartRect.width <= 0 || chartRect.height <= 0) return {
133
+ yAxes: axes,
134
+ yScales: scales
135
+ };
95
136
  yAxisConfig.forEach(axisParam => {
96
137
  var _axisParam$id, _series$filter, _axisParam$domainLimi;
97
138
  const axisId = (_axisParam$id = axisParam.id) !== null && _axisParam$id !== void 0 ? _axisParam$id : defaultAxisId;
@@ -102,36 +143,19 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
102
143
  return ((_s$yAxisId = s.yAxisId) !== null && _s$yAxisId !== void 0 ? _s$yAxisId : defaultAxisId) === axisId;
103
144
  })) !== null && _series$filter !== void 0 ? _series$filter : [];
104
145
 
105
- // Calculate domain and range in one pass
106
- const domain = getAxisDomain(axisParam, relevantSeries, 'y');
146
+ // Calculate domain and range
147
+ const dataDomain = getAxisDomain(axisParam, relevantSeries, 'y');
107
148
  const range = getAxisRange(axisParam, chartRect, 'y');
108
- axes.set(axisId, {
149
+ const axisConfig = {
109
150
  scaleType: axisParam.scaleType,
110
- domain,
151
+ domain: dataDomain,
111
152
  range,
112
153
  data: axisParam.data,
113
154
  categoryPadding: axisParam.categoryPadding,
114
155
  domainLimit: (_axisParam$domainLimi = axisParam.domainLimit) !== null && _axisParam$domainLimi !== void 0 ? _axisParam$domainLimi : 'nice'
115
- });
116
- });
117
- return axes;
118
- }, [yAxisConfig, series, chartRect]);
156
+ };
119
157
 
120
- // Scales are the functions that convert data values to visual positions.
121
- // They are calculated here based on the above axes.
122
- const xScale = useMemo(() => {
123
- if (!chartRect || chartRect.width <= 0 || chartRect.height <= 0 || xAxis === undefined) return undefined;
124
- return getAxisScale({
125
- config: xAxis,
126
- type: 'x',
127
- range: xAxis.range,
128
- dataDomain: xAxis.domain
129
- });
130
- }, [chartRect, xAxis]);
131
- const yScales = useMemo(() => {
132
- const scales = new Map();
133
- if (!chartRect || chartRect.width <= 0 || chartRect.height <= 0) return scales;
134
- yAxes.forEach((axisConfig, axisId) => {
158
+ // Create the scale
135
159
  const scale = getAxisScale({
136
160
  config: axisConfig,
137
161
  type: 'y',
@@ -140,10 +164,23 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
140
164
  });
141
165
  if (scale) {
142
166
  scales.set(axisId, scale);
167
+
168
+ // Update axis config with actual scale domain (after .nice() or other adjustments)
169
+ const scaleDomain = scale.domain();
170
+ const actualDomain = Array.isArray(scaleDomain) && scaleDomain.length === 2 ? {
171
+ min: scaleDomain[0],
172
+ max: scaleDomain[1]
173
+ } : axisConfig.domain;
174
+ axes.set(axisId, _objectSpread(_objectSpread({}, axisConfig), {}, {
175
+ domain: actualDomain
176
+ }));
143
177
  }
144
178
  });
145
- return scales;
146
- }, [chartRect, yAxes]);
179
+ return {
180
+ yAxes: axes,
181
+ yScales: scales
182
+ };
183
+ }, [yAxisConfig, series, chartRect]);
147
184
  const getXAxis = useCallback(() => xAxis, [xAxis]);
148
185
  const getYAxis = useCallback(id => yAxes.get(id !== null && id !== void 0 ? id : defaultAxisId), [yAxes]);
149
186
  const getXScale = useCallback(() => xScale, [xScale]);
@@ -157,6 +194,20 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
157
194
  if (!seriesId) return undefined;
158
195
  return stackedDataMap.get(seriesId);
159
196
  }, [stackedDataMap]);
197
+ const dataLength = useMemo(() => {
198
+ // If xAxis has categorical data, use that length
199
+ if (xAxisConfig.data && xAxisConfig.data.length > 0) {
200
+ return xAxisConfig.data.length;
201
+ }
202
+
203
+ // Otherwise, find the longest series
204
+ if (!series || series.length === 0) return 0;
205
+ return series.reduce((max, s) => {
206
+ var _seriesData$length;
207
+ const seriesData = getStackedSeriesData(s.id);
208
+ return Math.max(max, (_seriesData$length = seriesData === null || seriesData === void 0 ? void 0 : seriesData.length) !== null && _seriesData$length !== void 0 ? _seriesData$length : 0);
209
+ }, 0);
210
+ }, [xAxisConfig.data, series, getStackedSeriesData]);
160
211
  const getAxisBounds = useCallback(axisId => {
161
212
  const axis = renderedAxes.get(axisId);
162
213
  if (!axis || !chartRect) return;
@@ -168,7 +219,7 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
168
219
  const offsetFromPreviousAxes = axesAtPosition.slice(0, axisIndex).reduce((sum, a) => sum + a.size, 0);
169
220
  if (axis.position === 'top') {
170
221
  // Position above the chart rect, accounting for user inset
171
- const startY = userInset.top + offsetFromPreviousAxes;
222
+ const startY = calculatedInset.top + offsetFromPreviousAxes;
172
223
  return {
173
224
  x: chartRect.x,
174
225
  y: startY,
@@ -186,7 +237,7 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
186
237
  };
187
238
  } else if (axis.position === 'left') {
188
239
  // Position to the left of the chart rect, accounting for user inset
189
- const startX = userInset.left + offsetFromPreviousAxes;
240
+ const startX = calculatedInset.left + offsetFromPreviousAxes;
190
241
  return {
191
242
  x: startX,
192
243
  y: chartRect.y,
@@ -203,7 +254,7 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
203
254
  height: chartRect.height
204
255
  };
205
256
  }
206
- }, [renderedAxes, chartRect, userInset]);
257
+ }, [renderedAxes, chartRect, calculatedInset]);
207
258
  const contextValue = useMemo(() => ({
208
259
  series: series !== null && series !== void 0 ? series : [],
209
260
  getSeries,
@@ -216,43 +267,79 @@ export const CartesianChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
216
267
  getXScale,
217
268
  getYScale,
218
269
  drawingArea: chartRect,
270
+ dataLength,
219
271
  registerAxis,
220
272
  unregisterAxis,
221
273
  getAxisBounds
222
- }), [series, getSeries, getStackedSeriesData, animate, chartWidth, chartHeight, getXAxis, getYAxis, getXScale, getYScale, chartRect, registerAxis, unregisterAxis, getAxisBounds]);
223
- return /*#__PURE__*/_jsx(Box, _objectSpread(_objectSpread({
274
+ }), [series, getSeries, getStackedSeriesData, animate, chartWidth, chartHeight, getXAxis, getYAxis, getXScale, getYScale, chartRect, dataLength, registerAxis, unregisterAxis, getAxisBounds]);
275
+ const rootClassNames = useMemo(() => cx(className, classNames === null || classNames === void 0 ? void 0 : classNames.root), [className, classNames]);
276
+ const rootStyles = useMemo(() => _objectSpread(_objectSpread({}, style), styles === null || styles === void 0 ? void 0 : styles.root), [style, styles === null || styles === void 0 ? void 0 : styles.root]);
277
+ const legendElement = useMemo(() => {
278
+ if (!legend) return;
279
+ if (legend === true) {
280
+ const isHorizontal = legendPosition === 'top' || legendPosition === 'bottom';
281
+ const flexDirection = isHorizontal ? 'row' : 'column';
282
+ return /*#__PURE__*/_jsx(Legend, {
283
+ accessibilityLabel: legendAccessibilityLabel,
284
+ flexDirection: flexDirection
285
+ });
286
+ }
287
+ return legend;
288
+ }, [legend, legendAccessibilityLabel, legendPosition]);
289
+ const rootBoxProps = useMemo(() => _objectSpread({
290
+ className: rootClassNames,
291
+ height,
292
+ style: rootStyles,
293
+ width
294
+ }, props), [rootClassNames, height, rootStyles, width, props]);
295
+ const chartContent = /*#__PURE__*/_jsx(Box, {
224
296
  ref: node => {
225
- // Handle the observe ref, internal ref, and forwarded ref
226
297
  observe(node);
227
- if (internalSvgRef.current !== node) {
228
- internalSvgRef.current = node;
229
- }
230
- if (ref) {
231
- if (typeof ref === 'function') {
232
- ref(node);
233
- } else {
234
- ref.current = node;
235
- }
236
- }
237
298
  },
238
- "aria-live": "polite",
239
- as: "svg",
240
- className: cx(enableScrubbing && focusStylesCss, className),
241
- height: height,
242
- role: "figure",
243
- style: style,
244
- tabIndex: enableScrubbing ? 0 : undefined,
245
- width: width
246
- }, props), {}, {
247
- children: /*#__PURE__*/_jsx(CartesianChartProvider, {
248
- value: contextValue,
249
- children: /*#__PURE__*/_jsx(ScrubberProvider, {
250
- enableScrubbing: !!enableScrubbing,
251
- onScrubberPositionChange: onScrubberPositionChange,
252
- svgRef: internalSvgRef,
253
- children: children
254
- })
299
+ height: legend ? undefined : height,
300
+ style: {
301
+ flex: 1,
302
+ minHeight: 0,
303
+ minWidth: 0
304
+ },
305
+ width: legend ? undefined : width,
306
+ children: /*#__PURE__*/_jsx(Box, {
307
+ ref: node => {
308
+ const svgElement = node;
309
+ svgRef.current = svgElement;
310
+ // Forward the ref to the user
311
+ if (ref) {
312
+ if (typeof ref === 'function') {
313
+ ref(svgElement);
314
+ } else {
315
+ ref.current = svgElement;
316
+ }
317
+ }
318
+ },
319
+ accessibilityLabel: accessibilityLabel,
320
+ "aria-live": "polite",
321
+ as: "svg",
322
+ className: cx(enableScrubbing && focusStylesCss, classNames === null || classNames === void 0 ? void 0 : classNames.chart),
323
+ height: "100%",
324
+ style: styles === null || styles === void 0 ? void 0 : styles.chart,
325
+ tabIndex: enableScrubbing ? 0 : undefined,
326
+ width: "100%",
327
+ children: children
328
+ })
329
+ });
330
+ return /*#__PURE__*/_jsx(CartesianChartProvider, {
331
+ value: contextValue,
332
+ children: /*#__PURE__*/_jsx(ScrubberProvider, {
333
+ enableScrubbing: !!enableScrubbing,
334
+ onScrubberPositionChange: onScrubberPositionChange,
335
+ svgRef: svgRef,
336
+ children: legend ? /*#__PURE__*/_jsxs(Box, _objectSpread(_objectSpread({}, rootBoxProps), {}, {
337
+ flexDirection: legendPosition === 'top' || legendPosition === 'bottom' ? 'column' : 'row',
338
+ children: [(legendPosition === 'top' || legendPosition === 'left') && legendElement, chartContent, (legendPosition === 'bottom' || legendPosition === 'right') && legendElement]
339
+ })) : /*#__PURE__*/_jsx(Box, _objectSpread(_objectSpread({}, rootBoxProps), {}, {
340
+ children: chartContent
341
+ }))
255
342
  })
256
- }));
343
+ });
257
344
  }));
258
345
  import "./CartesianChart.css";
@@ -1,9 +1,9 @@
1
1
  import { createContext, useContext } from 'react';
2
- const CartesianChartContext = /*#__PURE__*/createContext(undefined);
2
+ export const CartesianChartContext = /*#__PURE__*/createContext(undefined);
3
3
  export const useCartesianChartContext = () => {
4
4
  const context = useContext(CartesianChartContext);
5
5
  if (!context) {
6
- throw new Error('useCartesianChartContext must be used within a CartesianChart component. See http://cds.coinbase.com/components/graphs/CartesianChart.');
6
+ throw new Error('useCartesianChartContext must be used within a CartesianChart component. See https://cds.coinbase.com/components/charts/CartesianChart.');
7
7
  }
8
8
  return context;
9
9
  };
package/esm/chart/Path.js CHANGED
@@ -1,4 +1,5 @@
1
- const _excluded = ["animate", "clipRect", "clipOffset", "d"];
1
+ const _excluded = ["d", "initialPath", "transitions"],
2
+ _excluded2 = ["animate", "clipRect", "clipOffset", "d", "transitions", "transition"];
2
3
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
4
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
5
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
@@ -6,84 +7,88 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
6
7
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7
8
  function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
8
9
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
9
- import 'd3-transition';
10
- import { memo, useCallback, useEffect, useId, useRef } from 'react';
11
- import { useValueChanges } from '@coinbase/cds-common/hooks/useValueChanges';
12
- import { interpolatePath } from 'd3-interpolate-path';
13
- import { select } from 'd3-selection';
10
+ import { memo, useId, useMemo } from 'react';
14
11
  import { m as motion } from 'framer-motion';
12
+ import { defaultPathEnterTransition } from './utils/path';
13
+ import { defaultTransition, getTransition, usePathTransition } from './utils/transition';
15
14
  import { useCartesianChartContext } from './ChartProvider';
15
+
16
+ /**
17
+ * Duration in seconds for path enter transition.
18
+ * @deprecated Use `transitions.enter` on the Path component instead.
19
+ */
16
20
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
17
- export const Path = /*#__PURE__*/memo(_ref => {
21
+ export const pathEnterTransitionDuration = 0.5;
22
+ const AnimatedPath = /*#__PURE__*/memo(_ref => {
23
+ let {
24
+ d = '',
25
+ initialPath,
26
+ transitions
27
+ } = _ref,
28
+ pathProps = _objectWithoutProperties(_ref, _excluded);
29
+ const interpolatedPath = usePathTransition({
30
+ currentPath: d,
31
+ initialPath,
32
+ transitions
33
+ });
34
+ return /*#__PURE__*/_jsx(motion.path, _objectSpread({
35
+ d: interpolatedPath
36
+ }, pathProps));
37
+ });
38
+ export const Path = /*#__PURE__*/memo(_ref2 => {
18
39
  let {
19
40
  animate: animateProp,
20
41
  clipRect,
21
42
  clipOffset = 0,
22
- d = ''
23
- } = _ref,
24
- pathProps = _objectWithoutProperties(_ref, _excluded);
25
- const pathRef = useRef(null);
43
+ d = '',
44
+ transitions,
45
+ transition
46
+ } = _ref2,
47
+ pathProps = _objectWithoutProperties(_ref2, _excluded2);
26
48
  const clipPathId = useId();
27
49
  const context = useCartesianChartContext();
28
- const rect = clipRect !== null && clipRect !== void 0 ? clipRect : context.drawingArea;
50
+ const rect = clipRect !== undefined ? clipRect : context.drawingArea;
29
51
  const animate = animateProp !== null && animateProp !== void 0 ? animateProp : context.animate;
30
- const {
31
- previousValue: previousPath,
32
- newValue: newPath,
33
- hasChanged,
34
- addPreviousValue
35
- } = useValueChanges(d);
36
- const morphPath = useCallback(() => {
37
- if (!pathRef.current || !newPath || !previousPath) return;
38
- select(pathRef.current).transition().duration(300).attrTween('d', function tween() {
39
- return interpolatePath(previousPath, newPath);
40
- });
41
- }, [previousPath, newPath]);
42
- useEffect(() => {
43
- addPreviousValue(newPath);
44
- if (animate && hasChanged && previousPath) {
45
- morphPath();
46
- }
47
- }, [addPreviousValue, newPath, animate, hasChanged, previousPath, morphPath]);
52
+ const enterTransition = useMemo(() => getTransition(transitions === null || transitions === void 0 ? void 0 : transitions.enter, animate, defaultPathEnterTransition), [animate, transitions === null || transitions === void 0 ? void 0 : transitions.enter]);
53
+ const updateTransition = useMemo(() => getTransition((transitions === null || transitions === void 0 ? void 0 : transitions.update) !== undefined ? transitions.update : transition, animate, defaultTransition), [animate, transitions === null || transitions === void 0 ? void 0 : transitions.update, transition]);
48
54
 
49
55
  // The clip offset provides extra padding to prevent path from being cut off
50
56
  // Area charts typically use offset=0 for exact clipping, while lines use offset=2 for breathing room
51
57
  const totalOffset = clipOffset * 2; // Applied on both sides
52
58
 
59
+ const clipPathAnimation = useMemo(() => {
60
+ if (rect === null) return;
61
+ return {
62
+ hidden: {
63
+ width: 0
64
+ },
65
+ visible: {
66
+ width: rect.width + totalOffset,
67
+ transition: enterTransition
68
+ }
69
+ };
70
+ }, [rect, totalOffset, enterTransition]);
71
+ const clipPath = useMemo(() => rect !== null ? "url(#".concat(clipPathId, ")") : undefined, [rect, clipPathId]);
53
72
  return /*#__PURE__*/_jsxs(_Fragment, {
54
- children: [/*#__PURE__*/_jsx("defs", {
73
+ children: [rect !== null && /*#__PURE__*/_jsx("defs", {
55
74
  children: /*#__PURE__*/_jsx("clipPath", {
56
75
  id: clipPathId,
57
- children: !animate ? /*#__PURE__*/_jsx("rect", {
58
- height: rect.height + totalOffset,
59
- width: rect.width + totalOffset,
60
- x: rect.x - clipOffset,
61
- y: rect.y - clipOffset
62
- }) : /*#__PURE__*/_jsx(motion.rect, {
76
+ children: /*#__PURE__*/_jsx(motion.rect, {
63
77
  animate: "visible",
64
78
  height: rect.height + totalOffset,
65
79
  initial: "hidden",
66
- variants: {
67
- hidden: {
68
- width: 0
69
- },
70
- visible: {
71
- width: rect.width + totalOffset,
72
- transition: {
73
- type: 'spring',
74
- duration: 1,
75
- bounce: 0
76
- }
77
- }
78
- },
80
+ variants: clipPathAnimation,
79
81
  x: rect.x - clipOffset,
80
82
  y: rect.y - clipOffset
81
83
  })
82
84
  })
83
- }), /*#__PURE__*/_jsx("path", _objectSpread({
84
- ref: pathRef,
85
- clipPath: "url(#".concat(clipPathId, ")"),
86
- d: d
85
+ }), /*#__PURE__*/_jsx(AnimatedPath, _objectSpread({
86
+ clipPath: clipPath,
87
+ d: d,
88
+ transitions: {
89
+ enter: enterTransition,
90
+ update: updateTransition
91
+ }
87
92
  }, pathProps))]
88
93
  });
89
94
  });