@coinbase/cds-mobile-visualization 3.4.0-beta.9 → 3.4.0

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 (167) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/dts/chart/CartesianChart.d.ts +92 -7
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/ChartContextBridge.d.ts.map +1 -1
  5. package/dts/chart/ChartProvider.d.ts +3 -0
  6. package/dts/chart/ChartProvider.d.ts.map +1 -1
  7. package/dts/chart/Path.d.ts +36 -13
  8. package/dts/chart/Path.d.ts.map +1 -1
  9. package/dts/chart/PeriodSelector.d.ts +20 -5
  10. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  11. package/dts/chart/area/Area.d.ts +14 -11
  12. package/dts/chart/area/Area.d.ts.map +1 -1
  13. package/dts/chart/area/AreaChart.d.ts +33 -9
  14. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  15. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  16. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  17. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  18. package/dts/chart/axis/Axis.d.ts +22 -42
  19. package/dts/chart/axis/Axis.d.ts.map +1 -1
  20. package/dts/chart/axis/XAxis.d.ts +6 -0
  21. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  22. package/dts/chart/axis/YAxis.d.ts +1 -0
  23. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  24. package/dts/chart/bar/Bar.d.ts +51 -51
  25. package/dts/chart/bar/Bar.d.ts.map +1 -1
  26. package/dts/chart/bar/BarChart.d.ts +56 -11
  27. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  28. package/dts/chart/bar/BarPlot.d.ts +2 -1
  29. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  30. package/dts/chart/bar/BarStack.d.ts +45 -20
  31. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  32. package/dts/chart/bar/BarStackGroup.d.ts +2 -1
  33. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  34. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  35. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  36. package/dts/chart/gradient/Gradient.d.ts +5 -0
  37. package/dts/chart/gradient/Gradient.d.ts.map +1 -1
  38. package/dts/chart/index.d.ts +1 -0
  39. package/dts/chart/index.d.ts.map +1 -1
  40. package/dts/chart/legend/DefaultLegendEntry.d.ts +5 -0
  41. package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
  42. package/dts/chart/legend/DefaultLegendShape.d.ts +5 -0
  43. package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
  44. package/dts/chart/legend/Legend.d.ts +168 -0
  45. package/dts/chart/legend/Legend.d.ts.map +1 -0
  46. package/dts/chart/legend/index.d.ts +4 -0
  47. package/dts/chart/legend/index.d.ts.map +1 -0
  48. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  49. package/dts/chart/line/Line.d.ts +23 -19
  50. package/dts/chart/line/Line.d.ts.map +1 -1
  51. package/dts/chart/line/LineChart.d.ts +26 -9
  52. package/dts/chart/line/LineChart.d.ts.map +1 -1
  53. package/dts/chart/line/ReferenceLine.d.ts +1 -0
  54. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  55. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  56. package/dts/chart/point/Point.d.ts +26 -2
  57. package/dts/chart/point/Point.d.ts.map +1 -1
  58. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +32 -2
  59. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
  60. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +2 -1
  61. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -1
  62. package/dts/chart/scrubber/Scrubber.d.ts +86 -17
  63. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  64. package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts +12 -0
  65. package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts.map +1 -0
  66. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +10 -0
  67. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
  68. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +16 -1
  69. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -1
  70. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
  71. package/dts/chart/utils/axis.d.ts +45 -10
  72. package/dts/chart/utils/axis.d.ts.map +1 -1
  73. package/dts/chart/utils/bar.d.ts +190 -0
  74. package/dts/chart/utils/bar.d.ts.map +1 -1
  75. package/dts/chart/utils/chart.d.ts +32 -0
  76. package/dts/chart/utils/chart.d.ts.map +1 -1
  77. package/dts/chart/utils/context.d.ts +21 -6
  78. package/dts/chart/utils/context.d.ts.map +1 -1
  79. package/dts/chart/utils/gradient.d.ts +3 -1
  80. package/dts/chart/utils/gradient.d.ts.map +1 -1
  81. package/dts/chart/utils/path.d.ts +26 -0
  82. package/dts/chart/utils/path.d.ts.map +1 -1
  83. package/dts/chart/utils/point.d.ts +24 -12
  84. package/dts/chart/utils/point.d.ts.map +1 -1
  85. package/dts/chart/utils/scale.d.ts +11 -0
  86. package/dts/chart/utils/scale.d.ts.map +1 -1
  87. package/dts/chart/utils/scrubber.d.ts +2 -1
  88. package/dts/chart/utils/scrubber.d.ts.map +1 -1
  89. package/dts/chart/utils/transition.d.ts +63 -22
  90. package/dts/chart/utils/transition.d.ts.map +1 -1
  91. package/dts/sparkline/Sparkline.d.ts +2 -1
  92. package/dts/sparkline/Sparkline.d.ts.map +1 -1
  93. package/dts/sparkline/SparklineArea.d.ts +2 -1
  94. package/dts/sparkline/SparklineArea.d.ts.map +1 -1
  95. package/dts/sparkline/SparklineGradient.d.ts +2 -1
  96. package/dts/sparkline/SparklineGradient.d.ts.map +1 -1
  97. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts +2 -1
  98. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts.map +1 -1
  99. package/esm/chart/CartesianChart.js +176 -82
  100. package/esm/chart/ChartContextBridge.js +14 -3
  101. package/esm/chart/ChartProvider.js +2 -2
  102. package/esm/chart/Path.js +34 -29
  103. package/esm/chart/PeriodSelector.js +5 -1
  104. package/esm/chart/__stories__/CartesianChart.stories.js +16 -80
  105. package/esm/chart/__stories__/ChartAccessibility.stories.js +721 -0
  106. package/esm/chart/__stories__/ChartTransitions.stories.js +625 -0
  107. package/esm/chart/__stories__/PeriodSelector.stories.js +99 -1
  108. package/esm/chart/area/Area.js +21 -9
  109. package/esm/chart/area/AreaChart.js +18 -13
  110. package/esm/chart/area/DottedArea.js +28 -18
  111. package/esm/chart/area/GradientArea.js +14 -7
  112. package/esm/chart/area/SolidArea.js +6 -2
  113. package/esm/chart/area/__stories__/AreaChart.stories.js +47 -5
  114. package/esm/chart/axis/Axis.js +5 -41
  115. package/esm/chart/axis/XAxis.js +116 -47
  116. package/esm/chart/axis/YAxis.js +105 -26
  117. package/esm/chart/axis/__stories__/Axis.stories.js +324 -48
  118. package/esm/chart/bar/Bar.js +17 -15
  119. package/esm/chart/bar/BarChart.js +38 -33
  120. package/esm/chart/bar/BarPlot.js +40 -45
  121. package/esm/chart/bar/BarStack.js +92 -475
  122. package/esm/chart/bar/BarStackGroup.js +37 -27
  123. package/esm/chart/bar/DefaultBar.js +27 -18
  124. package/esm/chart/bar/DefaultBarStack.js +25 -9
  125. package/esm/chart/bar/__stories__/BarChart.stories.js +728 -54
  126. package/esm/chart/gradient/Gradient.js +2 -1
  127. package/esm/chart/index.js +1 -0
  128. package/esm/chart/legend/DefaultLegendEntry.js +42 -0
  129. package/esm/chart/legend/DefaultLegendShape.js +64 -0
  130. package/esm/chart/legend/Legend.js +59 -0
  131. package/esm/chart/legend/__stories__/Legend.stories.js +574 -0
  132. package/esm/chart/legend/index.js +3 -0
  133. package/esm/chart/line/DottedLine.js +6 -2
  134. package/esm/chart/line/Line.js +42 -38
  135. package/esm/chart/line/LineChart.js +36 -12
  136. package/esm/chart/line/SolidLine.js +6 -2
  137. package/esm/chart/line/__stories__/LineChart.stories.js +236 -590
  138. package/esm/chart/line/__stories__/ReferenceLine.stories.js +95 -1
  139. package/esm/chart/point/Point.js +35 -36
  140. package/esm/chart/scrubber/DefaultScrubberBeacon.js +41 -38
  141. package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -10
  142. package/esm/chart/scrubber/Scrubber.js +67 -35
  143. package/esm/chart/scrubber/ScrubberAccessibilityView.js +177 -0
  144. package/esm/chart/scrubber/ScrubberBeaconGroup.js +30 -22
  145. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +35 -8
  146. package/esm/chart/scrubber/ScrubberProvider.js +29 -24
  147. package/esm/chart/scrubber/__stories__/Scrubber.stories.js +946 -0
  148. package/esm/chart/utils/axis.js +88 -44
  149. package/esm/chart/utils/bar.js +820 -0
  150. package/esm/chart/utils/chart.js +34 -7
  151. package/esm/chart/utils/context.js +7 -0
  152. package/esm/chart/utils/gradient.js +8 -4
  153. package/esm/chart/utils/path.js +91 -61
  154. package/esm/chart/utils/point.js +92 -39
  155. package/esm/chart/utils/scale.js +13 -2
  156. package/esm/chart/utils/scrubber.js +12 -5
  157. package/esm/chart/utils/transition.js +108 -60
  158. package/esm/sparkline/Sparkline.js +2 -1
  159. package/esm/sparkline/SparklineArea.js +2 -1
  160. package/esm/sparkline/SparklineGradient.js +2 -1
  161. package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
  162. package/esm/sparkline/sparkline-interactive/SparklineInteractive.js +2 -1
  163. package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
  164. package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
  165. package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +2 -0
  166. package/package.json +5 -6
  167. package/esm/chart/__stories__/Chart.stories.js +0 -77
