@coinbase/cds-mobile-visualization 3.4.0-beta.22 → 3.4.0-beta.24

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 (106) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dts/chart/CartesianChart.d.ts +58 -7
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/Path.d.ts.map +1 -1
  5. package/dts/chart/area/Area.d.ts +7 -0
  6. package/dts/chart/area/Area.d.ts.map +1 -1
  7. package/dts/chart/area/AreaChart.d.ts +5 -5
  8. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  9. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  10. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  11. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  12. package/dts/chart/axis/Axis.d.ts +3 -1
  13. package/dts/chart/axis/Axis.d.ts.map +1 -1
  14. package/dts/chart/axis/XAxis.d.ts +6 -0
  15. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  16. package/dts/chart/axis/YAxis.d.ts +1 -0
  17. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  18. package/dts/chart/bar/Bar.d.ts +4 -2
  19. package/dts/chart/bar/Bar.d.ts.map +1 -1
  20. package/dts/chart/bar/BarChart.d.ts +49 -9
  21. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  22. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  23. package/dts/chart/bar/BarStack.d.ts +30 -9
  24. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  25. package/dts/chart/bar/BarStackGroup.d.ts +1 -1
  26. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  27. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  28. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  29. package/dts/chart/gradient/Gradient.d.ts +5 -0
  30. package/dts/chart/gradient/Gradient.d.ts.map +1 -1
  31. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  32. package/dts/chart/line/Line.d.ts +7 -0
  33. package/dts/chart/line/Line.d.ts.map +1 -1
  34. package/dts/chart/line/LineChart.d.ts +22 -8
  35. package/dts/chart/line/LineChart.d.ts.map +1 -1
  36. package/dts/chart/line/ReferenceLine.d.ts +1 -0
  37. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  38. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  39. package/dts/chart/point/Point.d.ts +7 -0
  40. package/dts/chart/point/Point.d.ts.map +1 -1
  41. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
  42. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +2 -1
  43. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -1
  44. package/dts/chart/scrubber/Scrubber.d.ts +8 -0
  45. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  46. package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts +12 -0
  47. package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts.map +1 -0
  48. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
  49. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
  50. package/dts/chart/utils/axis.d.ts +20 -9
  51. package/dts/chart/utils/axis.d.ts.map +1 -1
  52. package/dts/chart/utils/bar.d.ts +4 -3
  53. package/dts/chart/utils/bar.d.ts.map +1 -1
  54. package/dts/chart/utils/chart.d.ts +13 -0
  55. package/dts/chart/utils/chart.d.ts.map +1 -1
  56. package/dts/chart/utils/context.d.ts +21 -6
  57. package/dts/chart/utils/context.d.ts.map +1 -1
  58. package/dts/chart/utils/gradient.d.ts +3 -1
  59. package/dts/chart/utils/gradient.d.ts.map +1 -1
  60. package/dts/chart/utils/path.d.ts +20 -0
  61. package/dts/chart/utils/path.d.ts.map +1 -1
  62. package/dts/chart/utils/point.d.ts +7 -0
  63. package/dts/chart/utils/point.d.ts.map +1 -1
  64. package/esm/chart/CartesianChart.js +145 -81
  65. package/esm/chart/Path.js +10 -7
  66. package/esm/chart/__stories__/CartesianChart.stories.js +10 -0
  67. package/esm/chart/__stories__/ChartAccessibility.stories.js +721 -0
  68. package/esm/chart/area/Area.js +19 -9
  69. package/esm/chart/area/AreaChart.js +11 -9
  70. package/esm/chart/area/DottedArea.js +11 -6
  71. package/esm/chart/area/GradientArea.js +11 -6
  72. package/esm/chart/area/SolidArea.js +3 -1
  73. package/esm/chart/area/__stories__/AreaChart.stories.js +47 -5
  74. package/esm/chart/axis/XAxis.js +14 -21
  75. package/esm/chart/axis/YAxis.js +4 -3
  76. package/esm/chart/axis/__stories__/Axis.stories.js +65 -48
  77. package/esm/chart/bar/Bar.js +9 -5
  78. package/esm/chart/bar/BarChart.js +34 -31
  79. package/esm/chart/bar/BarPlot.js +7 -5
  80. package/esm/chart/bar/BarStack.js +176 -36
  81. package/esm/chart/bar/BarStackGroup.js +37 -27
  82. package/esm/chart/bar/DefaultBar.js +24 -8
  83. package/esm/chart/bar/DefaultBarStack.js +24 -10
  84. package/esm/chart/bar/__stories__/BarChart.stories.js +105 -3
  85. package/esm/chart/gradient/Gradient.js +2 -1
  86. package/esm/chart/line/DottedLine.js +3 -1
  87. package/esm/chart/line/Line.js +32 -19
  88. package/esm/chart/line/LineChart.js +31 -9
  89. package/esm/chart/line/SolidLine.js +3 -1
  90. package/esm/chart/line/__stories__/LineChart.stories.js +115 -46
  91. package/esm/chart/point/Point.js +2 -1
  92. package/esm/chart/scrubber/DefaultScrubberBeacon.js +1 -1
  93. package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -10
  94. package/esm/chart/scrubber/Scrubber.js +47 -21
  95. package/esm/chart/scrubber/ScrubberAccessibilityView.js +177 -0
  96. package/esm/chart/scrubber/ScrubberBeaconGroup.js +24 -20
  97. package/esm/chart/scrubber/ScrubberProvider.js +29 -24
  98. package/esm/chart/scrubber/__stories__/Scrubber.stories.js +192 -6
  99. package/esm/chart/utils/axis.js +42 -14
  100. package/esm/chart/utils/bar.js +5 -4
  101. package/esm/chart/utils/chart.js +18 -5
  102. package/esm/chart/utils/context.js +7 -0
  103. package/esm/chart/utils/gradient.js +8 -4
  104. package/esm/chart/utils/path.js +90 -61
  105. package/esm/chart/utils/point.js +28 -18
  106. package/package.json +5 -5
