@coinbase/cds-web-visualization 3.4.0-beta.1 → 3.4.0-beta.11

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 (175) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dts/chart/CartesianChart.d.ts +38 -2
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/Path.d.ts +27 -7
  5. package/dts/chart/Path.d.ts.map +1 -1
  6. package/dts/chart/PeriodSelector.d.ts +0 -4
  7. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  8. package/dts/chart/area/Area.d.ts +54 -24
  9. package/dts/chart/area/Area.d.ts.map +1 -1
  10. package/dts/chart/area/AreaChart.d.ts +33 -6
  11. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  12. package/dts/chart/area/DottedArea.d.ts +21 -44
  13. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  14. package/dts/chart/area/GradientArea.d.ts +21 -12
  15. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  16. package/dts/chart/area/SolidArea.d.ts +16 -1
  17. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  18. package/dts/chart/axis/Axis.d.ts +109 -63
  19. package/dts/chart/axis/Axis.d.ts.map +1 -1
  20. package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
  21. package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
  22. package/dts/chart/axis/XAxis.d.ts +1 -1
  23. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  24. package/dts/chart/axis/YAxis.d.ts +2 -2
  25. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  26. package/dts/chart/axis/index.d.ts +1 -0
  27. package/dts/chart/axis/index.d.ts.map +1 -1
  28. package/dts/chart/bar/Bar.d.ts +16 -13
  29. package/dts/chart/bar/Bar.d.ts.map +1 -1
  30. package/dts/chart/bar/BarChart.d.ts +17 -8
  31. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  32. package/dts/chart/bar/BarPlot.d.ts +2 -1
  33. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  34. package/dts/chart/bar/BarStack.d.ts +40 -48
  35. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  36. package/dts/chart/bar/BarStackGroup.d.ts +1 -0
  37. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  38. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  39. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  40. package/dts/chart/gradient/Gradient.d.ts +35 -0
  41. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  42. package/dts/chart/gradient/index.d.ts +2 -0
  43. package/dts/chart/gradient/index.d.ts.map +1 -0
  44. package/dts/chart/index.d.ts +2 -1
  45. package/dts/chart/index.d.ts.map +1 -1
  46. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  47. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  48. package/dts/chart/line/DottedLine.d.ts +15 -3
  49. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  50. package/dts/chart/line/Line.d.ts +87 -28
  51. package/dts/chart/line/Line.d.ts.map +1 -1
  52. package/dts/chart/line/LineChart.d.ts +26 -8
  53. package/dts/chart/line/LineChart.d.ts.map +1 -1
  54. package/dts/chart/line/ReferenceLine.d.ts +91 -44
  55. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  56. package/dts/chart/line/SolidLine.d.ts +14 -3
  57. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  58. package/dts/chart/line/index.d.ts +1 -1
  59. package/dts/chart/line/index.d.ts.map +1 -1
  60. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  61. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  62. package/dts/chart/point/Point.d.ts +201 -0
  63. package/dts/chart/point/Point.d.ts.map +1 -0
  64. package/dts/chart/point/index.d.ts +3 -0
  65. package/dts/chart/point/index.d.ts.map +1 -0
  66. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +24 -0
  67. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  68. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  69. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  70. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +10 -0
  71. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  72. package/dts/chart/scrubber/Scrubber.d.ts +207 -66
  73. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  74. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +70 -0
  75. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  76. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +32 -0
  77. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  78. package/dts/chart/scrubber/index.d.ts +3 -0
  79. package/dts/chart/scrubber/index.d.ts.map +1 -1
  80. package/dts/chart/text/ChartText.d.ts +46 -43
  81. package/dts/chart/text/ChartText.d.ts.map +1 -1
  82. package/dts/chart/text/{SmartChartTextGroup.d.ts → ChartTextGroup.d.ts} +9 -3
  83. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  84. package/dts/chart/text/index.d.ts +1 -1
  85. package/dts/chart/text/index.d.ts.map +1 -1
  86. package/dts/chart/utils/axis.d.ts +25 -1
  87. package/dts/chart/utils/axis.d.ts.map +1 -1
  88. package/dts/chart/utils/chart.d.ts +27 -7
  89. package/dts/chart/utils/chart.d.ts.map +1 -1
  90. package/dts/chart/utils/context.d.ts +6 -0
  91. package/dts/chart/utils/context.d.ts.map +1 -1
  92. package/dts/chart/utils/gradient.d.ts +104 -0
  93. package/dts/chart/utils/gradient.d.ts.map +1 -0
  94. package/dts/chart/utils/index.d.ts +4 -0
  95. package/dts/chart/utils/index.d.ts.map +1 -1
  96. package/dts/chart/utils/interpolate.d.ts +112 -0
  97. package/dts/chart/utils/interpolate.d.ts.map +1 -0
  98. package/dts/chart/utils/path.d.ts +24 -1
  99. package/dts/chart/utils/path.d.ts.map +1 -1
  100. package/dts/chart/utils/point.d.ts +40 -7
  101. package/dts/chart/utils/point.d.ts.map +1 -1
  102. package/dts/chart/utils/scale.d.ts +11 -0
  103. package/dts/chart/utils/scale.d.ts.map +1 -1
  104. package/dts/chart/utils/scrubber.d.ts +39 -0
  105. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  106. package/dts/chart/utils/transition.d.ts +65 -0
  107. package/dts/chart/utils/transition.d.ts.map +1 -0
  108. package/dts/sparkline/sparkline-interactive/SparklineInteractiveMarkerDates.d.ts.map +1 -1
  109. package/esm/chart/CartesianChart.js +140 -85
  110. package/esm/chart/Path.js +53 -47
  111. package/esm/chart/PeriodSelector.js +4 -18
  112. package/esm/chart/area/Area.js +24 -34
  113. package/esm/chart/area/AreaChart.js +24 -15
  114. package/esm/chart/area/DottedArea.js +35 -89
  115. package/esm/chart/area/GradientArea.js +34 -80
  116. package/esm/chart/area/SolidArea.js +29 -11
  117. package/esm/chart/axis/Axis.js +4 -39
  118. package/esm/chart/axis/DefaultAxisTickLabel.js +15 -0
  119. package/esm/chart/axis/XAxis.js +184 -63
  120. package/esm/chart/axis/YAxis.js +190 -57
  121. package/esm/chart/axis/index.js +1 -0
  122. package/esm/chart/bar/Bar.js +3 -1
  123. package/esm/chart/bar/BarChart.js +15 -32
  124. package/esm/chart/bar/BarPlot.js +3 -2
  125. package/esm/chart/bar/BarStack.js +65 -23
  126. package/esm/chart/bar/BarStackGroup.js +7 -17
  127. package/esm/chart/bar/DefaultBar.js +4 -7
  128. package/esm/chart/bar/DefaultBarStack.js +5 -7
  129. package/esm/chart/gradient/Gradient.js +104 -0
  130. package/esm/chart/gradient/index.js +1 -0
  131. package/esm/chart/index.js +2 -1
  132. package/esm/chart/line/DefaultReferenceLineLabel.js +81 -0
  133. package/esm/chart/line/DottedLine.js +38 -17
  134. package/esm/chart/line/Line.js +96 -70
  135. package/esm/chart/line/LineChart.js +18 -6
  136. package/esm/chart/line/ReferenceLine.js +41 -43
  137. package/esm/chart/line/SolidLine.js +36 -15
  138. package/esm/chart/line/index.js +1 -1
  139. package/esm/chart/{line/GradientLine.js → point/DefaultPointLabel.js} +31 -45
  140. package/esm/chart/point/Point.css +2 -0
  141. package/esm/chart/{Point.js → point/Point.js} +66 -57
  142. package/esm/chart/point/index.js +2 -0
  143. package/esm/chart/scrubber/DefaultScrubberBeacon.js +155 -0
  144. package/esm/chart/scrubber/{ScrubberBeaconLabel.js → DefaultScrubberBeaconLabel.js} +23 -10
  145. package/esm/chart/scrubber/DefaultScrubberLabel.js +30 -0
  146. package/esm/chart/scrubber/Scrubber.js +98 -392
  147. package/esm/chart/scrubber/ScrubberBeaconGroup.js +166 -0
  148. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +186 -0
  149. package/esm/chart/scrubber/index.js +3 -1
  150. package/esm/chart/text/ChartText.js +15 -20
  151. package/esm/chart/text/{SmartChartTextGroup.js → ChartTextGroup.js} +4 -3
  152. package/esm/chart/text/index.js +1 -1
  153. package/esm/chart/utils/axis.js +45 -29
  154. package/esm/chart/utils/chart.js +29 -3
  155. package/esm/chart/utils/gradient.js +257 -0
  156. package/esm/chart/utils/index.js +4 -0
  157. package/esm/chart/utils/interpolate.js +644 -0
  158. package/esm/chart/utils/path.js +32 -9
  159. package/esm/chart/utils/point.js +99 -12
  160. package/esm/chart/utils/scale.js +13 -2
  161. package/esm/chart/utils/scrubber.js +132 -0
  162. package/esm/chart/utils/transition.js +111 -0
  163. package/esm/sparkline/sparkline-interactive/SparklineInteractiveMarkerDates.js +8 -4
  164. package/package.json +9 -9
  165. package/dts/chart/Point.d.ts +0 -153
  166. package/dts/chart/Point.d.ts.map +0 -1
  167. package/dts/chart/line/GradientLine.d.ts +0 -42
  168. package/dts/chart/line/GradientLine.d.ts.map +0 -1
  169. package/dts/chart/scrubber/ScrubberBeacon.d.ts +0 -93
  170. package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +0 -1
  171. package/dts/chart/scrubber/ScrubberBeaconLabel.d.ts +0 -7
  172. package/dts/chart/scrubber/ScrubberBeaconLabel.d.ts.map +0 -1
  173. package/dts/chart/text/SmartChartTextGroup.d.ts.map +0 -1
  174. package/esm/chart/Point.css +0 -2
  175. package/esm/chart/scrubber/ScrubberBeacon.js +0 -195