@@ -3,10 +3,41 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
3
3
  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; }
4
4
  import { useCallback, useMemo, useState } from 'react';
5
5
  import { getChartDomain, getChartRange, isValidBounds } from './chart';
6
+ import { getPointOnScale } from './point';
6
7
  import { getCategoricalScale, getNumericScale, isCategoricalScale, isNumericScale } from './scale';
7
8
  export const defaultAxisId = 'DEFAULT_AXIS_ID';
8
9
  export const defaultAxisScaleType = 'linear';
9
10
 
11
+ /**
12
+ * Position options for band scale axis elements (grid lines, tick marks, labels).
13
+ *
14
+ * - `'start'` - At the start of each step (before bar padding)
15
+ * - `'middle'` - At the center of each band
16
+ * - `'end'` - At the end of each step (after bar padding)
17
+ * - `'edges'` - At start of each tick, plus end for the last tick (encloses the chart)
18
+ *
19
+ * @note These properties only apply when using a band scale (`scaleType: 'band'`).
20
+ */
21
+
22
+ /**
23
+ * Converts an AxisBandPlacement to the corresponding PointAnchor for use with getPointOnScale.
24
+ *
25
+ * @param placement - The axis placement value
26
+ * @returns The corresponding PointAnchor for scale calculations
27
+ */
28
+ export const toPointAnchor = placement => {
29
+ switch (placement) {
30
+ case 'edges': // edges uses stepStart for each tick, stepEnd handled separately
31
+ case 'start':
32
+ return 'stepStart';
33
+ case 'end':
34
+ return 'stepEnd';
35
+ case 'middle':
36
+ default:
37
+ return 'middle';
38
+ }
39
+ };
40
+
10
41
  /**
11
42
  * Axis configuration with computed bounds
12
43
  */
