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