@coinbase/cds-web-visualization 3.4.0-beta.2 → 3.4.0-beta.21

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 (198) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dts/chart/CartesianChart.d.ts +56 -3
  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 +64 -7
  7. package/dts/chart/Path.d.ts.map +1 -1
  8. package/dts/chart/PeriodSelector.d.ts +5 -15
  9. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  10. package/dts/chart/area/Area.d.ts +50 -25
  11. package/dts/chart/area/Area.d.ts.map +1 -1
  12. package/dts/chart/area/AreaChart.d.ts +46 -6
  13. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  14. package/dts/chart/area/DottedArea.d.ts +21 -44
  15. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  16. package/dts/chart/area/GradientArea.d.ts +21 -12
  17. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  18. package/dts/chart/area/SolidArea.d.ts +16 -1
  19. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  20. package/dts/chart/axis/Axis.d.ts +109 -63
  21. package/dts/chart/axis/Axis.d.ts.map +1 -1
  22. package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
  23. package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
  24. package/dts/chart/axis/XAxis.d.ts +1 -1
  25. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  26. package/dts/chart/axis/YAxis.d.ts +2 -2
  27. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  28. package/dts/chart/axis/index.d.ts +1 -0
  29. package/dts/chart/axis/index.d.ts.map +1 -1
  30. package/dts/chart/bar/Bar.d.ts +50 -12
  31. package/dts/chart/bar/Bar.d.ts.map +1 -1
  32. package/dts/chart/bar/BarChart.d.ts +20 -8
  33. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  34. package/dts/chart/bar/BarPlot.d.ts +3 -1
  35. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  36. package/dts/chart/bar/BarStack.d.ts +41 -46
  37. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  38. package/dts/chart/bar/BarStackGroup.d.ts +2 -0
  39. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  40. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  41. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  42. package/dts/chart/gradient/Gradient.d.ts +35 -0
  43. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  44. package/dts/chart/gradient/index.d.ts +2 -0
  45. package/dts/chart/gradient/index.d.ts.map +1 -0
  46. package/dts/chart/index.d.ts +3 -1
  47. package/dts/chart/index.d.ts.map +1 -1
  48. package/dts/chart/legend/DefaultLegendEntry.d.ts +21 -0
  49. package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
  50. package/dts/chart/legend/DefaultLegendShape.d.ts +7 -0
  51. package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
  52. package/dts/chart/legend/Legend.d.ts +169 -0
  53. package/dts/chart/legend/Legend.d.ts.map +1 -0
  54. package/dts/chart/legend/index.d.ts +4 -0
  55. package/dts/chart/legend/index.d.ts.map +1 -0
  56. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  57. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  58. package/dts/chart/line/DottedLine.d.ts +15 -3
  59. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  60. package/dts/chart/line/Line.d.ts +84 -28
  61. package/dts/chart/line/Line.d.ts.map +1 -1
  62. package/dts/chart/line/LineChart.d.ts +28 -8
  63. package/dts/chart/line/LineChart.d.ts.map +1 -1
  64. package/dts/chart/line/ReferenceLine.d.ts +99 -44
  65. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  66. package/dts/chart/line/SolidLine.d.ts +14 -3
  67. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  68. package/dts/chart/line/index.d.ts +1 -1
  69. package/dts/chart/line/index.d.ts.map +1 -1
  70. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  71. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  72. package/dts/chart/point/Point.d.ts +217 -0
  73. package/dts/chart/point/Point.d.ts.map +1 -0
  74. package/dts/chart/point/index.d.ts +3 -0
  75. package/dts/chart/point/index.d.ts.map +1 -0
  76. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +41 -0
  77. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  78. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  79. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  80. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +10 -0
  81. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  82. package/dts/chart/scrubber/Scrubber.d.ts +303 -70
  83. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  84. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +80 -0
  85. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  86. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +56 -0
  87. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  88. package/dts/chart/scrubber/index.d.ts +3 -0
  89. package/dts/chart/scrubber/index.d.ts.map +1 -1
  90. package/dts/chart/text/ChartText.d.ts +46 -43
  91. package/dts/chart/text/ChartText.d.ts.map +1 -1
  92. package/dts/chart/text/{SmartChartTextGroup.d.ts → ChartTextGroup.d.ts} +9 -3
  93. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  94. package/dts/chart/text/index.d.ts +1 -1
  95. package/dts/chart/text/index.d.ts.map +1 -1
  96. package/dts/chart/utils/axis.d.ts +25 -1
  97. package/dts/chart/utils/axis.d.ts.map +1 -1
  98. package/dts/chart/utils/bar.d.ts +34 -0
  99. package/dts/chart/utils/bar.d.ts.map +1 -1
  100. package/dts/chart/utils/chart.d.ts +45 -7
  101. package/dts/chart/utils/chart.d.ts.map +1 -1
  102. package/dts/chart/utils/context.d.ts +6 -0
  103. package/dts/chart/utils/context.d.ts.map +1 -1
  104. package/dts/chart/utils/gradient.d.ts +104 -0
  105. package/dts/chart/utils/gradient.d.ts.map +1 -0
  106. package/dts/chart/utils/index.d.ts +4 -0
  107. package/dts/chart/utils/index.d.ts.map +1 -1
  108. package/dts/chart/utils/interpolate.d.ts +112 -0
  109. package/dts/chart/utils/interpolate.d.ts.map +1 -0
  110. package/dts/chart/utils/path.d.ts +30 -1
  111. package/dts/chart/utils/path.d.ts.map +1 -1
  112. package/dts/chart/utils/point.d.ts +40 -7
  113. package/dts/chart/utils/point.d.ts.map +1 -1
  114. package/dts/chart/utils/scale.d.ts +11 -0
  115. package/dts/chart/utils/scale.d.ts.map +1 -1
  116. package/dts/chart/utils/scrubber.d.ts +40 -0
  117. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  118. package/dts/chart/utils/transition.d.ts +101 -0
  119. package/dts/chart/utils/transition.d.ts.map +1 -0
  120. package/dts/sparkline/sparkline-interactive/SparklineInteractiveMarkerDates.d.ts.map +1 -1
  121. package/esm/chart/CartesianChart.js +170 -83
  122. package/esm/chart/ChartProvider.js +2 -2
  123. package/esm/chart/Path.js +59 -54
  124. package/esm/chart/PeriodSelector.js +36 -32
  125. package/esm/chart/area/Area.js +26 -34
  126. package/esm/chart/area/AreaChart.js +29 -15
  127. package/esm/chart/area/DottedArea.js +39 -89
  128. package/esm/chart/area/GradientArea.js +37 -80
  129. package/esm/chart/area/SolidArea.js +32 -11
  130. package/esm/chart/axis/Axis.js +4 -39
  131. package/esm/chart/axis/DefaultAxisTickLabel.js +15 -0
  132. package/esm/chart/axis/XAxis.js +184 -63
  133. package/esm/chart/axis/YAxis.js +190 -57
  134. package/esm/chart/axis/index.js +1 -0
  135. package/esm/chart/bar/Bar.js +7 -1
  136. package/esm/chart/bar/BarChart.js +17 -32
  137. package/esm/chart/bar/BarPlot.js +5 -2
  138. package/esm/chart/bar/BarStack.js +74 -22
  139. package/esm/chart/bar/BarStackGroup.js +8 -18
  140. package/esm/chart/bar/DefaultBar.js +23 -28
  141. package/esm/chart/bar/DefaultBarStack.js +24 -20
  142. package/esm/chart/gradient/Gradient.js +104 -0
  143. package/esm/chart/gradient/index.js +1 -0
  144. package/esm/chart/index.js +3 -1
  145. package/esm/chart/legend/DefaultLegendEntry.css +1 -0
  146. package/esm/chart/legend/DefaultLegendEntry.js +50 -0
  147. package/esm/chart/legend/DefaultLegendShape.css +5 -0
  148. package/esm/chart/legend/DefaultLegendShape.js +47 -0
  149. package/esm/chart/legend/Legend.js +76 -0
  150. package/esm/chart/legend/index.js +3 -0
  151. package/esm/chart/line/DefaultReferenceLineLabel.js +81 -0
  152. package/esm/chart/line/DottedLine.js +41 -17
  153. package/esm/chart/line/Line.js +87 -75
  154. package/esm/chart/line/LineChart.js +24 -8
  155. package/esm/chart/line/ReferenceLine.js +45 -43
  156. package/esm/chart/line/SolidLine.js +39 -15
  157. package/esm/chart/line/index.js +1 -1
  158. package/esm/chart/{line/GradientLine.js → point/DefaultPointLabel.js} +31 -45
  159. package/esm/chart/point/Point.css +2 -0
  160. package/esm/chart/{Point.js → point/Point.js} +87 -62
  161. package/esm/chart/point/index.js +2 -0
  162. package/esm/chart/scrubber/DefaultScrubberBeacon.js +154 -0
  163. package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +57 -0
  164. package/esm/chart/scrubber/{ScrubberBeaconLabel.js → DefaultScrubberLabel.js} +15 -18
  165. package/esm/chart/scrubber/Scrubber.js +101 -392
  166. package/esm/chart/scrubber/ScrubberBeaconGroup.js +174 -0
  167. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +209 -0
  168. package/esm/chart/scrubber/index.js +3 -1
  169. package/esm/chart/text/ChartText.js +15 -20
  170. package/esm/chart/text/{SmartChartTextGroup.js → ChartTextGroup.js} +4 -3
  171. package/esm/chart/text/index.js +1 -1
  172. package/esm/chart/utils/axis.js +47 -31
  173. package/esm/chart/utils/bar.js +48 -0
  174. package/esm/chart/utils/chart.js +42 -3
  175. package/esm/chart/utils/gradient.js +257 -0
  176. package/esm/chart/utils/index.js +4 -0
  177. package/esm/chart/utils/interpolate.js +644 -0
  178. package/esm/chart/utils/path.js +41 -9
  179. package/esm/chart/utils/point.js +99 -12
  180. package/esm/chart/utils/scale.js +13 -2
  181. package/esm/chart/utils/scrubber.js +137 -0
  182. package/esm/chart/utils/transition.js +133 -0
  183. package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
  184. package/esm/sparkline/sparkline-interactive/SparklineInteractiveMarkerDates.js +8 -4
  185. package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
  186. package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
  187. package/package.json +12 -11
  188. package/dts/chart/Point.d.ts +0 -153
  189. package/dts/chart/Point.d.ts.map +0 -1
  190. package/dts/chart/line/GradientLine.d.ts +0 -42
  191. package/dts/chart/line/GradientLine.d.ts.map +0 -1
  192. package/dts/chart/scrubber/ScrubberBeacon.d.ts +0 -93
  193. package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +0 -1
  194. package/dts/chart/scrubber/ScrubberBeaconLabel.d.ts +0 -7
  195. package/dts/chart/scrubber/ScrubberBeaconLabel.d.ts.map +0 -1
  196. package/dts/chart/text/SmartChartTextGroup.d.ts.map +0 -1
  197. package/esm/chart/Point.css +0 -2
  198. package/esm/chart/scrubber/ScrubberBeacon.js +0 -195