@@ -0,0 +1,166 @@
1
+ import { forwardRef, memo, useCallback, useImperativeHandle, useMemo } from 'react';
2
+ import { useRefMap } from '@coinbase/cds-common/hooks/useRefMap';
3
+ import { useCartesianChartContext } from '../ChartProvider';
4
+ import { evaluateGradientAtValue, getGradientConfig, useScrubberContext } from '../utils';
5
+ import { DefaultScrubberBeacon } from './DefaultScrubberBeacon';
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ // Helper component to calculate beacon data for a specific series
8
+ const BeaconWithData = /*#__PURE__*/memo(_ref => {
9
+ let {
10
+ seriesId,
11
+ dataIndex,
12
+ dataX,
13
+ isIdle,
14
+ BeaconComponent,
15
+ idlePulse,
16
+ transitions,
17
+ className,
18
+ style,
19
+ testID,
20
+ beaconRef
21
+ } = _ref;
22
+ const {
23
+ getSeries,
24
+ getSeriesData,
25
+ getXScale,
26
+ getYScale
27
+ } = useCartesianChartContext();
28
+ const series = useMemo(() => getSeries(seriesId), [getSeries, seriesId]);
29
+ const sourceData = useMemo(() => getSeriesData(seriesId), [getSeriesData, seriesId]);
30
+ const gradient = series === null || series === void 0 ? void 0 : series.gradient;
31
+
32
+ // Get dataY from series data
33
+ const dataY = useMemo(() => {
34
+ 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
+ if (validValues.length >= 1) {
41
+ return validValues[validValues.length - 1];
42
+ }
43
+ }
44
+ }
45
+ return undefined;
46
+ }, [sourceData, dataIndex]);
47
+
48
+ // Evaluate gradient color
49
+ const color = useMemo(() => {
50
+ 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)';
52
+ if (gradient) {
53
+ const xScale = getXScale();
54
+ const yScale = getYScale(series === null || series === void 0 ? void 0 : series.yAxisId);
55
+ if (xScale && yScale) {
56
+ const gradientScale = gradient.axis === 'x' ? xScale : yScale;
57
+ const stops = getGradientConfig(gradient, xScale, yScale);
58
+ if (stops) {
59
+ var _gradient$axis;
60
+ 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);
63
+ if (evaluatedColor) {
64
+ return evaluatedColor;
65
+ }
66
+ }
67
+ }
68
+ }
69
+ 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;
72
+ return /*#__PURE__*/_jsx(BeaconComponent, {
73
+ ref: beaconRef,
74
+ className: className,
75
+ color: color,
76
+ dataX: dataX,
77
+ dataY: dataY,
78
+ idlePulse: idlePulse,
79
+ isIdle: isIdle,
80
+ seriesId: seriesId,
81
+ style: style,
82
+ testID: testID,
83
+ transitions: transitions
84
+ });
85
+ });
86
+ export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref2, ref) => {
87
+ let {
88
+ seriesIds,
89
+ idlePulse,
90
+ transitions,
91
+ BeaconComponent = DefaultScrubberBeacon,
92
+ className,
93
+ style,
94
+ testID
95
+ } = _ref2;
96
+ const ScrubberBeaconRefs = useRefMap();
97
+ const {
98
+ scrubberPosition
99
+ } = useScrubberContext();
100
+ const {
101
+ getXScale,
102
+ getXAxis,
103
+ dataLength,
104
+ series
105
+ } = useCartesianChartContext();
106
+
107
+ // Expose imperative handle with pulse method
108
+ useImperativeHandle(ref, () => ({
109
+ pulse: () => {
110
+ Object.values(ScrubberBeaconRefs.refs).forEach(beaconRef => {
111
+ beaconRef === null || beaconRef === void 0 || beaconRef.pulse();
112
+ });
113
+ }
114
+ }));
115
+ const filteredSeries = useMemo(() => {
116
+ var _series$filter;
117
+ 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
+ }, [series, seriesIds]);
119
+ const {
120
+ dataX,
121
+ dataIndex
122
+ } = useMemo(() => {
123
+ const xScale = getXScale();
124
+ const xAxis = getXAxis();
125
+ if (!xScale) return {
126
+ dataX: undefined,
127
+ dataIndex: undefined
128
+ };
129
+ const dataIndex = scrubberPosition !== null && scrubberPosition !== void 0 ? scrubberPosition : Math.max(0, dataLength - 1);
130
+
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;
136
+ } else {
137
+ dataX = dataIndex;
138
+ }
139
+ return {
140
+ dataX,
141
+ dataIndex
142
+ };
143
+ }, [getXScale, getXAxis, scrubberPosition, dataLength]);
144
+ const isIdle = scrubberPosition === undefined;
145
+ const createBeaconRef = useCallback(seriesId => {
146
+ return beaconRef => {
147
+ if (beaconRef) {
148
+ ScrubberBeaconRefs.registerRef(seriesId, beaconRef);
149
+ }
150
+ };
151
+ }, [ScrubberBeaconRefs]);
152
+ if (dataX === undefined || dataIndex === undefined) return null;
153
+ return filteredSeries.map(s => /*#__PURE__*/_jsx(BeaconWithData, {
154
+ BeaconComponent: BeaconComponent,
155
+ beaconRef: createBeaconRef(s.id),
156
+ className: className,
157
+ dataIndex: dataIndex,
158
+ dataX: dataX,
159
+ idlePulse: idlePulse,
160
+ isIdle: isIdle,
161
+ seriesId: s.id,
162
+ style: style,
163
+ testID: testID ? "".concat(testID !== null && testID !== void 0 ? testID : 'beacon', "-").concat(s.id) : undefined,
164
+ transitions: transitions
165
+ }, s.id));
166
+ }));
@@ -0,0 +1,186 @@
1
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
4
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
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
+ import { memo, useCallback, useMemo, useState } from 'react';
7
+ import { useCartesianChartContext } from '../ChartProvider';
8
+ import { getPointOnScale, useScrubberContext } from '../utils';
9
+ import { calculateLabelYPositions, getLabelPosition } from '../utils/scrubber';
10
+ import { DefaultScrubberBeaconLabel } from './DefaultScrubberBeaconLabel';
11
+ import { jsx as _jsx } from "react/jsx-runtime";
12
+ const PositionedLabel = /*#__PURE__*/memo(_ref => {
13
+ let {
14
+ index,
15
+ positions,
16
+ position,
17
+ label,
18
+ color,
19
+ seriesId,
20
+ onDimensionsChange,
21
+ BeaconLabelComponent,
22
+ labelHorizontalOffset,
23
+ labelFont
24
+ } = _ref;
25
+ const pos = positions[index];
26
+
27
+ // Don't render if position is null (invalid data)
28
+ if (!pos) {
29
+ return null;
30
+ }
31
+ const x = pos.x;
32
+ const y = pos.y;
33
+ const dx = position === 'right' ? labelHorizontalOffset : -labelHorizontalOffset;
34
+ const horizontalAlignment = position === 'right' ? 'left' : 'right';
35
+ return /*#__PURE__*/_jsx(BeaconLabelComponent, {
36
+ color: color,
37
+ dx: dx,
38
+ font: labelFont,
39
+ horizontalAlignment: horizontalAlignment,
40
+ label: label,
41
+ onDimensionsChange: d => onDimensionsChange(seriesId, d),
42
+ seriesId: seriesId,
43
+ x: x,
44
+ y: y
45
+ });
46
+ });
47
+ export const ScrubberBeaconLabelGroup = /*#__PURE__*/memo(_ref2 => {
48
+ let {
49
+ labels,
50
+ labelMinGap = 4,
51
+ labelHorizontalOffset = 16,
52
+ labelFont,
53
+ BeaconLabelComponent = DefaultScrubberBeaconLabel
54
+ } = _ref2;
55
+ const {
56
+ getSeries,
57
+ getSeriesData,
58
+ getXScale,
59
+ getYScale,
60
+ getXAxis,
61
+ drawingArea,
62
+ dataLength
63
+ } = useCartesianChartContext();
64
+ const {
65
+ scrubberPosition
66
+ } = useScrubberContext();
67
+ const [labelDimensions, setLabelDimensions] = useState({});
68
+ const handleDimensionsChange = useCallback((seriesId, dimensions) => {
69
+ setLabelDimensions(prev => {
70
+ const existing = prev[seriesId];
71
+ if (existing && existing.width === dimensions.width && existing.height === dimensions.height) {
72
+ return prev;
73
+ }
74
+ return _objectSpread(_objectSpread({}, prev), {}, {
75
+ [seriesId]: dimensions
76
+ });
77
+ });
78
+ }, []);
79
+ const seriesInfo = useMemo(() => {
80
+ return labels.map(label => {
81
+ const series = getSeries(label.seriesId);
82
+ if (!series) return null;
83
+ const sourceData = getSeriesData(label.seriesId);
84
+ const yScale = getYScale(series.yAxisId);
85
+ return {
86
+ seriesId: label.seriesId,
87
+ sourceData,
88
+ yScale
89
+ };
90
+ }).filter(info => info !== null);
91
+ }, [labels, getSeries, getSeriesData, getYScale]);
92
+ const xScale = getXScale();
93
+ const xAxis = getXAxis();
94
+ const dataIndex = useMemo(() => {
95
+ return scrubberPosition !== null && scrubberPosition !== void 0 ? scrubberPosition : Math.max(0, dataLength - 1);
96
+ }, [scrubberPosition, dataLength]);
97
+ const dataX = useMemo(() => {
98
+ if (xAxis !== null && xAxis !== void 0 && xAxis.data && Array.isArray(xAxis.data) && xAxis.data[dataIndex] !== undefined) {
99
+ const dataValue = xAxis.data[dataIndex];
100
+ return typeof dataValue === 'string' ? dataIndex : dataValue;
101
+ }
102
+ return dataIndex;
103
+ }, [xAxis, dataIndex]);
104
+ const allLabelPositions = useMemo(() => {
105
+ if (!xScale || dataX === undefined) return [];
106
+ const sharedPixelX = getPointOnScale(dataX, xScale);
107
+ const desiredPositions = seriesInfo.map(info => {
108
+ let dataY;
109
+ if (info.yScale) {
110
+ if (info.sourceData && dataIndex !== undefined && dataIndex >= 0 && dataIndex < info.sourceData.length) {
111
+ const dataValue = info.sourceData[dataIndex];
112
+ if (Array.isArray(dataValue)) {
113
+ const validValues = dataValue.filter(val => val !== null);
114
+ if (validValues.length >= 1) {
115
+ dataY = validValues[validValues.length - 1];
116
+ }
117
+ }
118
+ }
119
+ }
120
+ if (dataY !== undefined && info.yScale) {
121
+ return {
122
+ seriesId: info.seriesId,
123
+ x: sharedPixelX,
124
+ desiredY: getPointOnScale(dataY, info.yScale)
125
+ };
126
+ }
127
+
128
+ // Return null for invalid data
129
+ return null;
130
+ });
131
+ const maxLabelHeight = Math.max(...Object.values(labelDimensions).map(dim => dim.height));
132
+ const maxLabelWidth = Math.max(...Object.values(labelDimensions).map(dim => dim.width));
133
+
134
+ // Only apply collision detection to valid positions
135
+ const validPositions = desiredPositions.filter(pos => pos !== null);
136
+
137
+ // Convert to LabelDimension format expected by utility
138
+ const dimensions = validPositions.map(pos => {
139
+ var _trackedDimensions$wi, _trackedDimensions$he;
140
+ const trackedDimensions = labelDimensions[pos.seriesId];
141
+ return {
142
+ seriesId: pos.seriesId,
143
+ width: (_trackedDimensions$wi = trackedDimensions === null || trackedDimensions === void 0 ? void 0 : trackedDimensions.width) !== null && _trackedDimensions$wi !== void 0 ? _trackedDimensions$wi : maxLabelWidth,
144
+ height: (_trackedDimensions$he = trackedDimensions === null || trackedDimensions === void 0 ? void 0 : trackedDimensions.height) !== null && _trackedDimensions$he !== void 0 ? _trackedDimensions$he : maxLabelHeight,
145
+ preferredX: pos.x,
146
+ preferredY: pos.desiredY
147
+ };
148
+ });
149
+
150
+ // Calculate Y positions with collision resolution for valid positions only
151
+ const yPositions = calculateLabelYPositions(dimensions, drawingArea, maxLabelHeight, labelMinGap);
152
+
153
+ // Return all positions (including null ones)
154
+ return desiredPositions.map(pos => {
155
+ var _yPositions$get;
156
+ if (!pos) return null;
157
+ return {
158
+ seriesId: pos.seriesId,
159
+ x: pos.x,
160
+ y: (_yPositions$get = yPositions.get(pos.seriesId)) !== null && _yPositions$get !== void 0 ? _yPositions$get : pos.desiredY
161
+ };
162
+ });
163
+ }, [seriesInfo, dataIndex, dataX, xScale, labelDimensions, drawingArea, labelMinGap]);
164
+ const currentPosition = useMemo(() => {
165
+ if (!xScale || dataX === undefined) return 'right';
166
+ const pixelX = getPointOnScale(dataX, xScale);
167
+ const maxWidth = Math.max(...Object.values(labelDimensions).map(dim => dim.width));
168
+ return getLabelPosition(pixelX, maxWidth, drawingArea, labelHorizontalOffset);
169
+ }, [dataX, xScale, labelDimensions, drawingArea, labelHorizontalOffset]);
170
+ return seriesInfo.map((info, index) => {
171
+ const labelInfo = labels.find(label => label.seriesId === info.seriesId);
172
+ if (!labelInfo) return;
173
+ return /*#__PURE__*/_jsx(PositionedLabel, {
174
+ BeaconLabelComponent: BeaconLabelComponent,
175
+ color: labelInfo.color,
176
+ index: index,
177
+ label: labelInfo.label,
178
+ labelFont: labelFont,
179
+ labelHorizontalOffset: labelHorizontalOffset,
180
+ onDimensionsChange: handleDimensionsChange,
181
+ position: currentPosition,
182
+ positions: allLabelPositions,
183
+ seriesId: info.seriesId
184
+ }, info.seriesId);
185
+ });
186
+ });
@@ -1,2 +1,4 @@
1
- // Only export Scrubber component
1
+ export * from './DefaultScrubberBeacon';
2
+ export * from './DefaultScrubberBeaconLabel';
3
+ export * from './DefaultScrubberLabel';
2
4
  export * from './Scrubber';