@@ -1,5 +1,5 @@
1
- import { area as d3Area, curveBumpX, curveCatmullRom, curveLinear, curveLinearClosed, curveMonotoneX, curveNatural, curveStep, curveStepAfter, curveStepBefore, line as d3Line } from 'd3-shape';
2
- import { projectPoint, projectPoints } from './point';
1
+ import { area as d3Area, curveBumpX, curveBumpY, curveCatmullRom, curveLinear, curveLinearClosed, curveMonotoneX, curveMonotoneY, curveNatural, curveStep, curveStepAfter, curveStepBefore, line as d3Line } from 'd3-shape';
2
+ import { getPointOnScale, projectPoints } from './point';
3
3
  import { isCategoricalScale } from './scale';
4
4
  /**
5
5
  * Default enter transition for path-based components (Line, Area).
@@ -13,18 +13,23 @@ export const defaultPathEnterTransition = {
13
13
  * Get the d3 curve function for a path.
14
14
  * See https://d3js.org/d3-shape/curve
15
15
  * @param curve - The curve type. Defaults to 'linear'.
16
+ * @param layout - The chart layout. Defaults to 'vertical'.
16
17
  * @returns The d3 curve function.
17
18
  */
18
- export const getPathCurveFunction = function (curve) {
19
+ export const getPathCurveFunction = function (curve, layout) {
19
20
  if (curve === void 0) {
20
21
  curve = 'linear';
21
22
  }
23
+ if (layout === void 0) {
24
+ layout = 'vertical';
25
+ }
22
26
  switch (curve) {
23
27
  case 'catmullRom':
24
28
  return curveCatmullRom;
25
29
  case 'monotone':
26
- // When we support layout="vertical" this should dynamically switch to curveMonotoneY
27
- return curveMonotoneX;
30
+ // For vertical layout, X is the independent axis (category/index), so use MonotoneX.
31
+ // For horizontal layout, Y is the independent axis (category/index), so use MonotoneY.
32
+ return layout !== 'horizontal' ? curveMonotoneX : curveMonotoneY;
28
33
  case 'natural':
29
34
  return curveNatural;
30
35
  case 'step':
@@ -34,8 +39,9 @@ export const getPathCurveFunction = function (curve) {
34
39
  case 'stepAfter':
35
40
  return curveStepAfter;
36
41
  case 'bump':
37
- // When we support layout="vertical" this should dynamically switch to curveBumpY
38
- return curveBumpX;
42
+ // For vertical layout, X is the independent axis (category/index), so use BumpX.
43
+ // For horizontal layout, Y is the independent axis (category/index), so use BumpY.
44
+ return layout !== 'horizontal' ? curveBumpX : curveBumpY;
39
45
  case 'linearClosed':
40
46
  return curveLinearClosed;
41
47
  case 'linear':
@@ -61,17 +67,21 @@ export const getLinePath = _ref => {
61
67
  xScale,
62
68
  yScale,
63
69
  xData,
64
- connectNulls = false
70
+ yData,
71
+ connectNulls = false,
72
+ layout = 'vertical'
65
73
  } = _ref;
66
74
  if (data.length === 0) {
67
75
  return '';
68
76
  }
69
- const curveFunction = getPathCurveFunction(curve);
77
+ const curveFunction = getPathCurveFunction(curve, layout);
70
78
  const dataPoints = projectPoints({
71
79
  data,
72
80
  xScale,
73
81
  yScale,
74
- xData
82
+ xData,
83
+ yData,
84
+ layout
75
85
  });
76
86
 
77
87
  // When connectNulls is true, filter out null values before rendering
@@ -110,14 +120,20 @@ export const getAreaPath = _ref2 => {
110
120
  xScale,
111
121
  yScale,
112
122
  xData,
113
- connectNulls = false
123
+ yData,
124
+ connectNulls = false,
125
+ layout = 'vertical'
114
126
  } = _ref2;
115
127
  if (data.length === 0) {
116
128
  return '';
117
129
  }
118
- const curveFunction = getPathCurveFunction(curve);
119
- const yDomain = yScale.domain();
120
- const yMin = Math.min(...yDomain);
130
+ const curveFunction = getPathCurveFunction(curve, layout);
131
+ const categoryAxisIsX = layout !== 'horizontal';
132
+
133
+ // Determine baseline from the value scale.
134
+ const valueScale = categoryAxisIsX ? yScale : xScale;
135
+ const domain = valueScale.domain();
136
+ const min = Math.min(...domain);
121
137
  const normalizedData = data.map((item, index) => {
122
138
  if (item === null) {
123
139
  return null;
@@ -129,7 +145,7 @@ export const getAreaPath = _ref2 => {
129
145
  return null;
130
146
  }
131
147
  if (typeof item === 'number') {
132
- return [yMin, item];
148
+ return [min, item];
133
149
  }
134
150
  return null;
135
151
  });
@@ -137,37 +153,28 @@ export const getAreaPath = _ref2 => {
137
153
  if (range === null) {
138
154
  return {
139
155
  x: 0,
156
+ y: 0,
140
157
  low: null,
141
158
  high: null,
142
159
  isValid: false
143
160
  };
144
161
  }
145
- let xValue = index;
146
- if (!isCategoricalScale(xScale) && xData && xData[index] !== undefined) {
147
- xValue = xData[index];
162
+
163
+ // Determine the position along the independent (index/category) axis.
164
+ let indexValue = index;
165
+ const indexScale = categoryAxisIsX ? xScale : yScale;
166
+ const indexData = categoryAxisIsX ? xData : yData;
167
+ if (!isCategoricalScale(indexScale) && indexData && indexData[index] !== undefined) {
168
+ indexValue = indexData[index];
148
169
  }
149
- const xPoint = projectPoint({
150
- x: xValue,
151
- y: 0,
152
- xScale,
153
- yScale
154
- });
155
- const lowPoint = projectPoint({
156
- x: xValue,
157
- y: range[0],
158
- xScale,
159
- yScale
160
- });
161
- const highPoint = projectPoint({
162
- x: xValue,
163
- y: range[1],
164
- xScale,
165
- yScale
166
- });
170
+ const position = getPointOnScale(indexValue, indexScale);
171
+ const low = getPointOnScale(range[0], valueScale);
172
+ const high = getPointOnScale(range[1], valueScale);
167
173
  return {
168
- x: xPoint.x,
169
- low: lowPoint.y,
170
- high: highPoint.y,
174
+ x: categoryAxisIsX ? position : 0,
175
+ y: !categoryAxisIsX ? position : 0,
176
+ low,
177
+ high,
171
178
  isValid: true
172
179
  };
173
180
  });
@@ -175,16 +182,25 @@ export const getAreaPath = _ref2 => {
175
182
  // When connectNulls is true, filter out invalid points before rendering
176
183
  // When false, use defined() to create gaps in the area
177
184
  const filteredPoints = connectNulls ? dataPoints.filter(d => d.isValid) : dataPoints;
178
- const areaGenerator = d3Area().x(d => d.x).y0(d => {
179
- var _d$low;
180
- return (_d$low = d.low) != null ? _d$low : 0;
181
- }) // Bottom boundary (low values), fallback to 0
182
- .y1(d => {
183
- var _d$high;
184
- return (_d$high = d.high) != null ? _d$high : 0;
185
- }) // Top boundary (high values), fallback to 0
186
- .curve(curveFunction).defined(d => connectNulls || d.isValid && d.low != null && d.high != null); // Only draw where both values exist
187
-
185
+ const areaGenerator = d3Area();
186
+ if (categoryAxisIsX) {
187
+ areaGenerator.x(d => d.x).y0(d => {
188
+ var _d$low;
189
+ return (_d$low = d.low) != null ? _d$low : 0;
190
+ }).y1(d => {
191
+ var _d$high;
192
+ return (_d$high = d.high) != null ? _d$high : 0;
193
+ });
194
+ } else {
195
+ areaGenerator.y(d => d.y).x0(d => {
196
+ var _d$low2;
197
+ return (_d$low2 = d.low) != null ? _d$low2 : 0;
198
+ }).x1(d => {
199
+ var _d$high2;
200
+ return (_d$high2 = d.high) != null ? _d$high2 : 0;
201
+ });
202
+ }
203
+ areaGenerator.curve(curveFunction).defined(d => connectNulls || d.isValid && d.low != null && d.high != null);
188
204
  const result = areaGenerator(filteredPoints);
189
205
  return result != null ? result : '';
190
206
  };
@@ -216,22 +232,35 @@ export const lineToPath = (x1, y1, x2, y2) => {
216
232
  * const roundedPath = getBarPath(10, 20, 50, 100, 8, true, false);
217
233
  * ```
218
234
  */
219
- export const getBarPath = (x, y, width, height, radius, roundTop, roundBottom) => {
235
+ export const getBarPath = function (x, y, width, height, radius, roundTop, roundBottom, layout) {
236
+ if (layout === void 0) {
237
+ layout = 'vertical';
238
+ }
239
+ const barsGrowVertically = layout !== 'horizontal';
220
240
  const roundBothSides = roundTop && roundBottom;
221
241
  const r = Math.min(radius, width / 2, roundBothSides ? height / 2 : height);
222
- const topR = roundTop ? r : 0;
223
- const bottomR = roundBottom ? r : 0;
242
+
243
+ // In vertical layout (bars grow up/down):
244
+ // - roundTop rounds the top face (min Y)
245
+ // - roundBottom rounds the bottom face (max Y)
246
+ // In horizontal layout (bars grow left/right):
247
+ // - roundTop rounds the right face (max X)
248
+ // - roundBottom rounds the left face (min X)
249
+ const rTL = barsGrowVertically ? roundTop ? r : 0 : roundBottom ? r : 0;
250
+ const rTR = barsGrowVertically ? roundTop ? r : 0 : roundTop ? r : 0;
251
+ const rBR = barsGrowVertically ? roundBottom ? r : 0 : roundTop ? r : 0;
252
+ const rBL = barsGrowVertically ? roundBottom ? r : 0 : roundBottom ? r : 0;
224
253
 
225
254
  // Build path with selective rounding
226
- let path = "M " + (x + (roundTop ? r : 0)) + " " + y;
227
- path += " L " + (x + width - topR) + " " + y;
228
- path += " A " + topR + " " + topR + " 0 0 1 " + (x + width) + " " + (y + topR);
229
- path += " L " + (x + width) + " " + (y + height - bottomR);
230
- path += " A " + bottomR + " " + bottomR + " 0 0 1 " + (x + width - bottomR) + " " + (y + height);
231
- path += " L " + (x + bottomR) + " " + (y + height);
232
- path += " A " + bottomR + " " + bottomR + " 0 0 1 " + x + " " + (y + height - bottomR);
233
- path += " L " + x + " " + (y + topR);
234
- path += " A " + topR + " " + topR + " 0 0 1 " + (x + topR) + " " + y;
255
+ let path = "M " + (x + rTL) + " " + y;
256
+ path += " L " + (x + width - rTR) + " " + y;
257
+ path += " A " + rTR + " " + rTR + " 0 0 1 " + (x + width) + " " + (y + rTR);
258
+ path += " L " + (x + width) + " " + (y + height - rBR);
259
+ path += " A " + rBR + " " + rBR + " 0 0 1 " + (x + width - rBR) + " " + (y + height);
260
+ path += " L " + (x + rBL) + " " + (y + height);
261
+ path += " A " + rBL + " " + rBL + " 0 0 1 " + x + " " + (y + height - rBL);
262
+ path += " L " + x + " " + (y + rTL);
263
+ path += " A " + rTL + " " + rTL + " 0 0 1 " + (x + rTL) + " " + y;
235
264
  path += ' Z';
236
265
  return path;
237
266
  };
@@ -1,4 +1,4 @@
1
- import { applyBandScale, applySerializableScale, isCategoricalScale, isLogScale, isNumericScale } from './scale';
1
+ import { applyBandScale, applySerializableScale, isCategoricalScale, isLogScale } from './scale';
2
2
 
3
3
  /**
4
4
  * Position a label should be placed relative to the point
@@ -164,7 +164,8 @@ export const projectPoints = _ref3 => {
164
164
  xScale,
165
165
  yScale,
166
166
  xData,
167
- yData
167
+ yData,
168
+ layout = 'vertical'
168
169
  } = _ref3;
169
170
  if (data.length === 0) {
170
171
  return [];
@@ -182,28 +183,37 @@ export const projectPoints = _ref3 => {
182
183
  });
183
184
  }
184
185
 
185
- // For scales with axis data, determine the correct x value
186
- let xValue = index;
186
+ // Determine values/scales based on role (index vs value) and layout.
187
+ const categoryAxisIsX = layout !== 'horizontal';
188
+ const indexScale = categoryAxisIsX ? xScale : yScale;
189
+ const indexData = categoryAxisIsX ? xData : yData;
187
190
 
188
- // For band scales, always use the index
189
- if (!isCategoricalScale(xScale)) {
190
- // For numeric scales with axis data, use the axis data values instead of indices
191
- if (xData && Array.isArray(xData) && xData.length > 0) {
192
- // Check if it's numeric data
193
- if (typeof xData[0] === 'number') {
194
- var _numericXData$index;
195
- const numericXData = xData;
196
- xValue = (_numericXData$index = numericXData[index]) != null ? _numericXData$index : index;
191
+ // 1. Calculate position along the index axis (categorical or numeric domain).
192
+ let indexValue = index;
193
+ if (!isCategoricalScale(indexScale)) {
194
+ if (indexData && Array.isArray(indexData) && indexData.length > 0) {
195
+ if (typeof indexData[0] === 'number') {
196
+ var _indexData$index;
197
+ indexValue = (_indexData$index = indexData[index]) != null ? _indexData$index : index;
197
198
  }
198
199
  }
199
200
  }
200
- let yValue = value;
201
- if (isNumericScale(yScale) && yData && Array.isArray(yData) && yData.length > 0 && typeof yData[0] === 'number' && typeof value === 'number') {
202
- yValue = value;
201
+
202
+ // 2. Calculate position along the value axis (measured magnitude).
203
+ const valueAsNumber = value;
204
+
205
+ // 3. Project final coordinates based on layout.
206
+ if (categoryAxisIsX) {
207
+ return projectPoint({
208
+ x: indexValue,
209
+ y: valueAsNumber,
210
+ xScale,
211
+ yScale
212
+ });
203
213
  }
204
214
  return projectPoint({
205
- x: xValue,
206
- y: yValue,
215
+ x: valueAsNumber,
216
+ y: indexValue,
207
217
  xScale,
208
218
  yScale
209
219
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinbase/cds-mobile-visualization",
3
- "version": "3.4.0-beta.22",
3
+ "version": "3.4.0-beta.24",
4
4
  "description": "Coinbase Design System - Mobile Visualization Native",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,9 +36,9 @@
36
36
  "CHANGELOG"
37
37
  ],
38
38
  "peerDependencies": {
39
- "@coinbase/cds-common": "^8.49.0",
39
+ "@coinbase/cds-common": "^8.52.2",
40
40
  "@coinbase/cds-lottie-files": "^3.3.4",
41
- "@coinbase/cds-mobile": "^8.49.0",
41
+ "@coinbase/cds-mobile": "^8.52.2",
42
42
  "@coinbase/cds-utils": "^2.3.5",
43
43
  "@shopify/react-native-skia": "^1.12.4 || ^2.0.0",
44
44
  "react": "^18.3.1",
@@ -57,9 +57,9 @@
57
57
  "@babel/preset-env": "^7.28.0",
58
58
  "@babel/preset-react": "^7.27.1",
59
59
  "@babel/preset-typescript": "^7.27.1",
60
- "@coinbase/cds-common": "^8.49.0",
60
+ "@coinbase/cds-common": "^8.52.2",
61
61
  "@coinbase/cds-lottie-files": "^3.3.4",
62
- "@coinbase/cds-mobile": "^8.49.0",
62
+ "@coinbase/cds-mobile": "^8.52.2",
63
63
  "@coinbase/cds-utils": "^2.3.5",
64
64
  "@shopify/react-native-skia": "1.12.4",
65
65
  "@types/react": "^18.3.12",