@@ -3,89 +3,86 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
3
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
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
- import React, { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
7
- import { useRefMap } from '@coinbase/cds-common/hooks/useRefMap';
6
+ import React, { forwardRef, memo, useImperativeHandle, useMemo } from 'react';
8
7
  import { m as motion } from 'framer-motion';
9
- import { axisTickLabelsInitialAnimationVariants } from '../axis';
10
8
  import { useCartesianChartContext } from '../ChartProvider';
11
9
  import { ReferenceLine } from '../line';
12
- import { getPointOnScale, useScrubberContext } from '../utils';
13
- import { ScrubberBeacon } from './ScrubberBeacon';
14
- import { ScrubberBeaconLabel } from './ScrubberBeaconLabel';
10
+ import { defaultAccessoryEnterTransition, getPointOnScale, getTransition, useScrubberContext } from '../utils';
11
+ import { DefaultScrubberBeacon } from './DefaultScrubberBeacon';
12
+ import { DefaultScrubberLabel } from './DefaultScrubberLabel';
13
+ import { ScrubberBeaconGroup } from './ScrubberBeaconGroup';
14
+ import { ScrubberBeaconLabelGroup } from './ScrubberBeaconLabelGroup';
15
15
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
- const minGap = 2;
17
-
18
- /**
19
- * Configuration for scrubber functionality across chart components.
20
- * Provides consistent API with smart defaults and component customization.
21
- */
22
-
23
16
  /**
24
- * Unified component that manages all scrubber elements (beacons, line, labels)
25
- * with intelligent collision detection and consistent positioning.
17
+ * Unified component that manages all scrubber elements (beacons, line, labels).
26
18
  */
27
19
  export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
28
20
  let {
29
21
  seriesIds,
22
+ hideBeaconLabels,
30
23
  hideLine,
31
24
  label,
25
+ accessibilityLabel,
32
26
  lineStroke,
33
- labelProps,
34
- BeaconComponent,
27
+ BeaconComponent = DefaultScrubberBeacon,
35
28
  BeaconLabelComponent,
36
29
  LineComponent,
30
+ LabelComponent = DefaultScrubberLabel,
31
+ labelElevated,
37
32
  hideOverlay,
38
33
  overlayOffset = 2,
34
+ beaconLabelMinGap,
35
+ beaconLabelHorizontalOffset,
36
+ beaconLabelPreferredSide,
37
+ labelFont,
38
+ labelBoundsInset,
39
+ beaconLabelFont,
39
40
  testID,
40
41
  idlePulse,
42
+ beaconTransitions,
43
+ transitions = beaconTransitions,
44
+ beaconStroke,
41
45
  styles,
42
46
  classNames
43
47
  } = _ref;
44
- const scrubberGroupRef = useRef(null);
45
- const ScrubberBeaconRefs = useRefMap();
48
+ const beaconGroupRef = React.useRef(null);
46
49
  const {
47
50
  scrubberPosition
48
51
  } = useScrubberContext();
49
52
  const {
50
53
  getXScale,
51
- getYScale,
52
- getSeriesData,
53
54
  getXAxis,
54
55
  animate,
55
56
  series,
56
- drawingArea
57
+ drawingArea,
58
+ dataLength
57
59
  } = useCartesianChartContext();
58
- const getStackedSeriesData = getSeriesData; // getSeriesData now returns stacked data
59
-
60
- // Track label dimensions for collision detection
61
- const [labelDimensions, setLabelDimensions] = useState(new Map());
62
60
 
63
61
  // Expose imperative handle with pulse method
64
62
  useImperativeHandle(ref, () => ({
65
63
  pulse: () => {
66
- // Pulse all registered scrubber beacons
67
- Object.values(ScrubberBeaconRefs.refs).forEach(beaconRef => {
68
- beaconRef === null || beaconRef === void 0 || beaconRef.pulse();
69
- });
64
+ var _beaconGroupRef$curre;
65
+ (_beaconGroupRef$curre = beaconGroupRef.current) === null || _beaconGroupRef$curre === void 0 || _beaconGroupRef$curre.pulse();
70
66
  }
71
67
  }));
68
+ const filteredSeriesIds = useMemo(() => {
69
+ if (seriesIds === undefined) {
70
+ var _series$map;
71
+ return (_series$map = series === null || series === void 0 ? void 0 : series.map(s => s.id)) !== null && _series$map !== void 0 ? _series$map : [];
72
+ }
73
+ return seriesIds;
74
+ }, [series, seriesIds]);
72
75
  const {
73
76
  dataX,
74
77
  dataIndex
75
78
  } = useMemo(() => {
76
- var _series$reduce;
77
79
  const xScale = getXScale();
78
80
  const xAxis = getXAxis();
79
81
  if (!xScale) return {
80
82
  dataX: undefined,
81
83
  dataIndex: undefined
82
84
  };
83
- const maxDataLength = (_series$reduce = series === null || series === void 0 ? void 0 : series.reduce((max, s) => {
84
- var _seriesData$length;
85
- const seriesData = getStackedSeriesData(s.id) || getSeriesData(s.id);
86
- return Math.max(max, (_seriesData$length = seriesData === null || seriesData === void 0 ? void 0 : seriesData.length) !== null && _seriesData$length !== void 0 ? _seriesData$length : 0);
87
- }, 0)) !== null && _series$reduce !== void 0 ? _series$reduce : 0;
88
- const dataIndex = scrubberPosition !== null && scrubberPosition !== void 0 ? scrubberPosition : Math.max(0, maxDataLength - 1);
85
+ const dataIndex = scrubberPosition !== null && scrubberPosition !== void 0 ? scrubberPosition : Math.max(0, dataLength - 1);
89
86
 
90
87
  // Convert index to actual x value if axis has data
91
88
  let dataX;
@@ -99,317 +96,52 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
99
96
  dataX,
100
97
  dataIndex
101
98
  };
102
- }, [getXScale, getXAxis, series, scrubberPosition, getStackedSeriesData, getSeriesData]);
103
- const beaconPositions = useMemo(() => {
104
- var _series$filter$map$fi, _series$filter;
105
- const xScale = getXScale();
106
- if (!xScale || dataX === undefined || dataIndex === undefined) return [];
107
- return (_series$filter$map$fi = series === null || series === void 0 || (_series$filter = series.filter(s => {
108
- if (seriesIds === undefined) return true;
109
- return seriesIds.includes(s.id);
110
- })) === null || _series$filter === void 0 ? void 0 : _series$filter.map(s => {
111
- const sourceData = getStackedSeriesData(s.id) || getSeriesData(s.id);
112
-
113
- // Use dataIndex to get the y value from the series data array
114
- const stuff = sourceData === null || sourceData === void 0 ? void 0 : sourceData[dataIndex];
115
- let dataY;
116
- if (Array.isArray(stuff)) {
117
- dataY = stuff[stuff.length - 1];
118
- } else if (typeof stuff === 'number') {
119
- dataY = stuff;
120
- }
121
- if (dataY !== undefined) {
122
- const yScale = getYScale(s.yAxisId);
123
- if (!yScale) {
124
- return undefined;
125
- }
126
- const pixelY = getPointOnScale(dataY, yScale);
127
- const resolvedLabel = typeof s.label === 'function' ? s.label(dataIndex) : s.label;
128
- return {
129
- x: dataX,
130
- y: dataY,
131
- label: resolvedLabel,
132
- pixelY,
133
- targetSeries: s
134
- };
135
- }
136
- }).filter(beacon => beacon !== undefined)) !== null && _series$filter$map$fi !== void 0 ? _series$filter$map$fi : [];
137
- }, [getXScale, dataX, dataIndex, series, seriesIds, getStackedSeriesData, getSeriesData, getYScale]);
138
- const labelVerticalInset = 2;
139
- const labelHorizontalInset = 4;
140
-
141
- // Calculate optimal label positioning strategy
142
- const labelPositioning = useMemo(() => {
143
- // Get current beacon IDs that are actually being rendered
144
- const currentBeaconIds = new Set(beaconPositions.map(beacon => beacon === null || beacon === void 0 ? void 0 : beacon.targetSeries.id).filter(Boolean));
145
-
146
- // Only use dimensions for beacons that are currently being rendered
147
- const dimensions = Array.from(labelDimensions.values()).filter(dim => currentBeaconIds.has(dim.id));
148
- if (dimensions.length === 0) return {
149
- strategy: 'auto',
150
- adjustments: new Map()
151
- };
152
- const adjustments = new Map();
153
-
154
- // Sort by Y position to handle overlaps systematically
155
- const sortedDimensions = [...dimensions].sort((a, b) => a.preferredY - b.preferredY);
99
+ }, [getXScale, getXAxis, scrubberPosition, dataLength]);
156
100
 