@@ -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.
@@ -67,9 +68,9 @@ export const ChartText = /*#__PURE__*/memo(_ref => {
67
68
  fontFamily,
68
69
  fontSize,
69
70
  fontWeight,
70
- elevation,
71
+ elevated,
71
72
  color = 'var(--color-fgMuted)',
72
- background = elevation && elevation > 0 ? 'var(--color-bg)' : 'transparent',
73
+ background = elevated ? 'var(--color-bg)' : 'transparent',
73
74
  borderRadius,
74
75
  inset: insetInput,
75
76
  onDimensionsChange,
@@ -112,7 +113,7 @@ export const ChartText = /*#__PURE__*/memo(_ref => {
112
113
  };
113
114
  }
114
115
  const parentBounds = bounds !== null && bounds !== void 0 ? bounds : fullChartBounds;
115
- if (!textBBox || !parentBounds || parentBounds.width <= 0 || parentBounds.height <= 0) {
116
+ if (!backgroundRectDimensions || !parentBounds || parentBounds.width <= 0 || parentBounds.height <= 0) {
116
117
  return {
117
118
  x: 0,
118
119
  y: 0
@@ -120,25 +121,21 @@ export const ChartText = /*#__PURE__*/memo(_ref => {
120
121
  }
121
122
  let x = 0;
122
123
  let y = 0;
123
-
124
- // X-axis overflow
125
- if (textBBox.x < parentBounds.x) {
126
- x = parentBounds.x - textBBox.x; // positive = shift right
127
- } else if (textBBox.x + textBBox.width > parentBounds.x + parentBounds.width) {
128
- x = parentBounds.x + parentBounds.width - (textBBox.x + textBBox.width); // negative = shift left
124
+ if (backgroundRectDimensions.x < parentBounds.x) {
125
+ x = parentBounds.x - backgroundRectDimensions.x; // positive = shift right
126
+ } else if (backgroundRectDimensions.x + backgroundRectDimensions.width > parentBounds.x + parentBounds.width) {
127
+ x = parentBounds.x + parentBounds.width - (backgroundRectDimensions.x + backgroundRectDimensions.width); // negative = shift left
129
128
  }
130
-
131
- // Y-axis overflow
132
- if (textBBox.y < parentBounds.y) {
133
- y = parentBounds.y - textBBox.y; // positive = shift down
134
- } else if (textBBox.y + textBBox.height > parentBounds.y + parentBounds.height) {
135
- y = parentBounds.y + parentBounds.height - (textBBox.y + textBBox.height); // negative = shift up
129
+ if (backgroundRectDimensions.y < parentBounds.y) {
130
+ y = parentBounds.y - backgroundRectDimensions.y; // positive = shift down
131
+ } else if (backgroundRectDimensions.y + backgroundRectDimensions.height > parentBounds.y + parentBounds.height) {
132
+ y = parentBounds.y + parentBounds.height - (backgroundRectDimensions.y + backgroundRectDimensions.height); // negative = shift up
136
133
  }
137
134
  return {
138
135
  x,
139
136
  y
140
137
  };
141
- }, [textBBox, fullChartBounds, bounds, disableRepositioning]);
138
+ }, [backgroundRectDimensions, fullChartBounds, bounds, disableRepositioning]);
142
139
 
143
140
  // Compose the final reported rect including any overflow translation applied
144
141
  const reportedRect = useMemo(() => {
@@ -150,8 +147,6 @@ export const ChartText = /*#__PURE__*/memo(_ref => {
150
147
  height: backgroundRectDimensions.height
151
148
  };
152
149
  }, [backgroundRectDimensions, overflowAmount.x, overflowAmount.y]);
153
-
154
- // send latest calculated dimensions (adjusted for translation) to parent
155
150
  useEffect(() => {
156
151
  if (onDimensionsChange && reportedRect !== null) {
157
152
  onDimensionsChange(reportedRect);
@@ -196,14 +191,14 @@ export const ChartText = /*#__PURE__*/memo(_ref => {
196
191
  opacity: isDimensionsReady ? 1 : 0
197
192
  },
198
193
  transition: animate ? {
199
- duration: 0.2,
194
+ duration: accessoryFadeTransitionDuration,
200
195
  ease: 'easeOut'
201
196
  } : undefined,
202
197
  children: [/*#__PURE__*/_jsx(Box, {
203
198
  as: "rect",
204
199
  className: classNames === null || classNames === void 0 ? void 0 : classNames.backgroundRect,
205
200
  fill: background,
206
- filter: elevation && elevation > 0 ? "drop-shadow(var(--shadow-elevation".concat(elevation, "))") : undefined,
201
+ filter: elevated ? 'drop-shadow(var(--shadow-elevation1))' : undefined,
207
202
  height: backgroundRectDimensions === null || backgroundRectDimensions === void 0 ? void 0 : backgroundRectDimensions.height,
208
203
  rx: borderRadius,
209
204
  ry: borderRadius,
@@ -36,12 +36,13 @@ const EPSILON_PX = 0.5;
36
36
  *
37
37
  * The component focuses solely on overlap prevention logic for better separation of concerns.
38
38
  */
39
- export const SmartChartTextGroup = /*#__PURE__*/memo(_ref => {
39
+ export const ChartTextGroup = /*#__PURE__*/memo(_ref => {
40
40
  let {
41
41
  labels,
42
42
  minGap = 8,
43
43
  prioritizeEndLabels = true,
44
- chartTextProps
44
+ chartTextProps,
45
+ LabelComponent = ChartText
45
46
  } = _ref;
46
47
  const [boundingBoxes, setBoundingBoxes] = useState(new Map());
47
48
  const _ref2 = chartTextProps !== null && chartTextProps !== void 0 ? chartTextProps : {},
@@ -213,7 +214,7 @@ export const SmartChartTextGroup = /*#__PURE__*/memo(_ref => {
213
214
  display: 'none'
214
215
  } : {})
215
216
  };
216
- return /*#__PURE__*/_jsx(ChartText, _objectSpread(_objectSpread(_objectSpread({
217
+ return /*#__PURE__*/_jsx(LabelComponent, _objectSpread(_objectSpread(_objectSpread({
217
218
  x: labelData.x,
218
219
  y: labelData.y
219
220
  }, restChartTextProps), labelData.chartTextProps), {}, {
@@ -1,4 +1,4 @@
1
1
  // codegen:start {preset: barrel, include: ./*.tsx, exclude: ./__stories__/*.tsx}
2
2
  export * from './ChartText';
3
- export * from './SmartChartTextGroup';
3
+ export * from './ChartTextGroup';
4
4
  // codegen:end
@@ -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
  */
@@ -458,6 +489,7 @@ const generateEvenlyDistributedTicks = (scale, tickInterval, possibleTickValues,
458
489
  * // Returns tick positions centered in each selected band
459
490
  */
460
491
  export const getAxisTicksData = _ref4 => {
492
+ var _options$anchor;
461
493
  let {
462
494
  ticks,
463
495
  scaleFunction,
@@ -467,53 +499,37 @@ export const getAxisTicksData = _ref4 => {
467
499
  tickInterval,
468
500
  options
469
501
  } = _ref4;
502
+ const anchor = (_options$anchor = options === null || options === void 0 ? void 0 : options.anchor) !== null && _options$anchor !== void 0 ? _options$anchor : 'middle';
503
+
470
504
  // Handle band scales
471
505
  if (isCategoricalScale(scaleFunction)) {
506
+ const bandScale = scaleFunction;
507
+
472
508
  // If explicit ticks are provided as array, use them
473
509
  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);
510
+ return ticks.filter(index => index >= 0 && index < categories.length).map(index => ({
511
+ tick: index,
512
+ position: getPointOnScale(index, bandScale, anchor)
513
+ }));
484
514
  }
485
515
 
486
516
  // If a tick function is provided, use it to filter
487
517
  if (typeof ticks === 'function') {
488
518
  return categories.map((category, index) => {
489
- var _bandwidth3, _bandwidth4, _ref6;
490
519
  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
520
  return {
496
521
  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
522
+ position: getPointOnScale(index, bandScale, anchor)
498
523
  };
499
524
  }).filter(Boolean);
500
525
  }
501
- if (typeof ticks === 'boolean' && !ticks) {
502
- return [];
503
- }
504
526
 
505
527
  // For band scales without explicit ticks, show all categories
506
528
  // 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);
529
+ return categories.map((_, index) => ({
530
+ tick: index,
531
+ position: getPointOnScale(index, bandScale, anchor)
532
+ }));
517
533
  }
518
534
 
519
535
  // Handle numeric scales
@@ -19,13 +19,13 @@ export const getChartDomain = (series, min, max) => {
19
19
  return domain;
20
20
  }
21
21
  if (series.length > 0) {
22
- const maxDataLength = Math.max(...series.map(s => {
22
+ const dataLength = Math.max(...series.map(s => {
23
23
  var _s$data;
24
24
  return ((_s$data = s.data) === null || _s$data === void 0 ? void 0 : _s$data.length) || 0;
25
25
  }));
26
- if (maxDataLength > 0) {
26
+ if (dataLength > 0) {
27
27
  if (domain.min === undefined) domain.min = 0;
28
- if (domain.max === undefined) domain.max = maxDataLength - 1;
28
+ if (domain.max === undefined) domain.max = dataLength - 1;
29
29
  }
30
30
  }
31
31
  return domain;
@@ -111,6 +111,32 @@ export const getStackedSeriesData = series => {
111
111
  return stackedDataMap;
112
112
  };
113
113
 
114
+ /**
115
+ * Extracts line data values from series data that may contain tuples.
116
+ * For tuple data [[baseline, value]], extracts the last value.
117
+ * For numeric data [value], returns as-is.
118
+ *
119
+ * @param data - Array of numbers, tuples, or null values
120
+ * @returns Array of numbers or null values
121
+ */
122
+ export const getLineData = data => {
123
+ if (!data) return [];
124
+
125
+ // Check if this is tuple data by finding first non-null entry
126
+ const firstNonNull = data.find(d => d !== null);
127
+ if (Array.isArray(firstNonNull)) {
128
+ return data.map(d => {
129
+ var _d$at;
130
+ if (d === null) return null;
131
+ if (Array.isArray(d)) return (_d$at = d.at(-1)) !== null && _d$at !== void 0 ? _d$at : null;
132
+ return d;
133
+ });
134
+ }
135
+
136
+ // Already numeric data
137
+ return data;
138
+ };
139
+
114
140
  /**
115
141
  * Calculates the range of a chart from series data.
116
142
  * Range represents the range of y-values from the data.