@@ -16,7 +47,7 @@ export const defaultAxisScaleType = 'linear';
16
47
  */
17
48
 
18
49
  /**
19
- * Gets a D3 scale based on the axis configuration.
50
+ * Gets a D3 scale based on the cartesian axis configuration.
20
51
  * Handles both numeric (linear/log) and categorical (band) scales.
21
52
  *
22
53
  * For numeric scales, the domain limit controls whether bounds are "nice" (human-friendly)
@@ -26,19 +57,27 @@ export const defaultAxisScaleType = 'linear';
26
57
  * @returns The D3 scale function
27
58
  * @throws An Error if bounds are invalid
28
59
  */
29
- export const getAxisScale = _ref => {
60
+ export const getCartesianAxisScale = _ref => {
30
61
  var _config$scaleType;
31
62
  let {
32
63
  config,
33
64
  type,
34
65
  range,
35
- dataDomain
66
+ dataDomain,
67
+ layout = 'vertical'
36
68
  } = _ref;
37
69
  const scaleType = (_config$scaleType = config == null ? void 0 : config.scaleType) != null ? _config$scaleType : 'linear';
38
70
  let adjustedRange = range;
39
71
 
40
- // Invert range for Y axis for SVG coordinate system
41
- if (type === 'y') {
72
+ // Determine if this axis needs range inversion for SVG coordinate system.
73
+ // SVG Y coordinates increase downward, so we need to invert for value axes
74
+ // where we want higher values at the top.
75
+ //
76
+ // For vertical layout: Y axis is the value axis -> invert (higher values at top)
77
+ // For horizontal layout: Y axis is the category axis -> don't invert (first category at top is natural)
78
+ // X axis never needs inversion (left-to-right is natural for both layouts)
79
+ const shouldInvertRange = type === 'y' && layout !== 'horizontal';
80
+ if (shouldInvertRange) {
42
81
  adjustedRange = {
43
82
  min: adjustedRange.max,
44
83
  max: adjustedRange.min
@@ -52,7 +91,7 @@ export const getAxisScale = _ref => {
52
91
  max: (_config$domain$max = config.domain.max) != null ? _config$domain$max : dataDomain.max
53
92
  };
54
93
  }
55
- if (!isValidBounds(adjustedDomain)) throw new Error('Invalid domain bounds. See https://cds.coinbase.com/http://localhost:3000/components/graphs/XAxis/#domain');
94
+ if (!isValidBounds(adjustedDomain)) throw new Error('Invalid domain bounds. See https://cds.coinbase.com/components/charts/XAxis/#domain');
56
95
  if (scaleType === 'band') {
57
96
  var _config$categoryPaddi;
58
97
  return getCategoricalScale({
@@ -90,6 +129,8 @@ export const getAxisConfig = function (type, axes, defaultId, defaultScaleType)
90
129
  defaultScaleType = defaultAxisScaleType;
91
130
  }
92
131
  const defaultDomainLimit = type === 'x' ? 'strict' : 'nice';
132
+ const axisName = type === 'x' ? 'x-axis' : 'y-axis';
133
+ const axisDocUrl = type === 'x' ? 'https://cds.coinbase.com/components/charts/XAxis' : 'https://cds.coinbase.com/components/charts/YAxis';
93
134
  if (!axes) {
94
135
  return [{
95
136
  id: defaultId,
@@ -106,16 +147,27 @@ export const getAxisConfig = function (type, axes, defaultId, defaultScaleType)
106
147
  } = _ref2;
107
148
  return id === undefined;
108
149
  })) {
109
- throw new Error('When defining multiple axes, each must have a unique id. See https://cds.coinbase.com/components/graphs/YAxis/#multiple-y-axes.');
150
+ throw new Error("When defining multiple " + axisName + ", each must have a unique id. See " + axisDocUrl + ".");
151
+ }
152
+ if (axesLength > 1) {
153
+ const ids = axes.map(_ref3 => {
154
+ let {
155
+ id
156
+ } = _ref3;
157
+ return id;
158
+ }).filter(id => id !== undefined);
159
+ if (new Set(ids).size !== ids.length) {
160
+ throw new Error("When defining multiple " + axisName + ", each must have a unique id. See " + axisDocUrl + ".");
161
+ }
110
162
  }
111
- return axes.map(_ref3 => {
163
+ return axes.map(_ref4 => {
112
164
  let {
113
165
  id
114
- } = _ref3,
115
- axis = _objectWithoutPropertiesLoose(_ref3, _excluded);
166
+ } = _ref4,
167
+ axis = _objectWithoutPropertiesLoose(_ref4, _excluded);
116
168
  return _extends({
117
169
  // defaults the axis id if only a single axis is provided
118
- id: axesLength > 1 ? id != null ? id : defaultAxisId : id,
170
+ id: axesLength > 1 ? id != null ? id : defaultAxisId : id != null ? id : defaultId,
119
171
  scaleType: defaultScaleType,
120
172
  domainLimit: defaultDomainLimit
121
173
  }, axis);
@@ -137,10 +189,14 @@ export const getAxisConfig = function (type, axes, defaultId, defaultScaleType)
137
189
  * @param axisParam - The axis configuration
138
190
  * @param series - Array of series objects (for stacking support)
139
191
  * @param axisType - Whether this is an 'x' or 'y' axis
192
+ * @param layout - The chart layout orientation
140
193
  * @returns The calculated axis bounds
141
194
  */
142
- export const getAxisDomain = (axisParam, series, axisType) => {
195
+ export const getCartesianAxisDomain = function (axisParam, series, axisType, layout) {
143
196
  var _finalDomain$min, _finalDomain$max;
197
+ if (layout === void 0) {
198
+ layout = 'vertical';
199
+ }
144
200
  let dataDomain = null;
145
201
  if (axisParam.data && Array.isArray(axisParam.data) && axisParam.data.length > 0) {
146
202
  const firstItem = axisParam.data[0];
@@ -162,7 +218,10 @@ export const getAxisDomain = (axisParam, series, axisType) => {
162
218
  }
163
219
 
164
220
  // Calculate domain from series data
165
- const seriesDomain = axisType === 'x' ? getChartDomain(series) : getChartRange(series);
221
+ // In vertical layout: X is category (index), Y is value (value)
222
+ // In horizontal layout: Y is category (index), X is value (value)
223
+ const isCategoryAxis = layout !== 'horizontal' && axisType === 'x' || layout === 'horizontal' && axisType === 'y';
224
+ const seriesDomain = isCategoryAxis ? getChartDomain(series) : getChartRange(series);
166
225
 
167
226
  // If data sets the domain, use that instead of the series domain
168
227
  const preferredDataDomain = dataDomain != null ? dataDomain : seriesDomain;
@@ -456,7 +515,8 @@ const generateEvenlyDistributedTicks = (scale, tickInterval, possibleTickValues,
456
515
  * });
457
516
  * // Returns tick positions centered in each selected band
458
517
  */
459
- export const getAxisTicksData = _ref4 => {
518
+ export const getAxisTicksData = _ref5 => {
519
+ var _options$anchor;
460
520
  let {
461
521
  ticks,
462
522
  scaleFunction,
@@ -465,54 +525,38 @@ export const getAxisTicksData = _ref4 => {
465
525
  possibleTickValues,
466
526
  tickInterval,
467
527
  options
468
- } = _ref4;
528
+ } = _ref5;
529
+ const anchor = (_options$anchor = options == null ? void 0 : options.anchor) != null ? _options$anchor : 'middle';
530
+
469
531
  // Handle band scales
470
532
  if (isCategoricalScale(scaleFunction)) {
533
+ const bandScale = scaleFunction;
534
+
471
535
  // If explicit ticks are provided as array, use them
472
536
  if (Array.isArray(ticks)) {
473
- return ticks.filter(index => index >= 0 && index < categories.length).map(index => {
474
- var _bandwidth;
475
- // Band scales expect numeric indices, not category strings
476
- const position = scaleFunction(index);
477
- if (position === undefined) return null;
478
- return {
479
- tick: index,
480
- position: position + ((_bandwidth = scaleFunction.bandwidth == null ? void 0 : scaleFunction.bandwidth()) != null ? _bandwidth : 0) / 2
481
- };
482
- }).filter(Boolean);
537
+ return ticks.filter(index => index >= 0 && index < categories.length).map(index => ({
538
+ tick: index,
539
+ position: getPointOnScale(index, bandScale, anchor)
540
+ }));
483
541
  }
484
542
 
485
543
  // If a tick function is provided, use it to filter
486
544
  if (typeof ticks === 'function') {
487
545
  return categories.map((category, index) => {
488
- var _bandwidth2;
489
546
  if (!ticks(index)) return null;
490
-
491
- // Band scales expect numeric indices, not category strings
492
- const position = scaleFunction(index);
493
- if (position === undefined) return null;
494
547
  return {
495
548
  tick: index,
496
- position: position + ((_bandwidth2 = scaleFunction.bandwidth == null ? void 0 : scaleFunction.bandwidth()) != null ? _bandwidth2 : 0) / 2
549
+ position: getPointOnScale(index, bandScale, anchor)
497
550
  };
498
551
  }).filter(Boolean);
499
552
  }
500
- if (typeof ticks === 'boolean' && !ticks) {
501
- return [];
502
- }
503
553
 
504
554
  // For band scales without explicit ticks, show all categories
505
555
  // requestedTickCount is ignored for categorical scales - use ticks parameter to control visibility
506
- return categories.map((category, index) => {
507
- var _bandwidth3;
508
- // Band scales expect numeric indices, not category strings
509
- const position = scaleFunction(index);
510
- if (position === undefined) return null;
511
- return {
512
- tick: index,
513
- position: position + ((_bandwidth3 = scaleFunction.bandwidth == null ? void 0 : scaleFunction.bandwidth()) != null ? _bandwidth3 : 0) / 2
514
- };
515
- }).filter(Boolean);
556
+ return categories.map((_, index) => ({
557
+ tick: index,
558
+ position: getPointOnScale(index, bandScale, anchor)
559
+ }));
516
560
  }
517
561
 
518
562
  // Handle numeric scales