157
- // Determine if we need to switch sides globally based on overflow
158
- let globalSide = 'right';
159
- const anchorRadius = 10; // Same as used in ScrubberBeaconLabel
160
- const bufferPx = 5; // Small buffer to prevent premature switching
101
+ // Compute resolved accessibility label
102
+ const resolvedAccessibilityLabel = useMemo(() => {
103
+ if (dataIndex === undefined) return undefined;
161
104
 
162
- // Safety check for valid bounds
163
- if (drawingArea.width <= 0 || drawingArea.height <= 0) {
164
- globalSide = 'right'; // Default to right if bounds are invalid
165
- } else {
166
- // Check if labels would overflow when positioned on the right side
167
- // Account for anchor radius and padding when calculating right edge
168
- const wouldOverflow = sortedDimensions.some(dim => {
169
- const labelRightEdge = dim.preferredX + anchorRadius + labelHorizontalInset + dim.width + bufferPx;
170
- return labelRightEdge > drawingArea.x + drawingArea.width;
171
- });
172
- globalSide = wouldOverflow ? 'left' : 'right';
173
- }
174
-
175
- // Initialize all labels at their preferred positions
176
- for (const dim of sortedDimensions) {
177
- adjustments.set(dim.id, {
178
- x: dim.preferredX,
179
- y: dim.preferredY,
180
- side: globalSide
181
- });
105
+ // If accessibilityLabel is provided, use it
106
+ if (accessibilityLabel) {
107
+ return typeof accessibilityLabel === 'function' ? accessibilityLabel(dataIndex) : accessibilityLabel;
182
108
  }
183
109
 
184
- // Check for collisions and resolve them
185
- const maxIterations = 10;
186
- let iteration = 0;
187
- while (iteration < maxIterations) {
188
- let hasCollisions = false;
189
- iteration++;
190
-
191
- // Sort by current Y position for systematic collision resolution
192
- const currentPositions = sortedDimensions.map(dim => _objectSpread(_objectSpread({}, dim), {}, {
193
- currentY: adjustments.get(dim.id).y
194
- })).sort((a, b) => a.currentY - b.currentY);
195
-
196
- // Check adjacent labels for overlaps
197
- for (let i = 0; i < currentPositions.length - 1; i++) {
198
- const current = currentPositions[i];
199
- const next = currentPositions[i + 1];
200
- const currentAdjustment = adjustments.get(current.id);
201
- const nextAdjustment = adjustments.get(next.id);
202
-
203
- // Calculate required separation
204
- const requiredSeparation = current.height / 2 + next.height / 2 + minGap;
205
- const currentSeparation = nextAdjustment.y - currentAdjustment.y;
206
- if (currentSeparation < requiredSeparation) {
207
- hasCollisions = true;
208
- const deficit = requiredSeparation - currentSeparation;
209
-
210
- // Move labels apart - split the adjustment
211
- const offsetPerLabel = deficit / 2;
212
- adjustments.set(current.id, _objectSpread(_objectSpread({}, currentAdjustment), {}, {
213
- y: currentAdjustment.y - offsetPerLabel
214
- }));
215
- adjustments.set(next.id, _objectSpread(_objectSpread({}, nextAdjustment), {}, {
216
- y: nextAdjustment.y + offsetPerLabel
217
- }));
218
- }
219
- }
220
- if (!hasCollisions) {
221
- break;
222
- }
223
- }
224
-
225
- // After collision resolution, ensure all labels are within bounds
226
- const labelIds = Array.from(adjustments.keys());
227
-
228
- // Group labels that are close together or overlapping
229
- // This prevents distant labels from being unnecessarily shifted
230
- const findConnectedGroups = () => {
231
- const groups = [];
232
- const visited = new Set();
233
- for (const id of labelIds) {
234
- if (visited.has(id)) continue;
235
- const group = [id];
236
- visited.add(id);
237
- const queue = [id];
238
- while (queue.length > 0) {
239
- const currentId = queue.shift();
240
- const currentAdjustment = adjustments.get(currentId);
241
- const currentDim = sortedDimensions.find(d => d.id === currentId);
242
-
243
- // Check if this label overlaps or is close to any other unvisited label
244
- for (const otherId of labelIds) {
245
- if (visited.has(otherId)) continue;
246
- const otherAdjustment = adjustments.get(otherId);
247
- const otherDim = sortedDimensions.find(d => d.id === otherId);
248
-
249
- // Calculate distance between labels
250
- const distance = Math.abs(currentAdjustment.y - otherAdjustment.y);
251
- const minDistance = (currentDim.height + otherDim.height) / 2 + minGap * 2;
252
-
253
- // Labels are considered connected if they're close enough to potentially overlap
254
- if (distance <= minDistance) {
255
- visited.add(otherId);
256
- group.push(otherId);
257
- queue.push(otherId);
258
- }
259
- }
260
- }
261
- groups.push(group);
262
- }
263
- return groups;
264
- };
265
- const connectedGroups = findConnectedGroups();
266
-
267
- // Process each connected group independently
268
- for (const groupIds of connectedGroups) {
269
- // Check if any labels in this group are outside bounds
270
- const groupOutOfBounds = groupIds.some(id => {
271
- const adjustment = adjustments.get(id);
272
- const dim = sortedDimensions.find(d => d.id === id);
273
- const labelTop = adjustment.y - dim.height / 2;
274
- const labelBottom = adjustment.y + dim.height / 2;
275
- return labelTop < drawingArea.y || labelBottom > drawingArea.y + drawingArea.height;
276
- });
277
- if (groupOutOfBounds) {
278
- // Get labels in this group sorted by their preferred Y position
279
- const groupLabels = groupIds.map(id => ({
280
- id,
281
- dim: sortedDimensions.find(d => d.id === id),
282
- preferredY: sortedDimensions.find(d => d.id === id).preferredY,
283
- currentY: adjustments.get(id).y
284
- })).sort((a, b) => a.preferredY - b.preferredY);
285
-
286
- // Calculate total height needed for this group
287
- const totalLabelHeight = groupLabels.reduce((sum, label) => sum + label.dim.height, 0);
288
- const totalGaps = (groupLabels.length - 1) * minGap;
289
- const totalNeeded = totalLabelHeight + totalGaps;
290
- if (totalNeeded > drawingArea.height) {
291
- // Not enough space - use compressed equal spacing as fallback
292
- const compressedGap = Math.max(2, (drawingArea.height - totalLabelHeight) / Math.max(1, groupLabels.length - 1));
293
- let currentY = drawingArea.y + groupLabels[0].dim.height / 2;
294
- for (const label of groupLabels) {
295
- adjustments.set(label.id, _objectSpread(_objectSpread({}, adjustments.get(label.id)), {}, {
296
- y: currentY
297
- }));
298
- currentY += label.dim.height + compressedGap;
299
- }
300
- } else {
301
- // Enough space - use minimal displacement algorithm for this group
302
- const finalPositions = [...groupLabels];
303
-
304
- // Ensure minimum spacing between adjacent labels in this group
305
- for (let i = 1; i < finalPositions.length; i++) {
306
- const prev = finalPositions[i - 1];
307
- const current = finalPositions[i];
308
-
309
- // Calculate minimum Y position for current label
310
- const minCurrentY = prev.preferredY + prev.dim.height / 2 + minGap + current.dim.height / 2;
311
- if (current.preferredY < minCurrentY) {
312
- // Need to push this label down
313
- current.preferredY = minCurrentY;
314
- }
315
- }
316
-
317
- // Check if this specific group fits within bounds, if not shift only this group
318
- const groupTop = finalPositions[0].preferredY - finalPositions[0].dim.height / 2;
319
- const groupBottom = finalPositions[finalPositions.length - 1].preferredY + finalPositions[finalPositions.length - 1].dim.height / 2;
320
- let shiftAmount = 0;
321
- if (groupTop < drawingArea.y) {
322
- // Group is too high, shift down
323
- shiftAmount = drawingArea.y - groupTop;
324
- } else if (groupBottom > drawingArea.y + drawingArea.height) {
325
- // Group is too low, shift up
326
- shiftAmount = drawingArea.y + drawingArea.height - groupBottom;
327
- }
328
-
329
- // Apply final positions with shift only to this group
330
- for (const label of finalPositions) {
331
- const finalY = label.preferredY + shiftAmount;
332
-
333
- // Final bounds check for individual labels
334
- const clampedY = Math.max(drawingArea.y + label.dim.height / 2, Math.min(drawingArea.y + drawingArea.height - label.dim.height / 2, finalY));
335
- adjustments.set(label.id, _objectSpread(_objectSpread({}, adjustments.get(label.id)), {}, {
336
- y: clampedY
337
- }));
338
- }
339
- }
340
- }
341
- }
342
- return {
343
- strategy: globalSide,
344
- adjustments
345
- };
346
- }, [beaconPositions, labelDimensions, drawingArea]);
347
-
348
- // Callback for labels to register their dimensions
349
- const registerLabelDimensions = useCallback((id, width, height, x, y) => {
350
- setLabelDimensions(prev => {
351
- const existing = prev.get(id);
352
- const newDimensions = {
353
- id,
354
- width,
355
- height,
356
- preferredX: x,
357
- preferredY: y
358
- };
359
-
360
- // Only update if dimensions actually changed
361
- if (existing && existing.width === width && existing.height === height && existing.preferredX === x && existing.preferredY === y) {
362
- return prev;
363
- }
364
- const next = new Map(prev);
365
- next.set(id, newDimensions);
366
- return next;
367
- });
368
- }, []);
369
-
370
- // Callback to create ref handlers for scrubber beacons
371
- const createScrubberBeaconRef = useCallback(seriesId => {
372
- return beaconRef => {
373
- if (beaconRef) {
374
- ScrubberBeaconRefs.registerRef(seriesId, beaconRef);
375
- }
376
- };
377
- }, [ScrubberBeaconRefs]);
378
-
379
- // synchronize label positioning state when the position of any scrubber beacons change
380
- useEffect(() => {
381
- const currentBeaconIds = new Set(beaconPositions.map(beacon => beacon === null || beacon === void 0 ? void 0 : beacon.targetSeries.id).filter(Boolean));
382
- setLabelDimensions(prev => {
383
- const next = new Map();
384
- for (const [id, dimensions] of prev) {
385
- if (currentBeaconIds.has(id)) {
386
- next.set(id, dimensions);
387
- }
388
- }
389
- return next;
390
- });
391
- }, [beaconPositions]);
110
+ // Otherwise, if label resolves to a string, use that
111
+ const resolvedLabel = typeof label === 'function' ? label(dataIndex) : label;
112
+ return typeof resolvedLabel === 'string' ? resolvedLabel : undefined;
113
+ }, [accessibilityLabel, label, dataIndex]);
114
+ const beaconLabels = useMemo(() => {
115
+ var _series$filter$filter;
116
+ return (_series$filter$filter = series === null || series === void 0 ? void 0 : series.filter(s => filteredSeriesIds.includes(s.id)).filter(s => s.label !== undefined && s.label.length > 0).map(s => ({
117
+ seriesId: s.id,
118
+ label: s.label,
119
+ color: s.color
120
+ }))) !== null && _series$filter$filter !== void 0 ? _series$filter$filter : [];
121
+ }, [series, filteredSeriesIds]);
122
+ const groupEnterTransition = useMemo(() => getTransition(transitions === null || transitions === void 0 ? void 0 : transitions.enter, animate, defaultAccessoryEnterTransition), [transitions === null || transitions === void 0 ? void 0 : transitions.enter, animate]);
392
123
 
