@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
@@ -9,34 +9,39 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
9
9
  let {
10
10
  seriesId,
11
11
  dataIndex,
12
- dataX,
12
+ dataIndexValue,
13
13
  isIdle,
14
14
  BeaconComponent,
15
15
  idlePulse,
16
+ animate,
16
17
  transitions,
17
18
  className,
18
19
  style,
19
20
  testID,
20
- beaconRef
21
+ beaconRef,
22
+ stroke
21
23
  } = _ref;
22
24
  const {
25
+ layout,
23
26
  getSeries,
24
27
  getSeriesData,
25
28
  getXScale,
26
- getYScale
29
+ getYScale,
30
+ getXAxis,
31
+ getYAxis
27
32
  } = useCartesianChartContext();
28
33
  const series = useMemo(() => getSeries(seriesId), [getSeries, seriesId]);
29
34
  const sourceData = useMemo(() => getSeriesData(seriesId), [getSeriesData, seriesId]);
30
35
  const gradient = series === null || series === void 0 ? void 0 : series.gradient;
31
36
 
32
- // Get dataY from series data
33
- const dataY = useMemo(() => {
37
+ // Get dataValue from series data
38
+ const dataValue = useMemo(() => {
34
39
  if (sourceData && dataIndex >= 0 && dataIndex < sourceData.length) {
35
- const dataValue = sourceData[dataIndex];
36
- if (typeof dataValue === 'number') {
37
- return dataValue;
38
- } else if (Array.isArray(dataValue)) {
39
- const validValues = dataValue.filter(val => val !== null);
40
+ const value = sourceData[dataIndex];
41
+ if (typeof value === 'number') {
42
+ return value;
43
+ } else if (Array.isArray(value)) {
44
+ const validValues = value.filter(val => val !== null);
40
45
  if (validValues.length >= 1) {
41
46
  return validValues[validValues.length - 1];
42
47
  }
@@ -48,18 +53,27 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
48
53
  // Evaluate gradient color
49
54
  const color = useMemo(() => {
50
55
  var _series$color, _series$color2;
51
- if (dataY === undefined) return (_series$color = series === null || series === void 0 ? void 0 : series.color) !== null && _series$color !== void 0 ? _series$color : 'var(--color-fgPrimary)';
56
+ if (dataValue === undefined) return (_series$color = series === null || series === void 0 ? void 0 : series.color) !== null && _series$color !== void 0 ? _series$color : 'var(--color-fgPrimary)';
52
57
  if (gradient) {
53
- const xScale = getXScale();
58
+ const xScale = getXScale(series === null || series === void 0 ? void 0 : series.xAxisId);
54
59
  const yScale = getYScale(series === null || series === void 0 ? void 0 : series.yAxisId);
55
60
  if (xScale && yScale) {
61
+ const categoryAxisIsX = layout !== 'horizontal';
56
62
  const gradientScale = gradient.axis === 'x' ? xScale : yScale;
57
63
  const stops = getGradientConfig(gradient, xScale, yScale);
58
64
  if (stops) {
59
65
  var _gradient$axis;
60
66
  const gradientAxis = (_gradient$axis = gradient.axis) !== null && _gradient$axis !== void 0 ? _gradient$axis : 'y';
61
- const dataValue = gradientAxis === 'x' ? dataX : dataY;
62
- const evaluatedColor = evaluateGradientAtValue(stops, dataValue, gradientScale);
67
+ // Determine the correct data value to evaluate against based on gradient axis and layout
68
+ let evalValue;
69
+ if (gradientAxis === 'x') {
70
+ // X-axis gradient: In vertical it's the index, in horizontal it's the value.
71
+ evalValue = categoryAxisIsX ? dataIndexValue : dataValue;
72
+ } else {
73
+ // Y-axis gradient: In vertical it's the value, in horizontal it's the index.
74
+ evalValue = categoryAxisIsX ? dataValue : dataIndexValue;
75
+ }
76
+ const evaluatedColor = evaluateGradientAtValue(stops, evalValue, gradientScale);
63
77
  if (evaluatedColor) {
64
78
  return evaluatedColor;
65
79
  }
@@ -67,17 +81,20 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
67
81
  }
68
82
  }
69
83
  return (_series$color2 = series === null || series === void 0 ? void 0 : series.color) !== null && _series$color2 !== void 0 ? _series$color2 : 'var(--color-fgPrimary)';
70
- }, [gradient, dataX, dataY, series === null || series === void 0 ? void 0 : series.color, series === null || series === void 0 ? void 0 : series.yAxisId, getXScale, getYScale]);
71
- if (dataY === undefined) return null;
84
+ }, [gradient, dataIndexValue, dataValue, series === null || series === void 0 ? void 0 : series.color, series === null || series === void 0 ? void 0 : series.xAxisId, series === null || series === void 0 ? void 0 : series.yAxisId, getXScale, getYScale, layout]);
85
+ if (dataValue === undefined) return null;
86
+ const categoryAxisIsX = layout !== 'horizontal';
72
87
  return /*#__PURE__*/_jsx(BeaconComponent, {
73
88
  ref: beaconRef,
89
+ animate: animate,
74
90
  className: className,
75
91
  color: color,
76
- dataX: dataX,
77
- dataY: dataY,
92
+ dataX: categoryAxisIsX ? dataIndexValue : dataValue,
93
+ dataY: categoryAxisIsX ? dataValue : dataIndexValue,
78
94
  idlePulse: idlePulse,
79
95
  isIdle: isIdle,
80
96
  seriesId: seriesId,
97
+ stroke: stroke,
81
98
  style: style,
82
99
  testID: testID,
83
100
  transitions: transitions
@@ -91,17 +108,22 @@ export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_r
91
108
  BeaconComponent = DefaultScrubberBeacon,
92
109
  className,
93
110
  style,
94
- testID
111
+ testID,
112
+ stroke
95
113
  } = _ref2;
96
114
  const ScrubberBeaconRefs = useRefMap();
97
115
  const {
98
116
  scrubberPosition
99
117
  } = useScrubberContext();
100
118
  const {
119
+ layout,
101
120
  getXScale,
121
+ getYScale,
102
122
  getXAxis,
123
+ getYAxis,
103
124
  dataLength,
104
- series
125
+ series,
126
+ animate
105
127
  } = useCartesianChartContext();
106
128
 
107
129
  // Expose imperative handle with pulse method
@@ -117,30 +139,31 @@ export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_r
117
139
  return (_series$filter = series === null || series === void 0 ? void 0 : series.filter(s => seriesIds.includes(s.id))) !== null && _series$filter !== void 0 ? _series$filter : [];
118
140
  }, [series, seriesIds]);
119
141
  const {
120
- dataX,
142
+ dataIndexValue,
121
143
  dataIndex
122
144
  } = useMemo(() => {
123
- const xScale = getXScale();
124
- const xAxis = getXAxis();
125
- if (!xScale) return {
126
- dataX: undefined,
145
+ const categoryAxisIsX = layout !== 'horizontal';
146
+ const indexScale = categoryAxisIsX ? getXScale() : getYScale();
147
+ const indexAxis = categoryAxisIsX ? getXAxis() : getYAxis();
148
+ if (!indexScale) return {
149
+ dataIndexValue: undefined,
127
150
  dataIndex: undefined
128
151
  };
129
152
  const dataIndex = scrubberPosition !== null && scrubberPosition !== void 0 ? scrubberPosition : Math.max(0, dataLength - 1);
130
153
 
131
- // Convert index to actual x value if axis has data
132
- let dataX;
133
- if (xAxis !== null && xAxis !== void 0 && xAxis.data && Array.isArray(xAxis.data) && xAxis.data[dataIndex] !== undefined) {
134
- const dataValue = xAxis.data[dataIndex];
135
- dataX = typeof dataValue === 'string' ? dataIndex : dataValue;
154
+ // Convert index to actual data value if axis has data
155
+ let dataIndexValue;
156
+ if (indexAxis !== null && indexAxis !== void 0 && indexAxis.data && Array.isArray(indexAxis.data) && indexAxis.data[dataIndex] !== undefined) {
157
+ const val = indexAxis.data[dataIndex];
158
+ dataIndexValue = typeof val === 'string' ? dataIndex : val;
136
159
  } else {
137
- dataX = dataIndex;
160
+ dataIndexValue = dataIndex;
138
161
  }
139
162
  return {
140
- dataX,
163
+ dataIndexValue,
141
164
  dataIndex
142
165
  };
143
- }, [getXScale, getXAxis, scrubberPosition, dataLength]);
166
+ }, [getXScale, getYScale, getXAxis, getYAxis, scrubberPosition, dataLength, layout]);
144
167
  const isIdle = scrubberPosition === undefined;
145
168
  const createBeaconRef = useCallback(seriesId => {
146
169
  return beaconRef => {
@@ -149,16 +172,18 @@ export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_r
149
172
  }
150
173
  };
151
174
  }, [ScrubberBeaconRefs]);
152
- if (dataX === undefined || dataIndex === undefined) return null;
175
+ if (dataIndexValue === undefined || dataIndex === undefined) return null;
153
176
  return filteredSeries.map(s => /*#__PURE__*/_jsx(BeaconWithData, {
154
177
  BeaconComponent: BeaconComponent,
178
+ animate: animate,
155
179
  beaconRef: createBeaconRef(s.id),
156
180
  className: className,
157
181
  dataIndex: dataIndex,
158
- dataX: dataX,
182
+ dataIndexValue: dataIndexValue,
159
183
  idlePulse: idlePulse,
160
184
  isIdle: isIdle,
161
185
  seriesId: s.id,
186
+ stroke: stroke,
162
187
  style: style,
163
188
  testID: testID ? "".concat(testID !== null && testID !== void 0 ? testID : 'beacon', "-").concat(s.id) : undefined,
164
189
  transitions: transitions
@@ -4,8 +4,9 @@ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object
4
4
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
5
5
  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); }
6
6
  import { memo, useCallback, useMemo, useState } from 'react';
7
+ import { usePreviousValue } from '@coinbase/cds-common/hooks/usePreviousValue';
7
8
  import { useCartesianChartContext } from '../ChartProvider';
8
- import { getPointOnScale, useScrubberContext } from '../utils';
9
+ import { defaultTransition, getPointOnScale, getTransition, instantTransition, useScrubberContext } from '../utils';
9
10
  import { calculateLabelYPositions, getLabelPosition } from '../utils/scrubber';
10
11
  import { DefaultScrubberBeaconLabel } from './DefaultScrubberBeaconLabel';
11
12
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -20,7 +21,10 @@ const PositionedLabel = /*#__PURE__*/memo(_ref => {
20
21
  onDimensionsChange,
21
22
  BeaconLabelComponent,
22
23
  labelHorizontalOffset,
23
- labelFont
24
+ labelFont,
25
+ updateTransition,
26
+ className,
27
+ style
24
28
  } = _ref;
25
29
  const pos = positions[index];
26
30
 
@@ -33,6 +37,7 @@ const PositionedLabel = /*#__PURE__*/memo(_ref => {
33
37
  const dx = position === 'right' ? labelHorizontalOffset : -labelHorizontalOffset;
34
38
  const horizontalAlignment = position === 'right' ? 'left' : 'right';
35
39
  return /*#__PURE__*/_jsx(BeaconLabelComponent, {
40
+ className: className,
36
41
  color: color,
37
42
  dx: dx,
38
43
  font: labelFont,
@@ -40,6 +45,8 @@ const PositionedLabel = /*#__PURE__*/memo(_ref => {
40
45
  label: label,
41
46
  onDimensionsChange: d => onDimensionsChange(seriesId, d),
42
47
  seriesId: seriesId,
48
+ style: style,
49
+ transition: updateTransition !== null && updateTransition !== void 0 ? updateTransition : instantTransition,
43
50
  x: x,
44
51
  y: y
45
52
  });
@@ -50,7 +57,11 @@ export const ScrubberBeaconLabelGroup = /*#__PURE__*/memo(_ref2 => {
50
57
  labelMinGap = 4,
51
58
  labelHorizontalOffset = 16,
52
59
  labelFont,
53
- BeaconLabelComponent = DefaultScrubberBeaconLabel
60
+ labelPreferredSide = 'right',
61
+ BeaconLabelComponent = DefaultScrubberBeaconLabel,
62
+ transitions,
63
+ className,
64
+ style
54
65
  } = _ref2;
55
66
  const {
56
67
  getSeries,
@@ -59,11 +70,20 @@ export const ScrubberBeaconLabelGroup = /*#__PURE__*/memo(_ref2 => {
59
70
  getYScale,
60
71
  getXAxis,
61
72
  drawingArea,
62
- dataLength
73
+ dataLength,
74
+ animate
63
75
  } = useCartesianChartContext();
64
76
  const {
65
77
  scrubberPosition
66
78
  } = useScrubberContext();
79
+ const isIdle = scrubberPosition === undefined;
80
+ const prevIsIdle = usePreviousValue(isIdle);
81
+ const isIdleTransition = prevIsIdle !== undefined && isIdle !== prevIsIdle;
82
+ const updateTransition = useMemo(() => {
83
+ if (isIdleTransition) return instantTransition;
84
+ if (!isIdle) return instantTransition;
85
+ return getTransition(transitions === null || transitions === void 0 ? void 0 : transitions.update, animate, defaultTransition);
86
+ }, [transitions === null || transitions === void 0 ? void 0 : transitions.update, isIdle, animate, isIdleTransition]);
67
87
  const [labelDimensions, setLabelDimensions] = useState({});
68
88
  const handleDimensionsChange = useCallback((seriesId, dimensions) => {
69
89
  setLabelDimensions(prev => {
@@ -162,16 +182,17 @@ export const ScrubberBeaconLabelGroup = /*#__PURE__*/memo(_ref2 => {
162
182
  });
163
183
  }, [seriesInfo, dataIndex, dataX, xScale, labelDimensions, drawingArea, labelMinGap]);
164
184
  const currentPosition = useMemo(() => {
165
- if (!xScale || dataX === undefined) return 'right';
185
+ if (!xScale || dataX === undefined) return labelPreferredSide;
166
186
  const pixelX = getPointOnScale(dataX, xScale);
167
187
  const maxWidth = Math.max(...Object.values(labelDimensions).map(dim => dim.width));
168
- return getLabelPosition(pixelX, maxWidth, drawingArea, labelHorizontalOffset);
169
- }, [dataX, xScale, labelDimensions, drawingArea, labelHorizontalOffset]);
188
+ return getLabelPosition(pixelX, maxWidth, drawingArea, labelHorizontalOffset, labelPreferredSide);
189
+ }, [dataX, xScale, labelDimensions, drawingArea, labelHorizontalOffset, labelPreferredSide]);
170
190
  return seriesInfo.map((info, index) => {
171
191
  const labelInfo = labels.find(label => label.seriesId === info.seriesId);
172
192
  if (!labelInfo) return;
173
193
  return /*#__PURE__*/_jsx(PositionedLabel, {
174
194
  BeaconLabelComponent: BeaconLabelComponent,
195
+ className: className,
175
196
  color: labelInfo.color,
176
197
  index: index,
177
198
  label: labelInfo.label,
@@ -180,7 +201,9 @@ export const ScrubberBeaconLabelGroup = /*#__PURE__*/memo(_ref2 => {
180
201
  onDimensionsChange: handleDimensionsChange,
181
202
  position: currentPosition,
182
203
  positions: allLabelPositions,
183
- seriesId: info.seriesId
204
+ seriesId: info.seriesId,
205
+ style: style,
206
+ updateTransition: updateTransition
184
207
  }, info.seriesId);
185
208
  });
186
209
  });
@@ -18,25 +18,29 @@ export const ScrubberProvider = _ref => {
18
18
  throw new Error('ScrubberProvider must be used within a ChartContext');
19
19
  }
20
20
  const {
21
+ layout,
21
22
  getXScale,
23
+ getYScale,
22
24
  getXAxis,
25
+ getYAxis,
23
26
  series
24
27
  } = chartContext;
25
28
  const [scrubberPosition, setScrubberPosition] = useState(undefined);
26
- const getDataIndexFromX = useCallback(mouseX => {
27
- const xScale = getXScale();
28
- const xAxis = getXAxis();
29
- if (!xScale || !xAxis) return 0;
30
- if (isCategoricalScale(xScale)) {
31
- var _ref2, _xScale$domain, _xScale$domain2, _xScale$bandwidth, _xScale$bandwidth2;
32
- const categories = (_ref2 = (_xScale$domain = (_xScale$domain2 = xScale.domain) === null || _xScale$domain2 === void 0 ? void 0 : _xScale$domain2.call(xScale)) !== null && _xScale$domain !== void 0 ? _xScale$domain : xAxis.data) !== null && _ref2 !== void 0 ? _ref2 : [];
33
- const bandwidth = (_xScale$bandwidth = (_xScale$bandwidth2 = xScale.bandwidth) === null || _xScale$bandwidth2 === void 0 ? void 0 : _xScale$bandwidth2.call(xScale)) !== null && _xScale$bandwidth !== void 0 ? _xScale$bandwidth : 0;
29
+ const getDataIndexFromPosition = useCallback(mousePosition => {
30
+ const categoryAxisIsX = layout !== 'horizontal';
31
+ const categoryScale = categoryAxisIsX ? getXScale() : getYScale();
32
+ const categoryAxis = categoryAxisIsX ? getXAxis() : getYAxis();
33
+ if (!categoryScale || !categoryAxis) return 0;
34
+ if (isCategoricalScale(categoryScale)) {
35
+ var _ref2, _categoryScale$domain, _categoryScale$domain2, _categoryScale$bandwi, _categoryScale$bandwi2;
36
+ const categories = (_ref2 = (_categoryScale$domain = (_categoryScale$domain2 = categoryScale.domain) === null || _categoryScale$domain2 === void 0 ? void 0 : _categoryScale$domain2.call(categoryScale)) !== null && _categoryScale$domain !== void 0 ? _categoryScale$domain : categoryAxis.data) !== null && _ref2 !== void 0 ? _ref2 : [];
37
+ const bandwidth = (_categoryScale$bandwi = (_categoryScale$bandwi2 = categoryScale.bandwidth) === null || _categoryScale$bandwi2 === void 0 ? void 0 : _categoryScale$bandwi2.call(categoryScale)) !== null && _categoryScale$bandwi !== void 0 ? _categoryScale$bandwi : 0;
34
38
  let closestIndex = 0;
35
39
  let closestDistance = Infinity;
36
40
  for (let i = 0; i < categories.length; i++) {
37
- const xPos = xScale(i);
38
- if (xPos !== undefined) {
39
- const distance = Math.abs(mouseX - (xPos + bandwidth / 2));
41
+ const pos = categoryScale(i);
42
+ if (pos !== undefined) {
43
+ const distance = Math.abs(mousePosition - (pos + bandwidth / 2));
40
44
  if (distance < closestDistance) {
41
45
  closestDistance = distance;
42
46
  closestIndex = i;
@@ -46,17 +50,17 @@ export const ScrubberProvider = _ref => {
46
50
  return closestIndex;
47
51
  } else {
48
52
  // For numeric scales with axis data, find the nearest data point
49
- const axisData = xAxis.data;
53
+ const axisData = categoryAxis.data;
50
54
  if (axisData && Array.isArray(axisData) && typeof axisData[0] === 'number') {
51
55
  // We have numeric axis data - find the closest data point
52
56
  const numericData = axisData;
53
57
  let closestIndex = 0;
54
58
  let closestDistance = Infinity;
55
59
  for (let i = 0; i < numericData.length; i++) {
56
- const xValue = numericData[i];
57
- const xPos = xScale(xValue);
58
- if (xPos !== undefined) {
59
- const distance = Math.abs(mouseX - xPos);
60
+ const dataValue = numericData[i];
61
+ const pos = categoryScale(dataValue);
62
+ if (pos !== undefined) {
63
+ const distance = Math.abs(mousePosition - pos);
60
64
  if (distance < closestDistance) {
61
65
  closestDistance = distance;
62
66
  closestIndex = i;
@@ -66,26 +70,26 @@ export const ScrubberProvider = _ref => {
66
70
  return closestIndex;
67
71
  } else {
68
72
  var _domain$min, _domain$max;
69
- const xValue = xScale.invert(mouseX);
70
- const dataIndex = Math.round(xValue);
71
- const domain = xAxis.domain;
72
- return Math.max((_domain$min = domain.min) !== null && _domain$min !== void 0 ? _domain$min : 0, Math.min(dataIndex, (_domain$max = domain.max) !== null && _domain$max !== void 0 ? _domain$max : 0));
73
+ const dataValue = categoryScale.invert(mousePosition);
74
+ const dataIndexVal = Math.round(dataValue);
75
+ const domain = categoryAxis.domain;
76
+ return Math.max((_domain$min = domain.min) !== null && _domain$min !== void 0 ? _domain$min : 0, Math.min(dataIndexVal, (_domain$max = domain.max) !== null && _domain$max !== void 0 ? _domain$max : 0));
73
77
  }
74
78
  }
75
- }, [getXScale, getXAxis]);
76
- const handlePointerMove = useCallback((clientX, target) => {
79
+ }, [layout, getXScale, getYScale, getXAxis, getYAxis]);
80
+ const handlePointerMove = useCallback((clientX, clientY, target) => {
77
81
  if (!enableScrubbing || !series || series.length === 0) return;
78
82
  const rect = target.getBoundingClientRect();
79
- const x = clientX - rect.left;
80
- const dataIndex = getDataIndexFromX(x);
83
+ const position = layout === 'horizontal' ? clientY - rect.top : clientX - rect.left;
84
+ const dataIndex = getDataIndexFromPosition(position);
81
85
  if (dataIndex !== scrubberPosition) {
82
86
  setScrubberPosition(dataIndex);
83
87
  onScrubberPositionChange === null || onScrubberPositionChange === void 0 || onScrubberPositionChange(dataIndex);
84
88
  }
85
- }, [enableScrubbing, series, getDataIndexFromX, scrubberPosition, onScrubberPositionChange]);
89
+ }, [enableScrubbing, series, layout, getDataIndexFromPosition, scrubberPosition, onScrubberPositionChange]);
86
90
  const handleMouseMove = useCallback(event => {
87
91
  const target = event.currentTarget;
88
- handlePointerMove(event.clientX, target);
92
+ handlePointerMove(event.clientX, event.clientY, target);
89
93
  }, [handlePointerMove]);
90
94
  const handleTouchMove = useCallback(event => {
91
95
  if (!event.touches.length) return;
@@ -93,14 +97,14 @@ export const ScrubberProvider = _ref => {
93
97
  event.preventDefault();
94
98
  const touch = event.touches[0];
95
99
  const target = event.currentTarget;
96
- handlePointerMove(touch.clientX, target);
100
+ handlePointerMove(touch.clientX, touch.clientY, target);
97
101
  }, [handlePointerMove]);
98
102
  const handleTouchStart = useCallback(event => {
99
103
  if (!enableScrubbing || !event.touches.length) return;
100
104
  // Handle initial touch
101
105
  const touch = event.touches[0];
102
106
  const target = event.currentTarget;
103
- handlePointerMove(touch.clientX, target);
107
+ handlePointerMove(touch.clientX, touch.clientY, target);
104
108
  }, [enableScrubbing, handlePointerMove]);
105
109
  const handlePointerLeave = useCallback(() => {
106
110
  if (!enableScrubbing) return;
@@ -111,25 +115,26 @@ export const ScrubberProvider = _ref => {
111
115
  const handleTouchEnd = handlePointerLeave;
112
116
  const handleKeyDown = useCallback(event => {
113
117
  if (!enableScrubbing) return;
114
- const xScale = getXScale();
115
- const xAxis = getXAxis();
116
- if (!xScale || !xAxis) return;
117
- const isBand = isCategoricalScale(xScale);
118
+ const categoryAxisIsX = layout !== 'horizontal';
119
+ const categoryScale = categoryAxisIsX ? getXScale() : getYScale();
120
+ const categoryAxis = categoryAxisIsX ? getXAxis() : getYAxis();
121
+ if (!categoryScale || !categoryAxis) return;
122
+ const isBand = isCategoricalScale(categoryScale);
118
123
 
119
124
  // Determine the actual data indices we can navigate to
120
125
  let minIndex;
121
126
  let maxIndex;
122
127
  let dataPoints;
123
128
  if (isBand) {
124
- var _ref3, _xScale$domain3, _xScale$domain4;
129
+ var _ref3, _categoryScale$domain3, _categoryScale$domain4;
125
130
  // For categorical scales, use the categories
126
- const categories = (_ref3 = (_xScale$domain3 = (_xScale$domain4 = xScale.domain) === null || _xScale$domain4 === void 0 ? void 0 : _xScale$domain4.call(xScale)) !== null && _xScale$domain3 !== void 0 ? _xScale$domain3 : xAxis.data) !== null && _ref3 !== void 0 ? _ref3 : [];
131
+ const categories = (_ref3 = (_categoryScale$domain3 = (_categoryScale$domain4 = categoryScale.domain) === null || _categoryScale$domain4 === void 0 ? void 0 : _categoryScale$domain4.call(categoryScale)) !== null && _categoryScale$domain3 !== void 0 ? _categoryScale$domain3 : categoryAxis.data) !== null && _ref3 !== void 0 ? _ref3 : [];
127
132
  minIndex = 0;
128
133
  maxIndex = Math.max(0, categories.length - 1);
129
134
  dataPoints = categories.length;
130
135
  } else {
131
136
  // For numeric scales, check if we have specific data points
132
- const axisData = xAxis.data;
137
+ const axisData = categoryAxis.data;
133
138
  if (axisData && Array.isArray(axisData)) {
134
139
  // We have specific data points - use their indices
135
140
  minIndex = 0;
@@ -138,7 +143,7 @@ export const ScrubberProvider = _ref => {
138
143
  } else {
139
144
  var _domain$min2, _domain$max2;
140
145
  // Fall back to domain-based navigation for continuous scales without specific data
141
- const domain = xAxis.domain;
146
+ const domain = categoryAxis.domain;
142
147
  minIndex = (_domain$min2 = domain.min) !== null && _domain$min2 !== void 0 ? _domain$min2 : 0;
143
148
  maxIndex = (_domain$max2 = domain.max) !== null && _domain$max2 !== void 0 ? _domain$max2 : 0;
144
149
  dataPoints = maxIndex - minIndex + 1;
@@ -152,11 +157,11 @@ export const ScrubberProvider = _ref => {
152
157
  const stepSize = multiSkip ? Math.min(10, Math.max(1, Math.floor(dataRange * 0.1))) : 1;
153
158
  let newIndex;
154
159
  switch (event.key) {
155
- case 'ArrowLeft':
160
+ case categoryAxisIsX ? 'ArrowLeft' : 'ArrowUp':
156
161
  event.preventDefault();
157
162
  newIndex = Math.max(minIndex, currentIndex - stepSize);
158
163
  break;
159
- case 'ArrowRight':
164
+ case categoryAxisIsX ? 'ArrowRight' : 'ArrowDown':
160
165
  event.preventDefault();
161
166
  newIndex = Math.min(maxIndex, currentIndex + stepSize);
162
167
  break;
@@ -180,7 +185,7 @@ export const ScrubberProvider = _ref => {
180
185
  setScrubberPosition(newIndex);
181
186
  onScrubberPositionChange === null || onScrubberPositionChange === void 0 || onScrubberPositionChange(newIndex);
182
187
  }
183
- }, [enableScrubbing, getXScale, getXAxis, scrubberPosition, onScrubberPositionChange]);
188
+ }, [enableScrubbing, layout, getXScale, getYScale, getXAxis, getYAxis, scrubberPosition, onScrubberPositionChange]);
184
189
  const handleBlur = useCallback(() => {
185
190
  if (!enableScrubbing || scrubberPosition === undefined) return;
186
191
  setScrubberPosition(undefined);
@@ -10,6 +10,7 @@ import { Text } from '@coinbase/cds-web/typography';
10
10
  import { m as motion } from 'framer-motion';
11
11
  import { useCartesianChartContext } from '../ChartProvider';
12
12
  import { getChartInset } from '../utils';
13
+ import { accessoryFadeTransitionDuration } from '../utils/transition';
13
14
 
14
15
  /**
15
16
  * The supported content types for ChartText.
@@ -69,7 +70,7 @@ export const ChartText = /*#__PURE__*/memo(_ref => {
69
70
  fontWeight,
70
71
  elevated,
71
72
  color = 'var(--color-fgMuted)',
72
- background = elevated ? 'var(--color-bg)' : 'transparent',
73
+ background = elevated ? 'var(--color-bgElevation1)' : 'transparent',
73
74
  borderRadius,
74
75
  inset: insetInput,
75
76
  onDimensionsChange,
@@ -190,7 +191,7 @@ export const ChartText = /*#__PURE__*/memo(_ref => {
190
191
  opacity: isDimensionsReady ? 1 : 0
191
192
  },
192
193
  transition: animate ? {
193
- duration: 0.2,
194
+ duration: accessoryFadeTransitionDuration,
194
195
  ease: 'easeOut'
195
196
  } : undefined,
196
197
  children: [/*#__PURE__*/_jsx(Box, {