393
124
  // Check if we have at least the default X scale
394
125
  const defaultXScale = getXScale();
395
126
  if (!defaultXScale) return null;
396
-
397
- // Use custom components if provided
398
- const ScrubberLineComponent = LineComponent !== null && LineComponent !== void 0 ? LineComponent : ReferenceLine;
399
- const ScrubberBeaconComponent = BeaconComponent !== null && BeaconComponent !== void 0 ? BeaconComponent : ScrubberBeacon;
400
- const ScrubberBeaconLabelComponent = BeaconLabelComponent !== null && BeaconLabelComponent !== void 0 ? BeaconLabelComponent : ScrubberBeaconLabel;
401
127
  const pixelX = dataX !== undefined && defaultXScale ? getPointOnScale(dataX, defaultXScale) : undefined;
402
128
  return /*#__PURE__*/_jsxs(motion.g, _objectSpread(_objectSpread({
403
- ref: scrubberGroupRef,
129
+ "aria-atomic": "true",
130
+ "aria-label": resolvedAccessibilityLabel,
131
+ "aria-live": "polite",
404
132
  "data-component": "scrubber-group",
405
- "data-testid": testID
133
+ "data-testid": testID,
134
+ role: "status"
406
135
  }, animate ? {
407
- animate: 'animate',
408
- exit: 'exit',
409
- initial: 'initial',
410
- variants: axisTickLabelsInitialAnimationVariants
136
+ animate: {
137
+ opacity: 1,
138
+ transition: groupEnterTransition
139
+ },
140
+ initial: {
141
+ opacity: 0
142
+ }
411
143
  } : {}), {}, {
412
- children: [!hideOverlay && dataX !== undefined && scrubberPosition !== undefined && pixelX !== undefined && /*#__PURE__*/_jsx("rect", {
144
+ children: [!hideOverlay && scrubberPosition !== undefined && pixelX !== undefined && /*#__PURE__*/_jsx("rect", {
413
145
  className: classNames === null || classNames === void 0 ? void 0 : classNames.overlay,
414
146
  fill: "var(--color-bg)",
415
147
  height: drawingArea.height + overlayOffset * 2,
@@ -418,66 +150,43 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
418
150
  width: drawingArea.x + drawingArea.width - pixelX + overlayOffset,
419
151
  x: pixelX,
420
152
  y: drawingArea.y - overlayOffset
421
- }), !hideLine && scrubberPosition !== undefined && dataX !== undefined && /*#__PURE__*/_jsx(ScrubberLineComponent, {
422
- className: classNames === null || classNames === void 0 ? void 0 : classNames.line,
153
+ }), !hideLine && scrubberPosition !== undefined && dataX !== undefined && /*#__PURE__*/_jsx(ReferenceLine, {
154
+ LabelComponent: LabelComponent,
155
+ LineComponent: LineComponent,
156
+ classNames: {
157
+ label: classNames === null || classNames === void 0 ? void 0 : classNames.label,
158
+ line: classNames === null || classNames === void 0 ? void 0 : classNames.line
159
+ },
423
160
  dataX: dataX,
424
161
  label: typeof label === 'function' ? label(dataIndex) : label,
425
- labelProps: _objectSpread({
426
- verticalAlignment: 'middle',
427
- // Place in the middle vertically by default
428
- dy: -0.5 * drawingArea.y
429
- }, labelProps),
162
+ labelBoundsInset: labelBoundsInset,
163
+ labelElevated: labelElevated,
164
+ labelFont: labelFont,
430
165
  stroke: lineStroke,
431
- style: styles === null || styles === void 0 ? void 0 : styles.line
432
- }), beaconPositions.map(beacon => {
433
- var _beacon$targetSeries, _beacon$targetSeries2;
434
- if (!beacon) return null;
435
- const adjustment = labelPositioning.adjustments.get(beacon.targetSeries.id);
436
- const dotStroke = ((_beacon$targetSeries = beacon.targetSeries) === null || _beacon$targetSeries === void 0 ? void 0 : _beacon$targetSeries.color) || 'var(--color-fgPrimary)';
437
- return /*#__PURE__*/_jsxs("g", {
438
- "data-component": "scrubber-beacon",
439
- children: [/*#__PURE__*/_jsx(ScrubberBeaconComponent, {
440
- ref: createScrubberBeaconRef(beacon.targetSeries.id),
441
- className: classNames === null || classNames === void 0 ? void 0 : classNames.beacon,
442
- color: (_beacon$targetSeries2 = beacon.targetSeries) === null || _beacon$targetSeries2 === void 0 ? void 0 : _beacon$targetSeries2.color,
443
- dataX: beacon.x,
444
- dataY: beacon.y,
445
- idlePulse: idlePulse,
446
- seriesId: beacon.targetSeries.id,
447
- style: styles === null || styles === void 0 ? void 0 : styles.beacon,
448
- testID: testID ? "".concat(testID, "-").concat(beacon.targetSeries.id, "-dot") : undefined
449
- }), beacon.label && pixelX !== undefined && ((_adjustment$x, _adjustment$y, _adjustment$side) => {
450
- const finalAnchorX = (_adjustment$x = adjustment === null || adjustment === void 0 ? void 0 : adjustment.x) !== null && _adjustment$x !== void 0 ? _adjustment$x : pixelX;
451
- const finalAnchorY = (_adjustment$y = adjustment === null || adjustment === void 0 ? void 0 : adjustment.y) !== null && _adjustment$y !== void 0 ? _adjustment$y : beacon.pixelY;
452
- const finalSide = (_adjustment$side = adjustment === null || adjustment === void 0 ? void 0 : adjustment.side) !== null && _adjustment$side !== void 0 ? _adjustment$side : labelPositioning.strategy;
453
- return /*#__PURE__*/_jsx(ScrubberBeaconLabelComponent, {
454
- background: "var(--color-bg)",
455
- bounds: drawingArea,
456
- className: classNames === null || classNames === void 0 ? void 0 : classNames.beaconLabel,
457
- color: dotStroke,
458
- dx: finalSide === 'right' ? 16 : -16,
459
- horizontalAlignment: finalSide === 'right' ? 'left' : 'right',
460
- inset: {
461
- left: labelHorizontalInset,
462
- right: labelHorizontalInset,
463
- top: labelVerticalInset,
464
- bottom: labelVerticalInset
465
- },
466
- onDimensionsChange: _ref2 => {
467
- let {
468
- width,
469
- height
470
- } = _ref2;
471
- return registerLabelDimensions(beacon.targetSeries.id, width, height, pixelX, beacon.pixelY);
472
- },
473
- style: styles === null || styles === void 0 ? void 0 : styles.beaconLabel,
474
- testID: testID ? "".concat(testID, "-").concat(beacon.targetSeries.id, "-label") : undefined,
475
- x: finalAnchorX,
476
- y: finalAnchorY,
477
- children: beacon.label
478
- });
479
- })()]
480
- }, beacon.targetSeries.id);
166
+ styles: {
167
+ label: styles === null || styles === void 0 ? void 0 : styles.label,
168
+ line: styles === null || styles === void 0 ? void 0 : styles.line
169
+ }
170
+ }), /*#__PURE__*/_jsx(ScrubberBeaconGroup, {
171
+ ref: beaconGroupRef,
172
+ BeaconComponent: BeaconComponent,
173
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.beacon,
174
+ idlePulse: idlePulse,
175
+ seriesIds: filteredSeriesIds,
176
+ stroke: beaconStroke,
177
+ style: styles === null || styles === void 0 ? void 0 : styles.beacon,
178
+ testID: testID,
179
+ transitions: transitions
180
+ }), !hideBeaconLabels && beaconLabels.length > 0 && /*#__PURE__*/_jsx(ScrubberBeaconLabelGroup, {
181
+ BeaconLabelComponent: BeaconLabelComponent,
182
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.beaconLabel,
183
+ labelFont: beaconLabelFont,
184
+ labelHorizontalOffset: beaconLabelHorizontalOffset,
185
+ labelMinGap: beaconLabelMinGap,
186
+ labelPreferredSide: beaconLabelPreferredSide,
187
+ labels: beaconLabels,
188
+ style: styles === null || styles === void 0 ? void 0 : styles.beaconLabel,
189
+ transitions: transitions
481
190
  })]
482
191
  }));
483
192
  }));