@coinbase/cds-web-visualization 3.3.2 → 3.4.0-beta.10

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 (212) hide show
  1. package/CHANGELOG.md +50 -2
  2. package/dts/chart/CartesianChart.d.ts +72 -0
  3. package/dts/chart/CartesianChart.d.ts.map +1 -0
  4. package/dts/chart/ChartProvider.d.ts +6 -0
  5. package/dts/chart/ChartProvider.d.ts.map +1 -0
  6. package/dts/chart/Path.d.ts +54 -0
  7. package/dts/chart/Path.d.ts.map +1 -0
  8. package/dts/chart/PeriodSelector.d.ts +57 -0
  9. package/dts/chart/PeriodSelector.d.ts.map +1 -0
  10. package/dts/chart/area/Area.d.ts +78 -0
  11. package/dts/chart/area/Area.d.ts.map +1 -0
  12. package/dts/chart/area/AreaChart.d.ts +79 -0
  13. package/dts/chart/area/AreaChart.d.ts.map +1 -0
  14. package/dts/chart/area/DottedArea.d.ts +45 -0
  15. package/dts/chart/area/DottedArea.d.ts.map +1 -0
  16. package/dts/chart/area/GradientArea.d.ts +39 -0
  17. package/dts/chart/area/GradientArea.d.ts.map +1 -0
  18. package/dts/chart/area/SolidArea.d.ts +23 -0
  19. package/dts/chart/area/SolidArea.d.ts.map +1 -0
  20. package/dts/chart/area/index.d.ts +6 -0
  21. package/dts/chart/area/index.d.ts.map +1 -0
  22. package/dts/chart/axis/Axis.d.ts +255 -0
  23. package/dts/chart/axis/Axis.d.ts.map +1 -0
  24. package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
  25. package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
  26. package/dts/chart/axis/XAxis.d.ts +16 -0
  27. package/dts/chart/axis/XAxis.d.ts.map +1 -0
  28. package/dts/chart/axis/YAxis.d.ts +21 -0
  29. package/dts/chart/axis/YAxis.d.ts.map +1 -0
  30. package/dts/chart/axis/index.d.ts +5 -0
  31. package/dts/chart/axis/index.d.ts.map +1 -0
  32. package/dts/chart/bar/Bar.d.ts +94 -0
  33. package/dts/chart/bar/Bar.d.ts.map +1 -0
  34. package/dts/chart/bar/BarChart.d.ts +62 -0
  35. package/dts/chart/bar/BarChart.d.ts.map +1 -0
  36. package/dts/chart/bar/BarPlot.d.ts +30 -0
  37. package/dts/chart/bar/BarPlot.d.ts.map +1 -0
  38. package/dts/chart/bar/BarStack.d.ts +103 -0
  39. package/dts/chart/bar/BarStack.d.ts.map +1 -0
  40. package/dts/chart/bar/BarStackGroup.d.ts +36 -0
  41. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -0
  42. package/dts/chart/bar/DefaultBar.d.ts +17 -0
  43. package/dts/chart/bar/DefaultBar.d.ts.map +1 -0
  44. package/dts/chart/bar/DefaultBarStack.d.ts +16 -0
  45. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -0
  46. package/dts/chart/bar/index.d.ts +8 -0
  47. package/dts/chart/bar/index.d.ts.map +1 -0
  48. package/dts/chart/gradient/Gradient.d.ts +35 -0
  49. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  50. package/dts/chart/gradient/index.d.ts +2 -0
  51. package/dts/chart/gradient/index.d.ts.map +1 -0
  52. package/dts/chart/index.d.ts +14 -0
  53. package/dts/chart/index.d.ts.map +1 -0
  54. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  55. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  56. package/dts/chart/line/DottedLine.d.ts +26 -0
  57. package/dts/chart/line/DottedLine.d.ts.map +1 -0
  58. package/dts/chart/line/Line.d.ts +122 -0
  59. package/dts/chart/line/Line.d.ts.map +1 -0
  60. package/dts/chart/line/LineChart.d.ts +77 -0
  61. package/dts/chart/line/LineChart.d.ts.map +1 -0
  62. package/dts/chart/line/ReferenceLine.d.ts +178 -0
  63. package/dts/chart/line/ReferenceLine.d.ts.map +1 -0
  64. package/dts/chart/line/SolidLine.d.ts +25 -0
  65. package/dts/chart/line/SolidLine.d.ts.map +1 -0
  66. package/dts/chart/line/index.d.ts +7 -0
  67. package/dts/chart/line/index.d.ts.map +1 -0
  68. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  69. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  70. package/dts/chart/point/Point.d.ts +201 -0
  71. package/dts/chart/point/Point.d.ts.map +1 -0
  72. package/dts/chart/point/index.d.ts +3 -0
  73. package/dts/chart/point/index.d.ts.map +1 -0
  74. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +24 -0
  75. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  76. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  77. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  78. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +10 -0
  79. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  80. package/dts/chart/scrubber/Scrubber.d.ts +290 -0
  81. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -0
  82. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +70 -0
  83. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  84. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +32 -0
  85. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  86. package/dts/chart/scrubber/ScrubberProvider.d.ts +17 -0
  87. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -0
  88. package/dts/chart/scrubber/index.d.ts +5 -0
  89. package/dts/chart/scrubber/index.d.ts.map +1 -0
  90. package/dts/chart/text/ChartText.d.ts +117 -0
  91. package/dts/chart/text/ChartText.d.ts.map +1 -0
  92. package/dts/chart/text/ChartTextGroup.d.ts +61 -0
  93. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  94. package/dts/chart/text/index.d.ts +3 -0
  95. package/dts/chart/text/index.d.ts.map +1 -0
  96. package/dts/chart/utils/axis.d.ts +342 -0
  97. package/dts/chart/utils/axis.d.ts.map +1 -0
  98. package/dts/chart/utils/bar.d.ts +20 -0
  99. package/dts/chart/utils/bar.d.ts.map +1 -0
  100. package/dts/chart/utils/chart.d.ts +117 -0
  101. package/dts/chart/utils/chart.d.ts.map +1 -0
  102. package/dts/chart/utils/context.d.ts +101 -0
  103. package/dts/chart/utils/context.d.ts.map +1 -0
  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 +12 -0
  107. package/dts/chart/utils/index.d.ts.map +1 -0
  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 +130 -0
  111. package/dts/chart/utils/path.d.ts.map +1 -0
  112. package/dts/chart/utils/point.d.ts +104 -0
  113. package/dts/chart/utils/point.d.ts.map +1 -0
  114. package/dts/chart/utils/scale.d.ts +43 -0
  115. package/dts/chart/utils/scale.d.ts.map +1 -0
  116. package/dts/chart/utils/scrubber.d.ts +39 -0
  117. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  118. package/dts/chart/utils/transition.d.ts +65 -0
  119. package/dts/chart/utils/transition.d.ts.map +1 -0
  120. package/dts/index.d.ts +1 -0
  121. package/dts/index.d.ts.map +1 -1
  122. package/dts/sparkline/Sparkline.d.ts +44 -9
  123. package/dts/sparkline/Sparkline.d.ts.map +1 -1
  124. package/dts/sparkline/SparklineArea.d.ts +4 -0
  125. package/dts/sparkline/SparklineArea.d.ts.map +1 -1
  126. package/dts/sparkline/SparklineAreaPattern.d.ts +5 -0
  127. package/dts/sparkline/SparklineAreaPattern.d.ts.map +1 -1
  128. package/dts/sparkline/SparklineGradient.d.ts +5 -0
  129. package/dts/sparkline/SparklineGradient.d.ts.map +1 -1
  130. package/dts/sparkline/generateSparklineWithId.d.ts +1 -0
  131. package/dts/sparkline/generateSparklineWithId.d.ts.map +1 -1
  132. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts +9 -0
  133. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts.map +1 -1
  134. package/dts/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.d.ts +3 -0
  135. package/dts/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.d.ts.map +1 -1
  136. package/dts/sparkline/sparkline-interactive/SparklineInteractivePaths.d.ts +2 -1
  137. package/dts/sparkline/sparkline-interactive/SparklineInteractivePaths.d.ts.map +1 -1
  138. package/esm/chart/CartesianChart.css +1 -0
  139. package/esm/chart/CartesianChart.js +313 -0
  140. package/esm/chart/ChartProvider.js +10 -0
  141. package/esm/chart/Path.js +95 -0
  142. package/esm/chart/PeriodSelector.css +1 -0
  143. package/esm/chart/PeriodSelector.js +112 -0
  144. package/esm/chart/area/Area.js +75 -0
  145. package/esm/chart/area/AreaChart.js +173 -0
  146. package/esm/chart/area/DottedArea.js +87 -0
  147. package/esm/chart/area/GradientArea.js +65 -0
  148. package/esm/chart/area/SolidArea.js +47 -0
  149. package/esm/chart/area/index.js +7 -0
  150. package/esm/chart/axis/Axis.js +25 -0
  151. package/esm/chart/axis/DefaultAxisTickLabel.js +15 -0
  152. package/esm/chart/axis/XAxis.css +2 -0
  153. package/esm/chart/axis/XAxis.js +219 -0
  154. package/esm/chart/axis/YAxis.css +2 -0
  155. package/esm/chart/axis/YAxis.js +214 -0
  156. package/esm/chart/axis/index.js +6 -0
  157. package/esm/chart/bar/Bar.js +61 -0
  158. package/esm/chart/bar/BarChart.js +130 -0
  159. package/esm/chart/bar/BarPlot.js +97 -0
  160. package/esm/chart/bar/BarStack.js +561 -0
  161. package/esm/chart/bar/BarStackGroup.js +86 -0
  162. package/esm/chart/bar/DefaultBar.js +61 -0
  163. package/esm/chart/bar/DefaultBarStack.js +58 -0
  164. package/esm/chart/bar/index.js +9 -0
  165. package/esm/chart/gradient/Gradient.js +104 -0
  166. package/esm/chart/gradient/index.js +1 -0
  167. package/esm/chart/index.js +15 -0
  168. package/esm/chart/line/DefaultReferenceLineLabel.js +81 -0
  169. package/esm/chart/line/DottedLine.js +59 -0
  170. package/esm/chart/line/Line.js +185 -0
  171. package/esm/chart/line/LineChart.js +132 -0
  172. package/esm/chart/line/ReferenceLine.js +140 -0
  173. package/esm/chart/line/SolidLine.js +55 -0
  174. package/esm/chart/line/index.js +8 -0
  175. package/esm/chart/point/DefaultPointLabel.js +44 -0
  176. package/esm/chart/point/Point.css +2 -0
  177. package/esm/chart/point/Point.js +180 -0
  178. package/esm/chart/point/index.js +2 -0
  179. package/esm/chart/scrubber/DefaultScrubberBeacon.js +155 -0
  180. package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +46 -0
  181. package/esm/chart/scrubber/DefaultScrubberLabel.js +30 -0
  182. package/esm/chart/scrubber/Scrubber.js +189 -0
  183. package/esm/chart/scrubber/ScrubberBeaconGroup.js +166 -0
  184. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +186 -0
  185. package/esm/chart/scrubber/ScrubberProvider.js +228 -0
  186. package/esm/chart/scrubber/index.js +4 -0
  187. package/esm/chart/text/ChartText.js +230 -0
  188. package/esm/chart/text/ChartTextGroup.js +227 -0
  189. package/esm/chart/text/index.js +4 -0
  190. package/esm/chart/utils/axis.js +593 -0
  191. package/esm/chart/utils/bar.js +24 -0
  192. package/esm/chart/utils/chart.js +255 -0
  193. package/esm/chart/utils/context.js +15 -0
  194. package/esm/chart/utils/gradient.js +257 -0
  195. package/esm/chart/utils/index.js +13 -0
  196. package/esm/chart/utils/interpolate.js +644 -0
  197. package/esm/chart/utils/path.js +227 -0
  198. package/esm/chart/utils/point.js +187 -0
  199. package/esm/chart/utils/scale.js +48 -0
  200. package/esm/chart/utils/scrubber.js +132 -0
  201. package/esm/chart/utils/transition.js +111 -0
  202. package/esm/index.js +4 -1
  203. package/esm/sparkline/Sparkline.js +129 -15
  204. package/esm/sparkline/SparklineArea.js +7 -2
  205. package/esm/sparkline/SparklineAreaPattern.js +4 -2
  206. package/esm/sparkline/SparklineGradient.js +16 -58
  207. package/esm/sparkline/generateSparklineWithId.js +3 -2
  208. package/esm/sparkline/sparkline-interactive/SparklineInteractive.js +5 -1
  209. package/esm/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.js +5 -2
  210. package/esm/sparkline/sparkline-interactive/SparklineInteractiveMarkerDates.js +1 -1
  211. package/esm/sparkline/sparkline-interactive/SparklineInteractivePaths.js +4 -0
  212. package/package.json +13 -9
@@ -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
+ });
@@ -0,0 +1,228 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import { useCartesianChartContext } from '../ChartProvider';
3
+ import { isCategoricalScale, ScrubberContext } from '../utils';
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ /**
6
+ * A component which encapsulates the ScrubberContext.
7
+ * It depends on a ChartContext in order to provide accurate mouse tracking.
8
+ */
9
+ export const ScrubberProvider = _ref => {
10
+ let {
11
+ children,
12
+ svgRef,
13
+ enableScrubbing,
14
+ onScrubberPositionChange
15
+ } = _ref;
16
+ const chartContext = useCartesianChartContext();
17
+ if (!chartContext) {
18
+ throw new Error('ScrubberProvider must be used within a ChartContext');
19
+ }
20
+ const {
21
+ getXScale,
22
+ getXAxis,
23
+ series
24
+ } = chartContext;
25
+ 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;
34
+ let closestIndex = 0;
35
+ let closestDistance = Infinity;
36
+ 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));
40
+ if (distance < closestDistance) {
41
+ closestDistance = distance;
42
+ closestIndex = i;
43
+ }
44
+ }
45
+ }
46
+ return closestIndex;
47
+ } else {
48
+ // For numeric scales with axis data, find the nearest data point
49
+ const axisData = xAxis.data;
50
+ if (axisData && Array.isArray(axisData) && typeof axisData[0] === 'number') {
51
+ // We have numeric axis data - find the closest data point
52
+ const numericData = axisData;
53
+ let closestIndex = 0;
54
+ let closestDistance = Infinity;
55
+ 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
+ if (distance < closestDistance) {
61
+ closestDistance = distance;
62
+ closestIndex = i;
63
+ }
64
+ }
65
+ }
66
+ return closestIndex;
67
+ } else {
68
+ 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
+ }
74
+ }
75
+ }, [getXScale, getXAxis]);
76
+ const handlePointerMove = useCallback((clientX, target) => {
77
+ if (!enableScrubbing || !series || series.length === 0) return;
78
+ const rect = target.getBoundingClientRect();
79
+ const x = clientX - rect.left;
80
+ const dataIndex = getDataIndexFromX(x);
81
+ if (dataIndex !== scrubberPosition) {
82
+ setScrubberPosition(dataIndex);
83
+ onScrubberPositionChange === null || onScrubberPositionChange === void 0 || onScrubberPositionChange(dataIndex);
84
+ }
85
+ }, [enableScrubbing, series, getDataIndexFromX, scrubberPosition, onScrubberPositionChange]);
86
+ const handleMouseMove = useCallback(event => {
87
+ const target = event.currentTarget;
88
+ handlePointerMove(event.clientX, target);
89
+ }, [handlePointerMove]);
90
+ const handleTouchMove = useCallback(event => {
91
+ if (!event.touches.length) return;
92
+ // Prevent scrolling while scrubbing
93
+ event.preventDefault();
94
+ const touch = event.touches[0];
95
+ const target = event.currentTarget;
96
+ handlePointerMove(touch.clientX, target);
97
+ }, [handlePointerMove]);
98
+ const handleTouchStart = useCallback(event => {
99
+ if (!enableScrubbing || !event.touches.length) return;
100
+ // Handle initial touch
101
+ const touch = event.touches[0];
102
+ const target = event.currentTarget;
103
+ handlePointerMove(touch.clientX, target);
104
+ }, [enableScrubbing, handlePointerMove]);
105
+ const handlePointerLeave = useCallback(() => {
106
+ if (!enableScrubbing) return;
107
+ setScrubberPosition(undefined);
108
+ onScrubberPositionChange === null || onScrubberPositionChange === void 0 || onScrubberPositionChange(undefined);
109
+ }, [enableScrubbing, onScrubberPositionChange]);
110
+ const handleMouseLeave = handlePointerLeave;
111
+ const handleTouchEnd = handlePointerLeave;
112
+ const handleKeyDown = useCallback(event => {
113
+ if (!enableScrubbing) return;
114
+ const xScale = getXScale();
115
+ const xAxis = getXAxis();
116
+ if (!xScale || !xAxis) return;
117
+ const isBand = isCategoricalScale(xScale);
118
+
119
+ // Determine the actual data indices we can navigate to
120
+ let minIndex;
121
+ let maxIndex;
122
+ let dataPoints;
123
+ if (isBand) {
124
+ var _ref3, _xScale$domain3, _xScale$domain4;
125
+ // 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 : [];
127
+ minIndex = 0;
128
+ maxIndex = Math.max(0, categories.length - 1);
129
+ dataPoints = categories.length;
130
+ } else {
131
+ // For numeric scales, check if we have specific data points
132
+ const axisData = xAxis.data;
133
+ if (axisData && Array.isArray(axisData)) {
134
+ // We have specific data points - use their indices
135
+ minIndex = 0;
136
+ maxIndex = Math.max(0, axisData.length - 1);
137
+ dataPoints = axisData.length;
138
+ } else {
139
+ var _domain$min2, _domain$max2;
140
+ // Fall back to domain-based navigation for continuous scales without specific data
141
+ const domain = xAxis.domain;
142
+ minIndex = (_domain$min2 = domain.min) !== null && _domain$min2 !== void 0 ? _domain$min2 : 0;
143
+ maxIndex = (_domain$max2 = domain.max) !== null && _domain$max2 !== void 0 ? _domain$max2 : 0;
144
+ dataPoints = maxIndex - minIndex + 1;
145
+ }
146
+ }
147
+ const currentIndex = scrubberPosition !== null && scrubberPosition !== void 0 ? scrubberPosition : minIndex;
148
+ const dataRange = maxIndex - minIndex;
149
+
150
+ // Multi-step jump when shift is held (10% of data range, minimum 1, maximum 10)
151
+ const multiSkip = event.shiftKey;
152
+ const stepSize = multiSkip ? Math.min(10, Math.max(1, Math.floor(dataRange * 0.1))) : 1;
153
+ let newIndex;
154
+ switch (event.key) {
155
+ case 'ArrowLeft':
156
+ event.preventDefault();
157
+ newIndex = Math.max(minIndex, currentIndex - stepSize);
158
+ break;
159
+ case 'ArrowRight':
160
+ event.preventDefault();
161
+ newIndex = Math.min(maxIndex, currentIndex + stepSize);
162
+ break;
163
+ case 'Home':
164
+ event.preventDefault();
165
+ newIndex = minIndex;
166
+ break;
167
+ case 'End':
168
+ event.preventDefault();
169
+ newIndex = maxIndex;
170
+ break;
171
+ case 'Escape':
172
+ event.preventDefault();
173
+ newIndex = undefined; // Clear highlighting
174
+ break;
175
+ default:
176
+ return;
177
+ // Don't handle other keys
178
+ }
179
+ if (newIndex !== scrubberPosition) {
180
+ setScrubberPosition(newIndex);
181
+ onScrubberPositionChange === null || onScrubberPositionChange === void 0 || onScrubberPositionChange(newIndex);
182
+ }
183
+ }, [enableScrubbing, getXScale, getXAxis, scrubberPosition, onScrubberPositionChange]);
184
+ const handleBlur = useCallback(() => {
185
+ if (!enableScrubbing || scrubberPosition === undefined) return;
186
+ setScrubberPosition(undefined);
187
+ onScrubberPositionChange === null || onScrubberPositionChange === void 0 || onScrubberPositionChange(undefined);
188
+ }, [enableScrubbing, onScrubberPositionChange, scrubberPosition]);
189
+
190
+ // Attach event listeners to SVG element
191
+ useEffect(() => {
192
+ if (!(svgRef !== null && svgRef !== void 0 && svgRef.current) || !enableScrubbing) return;
193
+ const svg = svgRef.current;
194
+
195
+ // Add event listeners
196
+ svg.addEventListener('mousemove', handleMouseMove);
197
+ svg.addEventListener('mouseleave', handleMouseLeave);
198
+ svg.addEventListener('touchstart', handleTouchStart, {
199
+ passive: false
200
+ });
201
+ svg.addEventListener('touchmove', handleTouchMove, {
202
+ passive: false
203
+ });
204
+ svg.addEventListener('touchend', handleTouchEnd);
205
+ svg.addEventListener('touchcancel', handleTouchEnd);
206
+ svg.addEventListener('keydown', handleKeyDown);
207
+ svg.addEventListener('blur', handleBlur);
208
+ return () => {
209
+ svg.removeEventListener('mousemove', handleMouseMove);
210
+ svg.removeEventListener('mouseleave', handleMouseLeave);
211
+ svg.removeEventListener('touchstart', handleTouchStart);
212
+ svg.removeEventListener('touchmove', handleTouchMove);
213
+ svg.removeEventListener('touchend', handleTouchEnd);
214
+ svg.removeEventListener('touchcancel', handleTouchEnd);
215
+ svg.removeEventListener('keydown', handleKeyDown);
216
+ svg.removeEventListener('blur', handleBlur);
217
+ };
218
+ }, [svgRef, enableScrubbing, handleMouseMove, handleMouseLeave, handleTouchStart, handleTouchMove, handleTouchEnd, handleKeyDown, handleBlur]);
219
+ const contextValue = useMemo(() => ({
220
+ enableScrubbing: !!enableScrubbing,
221
+ scrubberPosition,
222
+ onScrubberPositionChange: setScrubberPosition
223
+ }), [enableScrubbing, scrubberPosition]);
224
+ return /*#__PURE__*/_jsx(ScrubberContext.Provider, {
225
+ value: contextValue,
226
+ children: children
227
+ });
228
+ };
@@ -0,0 +1,4 @@
1
+ export * from './DefaultScrubberBeacon';
2
+ export * from './DefaultScrubberBeaconLabel';
3
+ export * from './DefaultScrubberLabel';
4
+ export * from './Scrubber';
@@ -0,0 +1,230 @@
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 React, { memo, useEffect, useMemo, useRef, useState } from 'react';
7
+ import { cx } from '@coinbase/cds-web';
8
+ import { Box } from '@coinbase/cds-web/layout';
9
+ import { Text } from '@coinbase/cds-web/typography';
10
+ import { m as motion } from 'framer-motion';
11
+ import { useCartesianChartContext } from '../ChartProvider';
12
+ import { getChartInset } from '../utils';
13
+
14
+ /**
15
+ * The supported content types for ChartText.
16
+ */
17
+
18
+ /**
19
+ * Horizontal alignment options for chart text.
20
+ */
21
+
22
+ /**
23
+ * Vertical alignment options for chart text.
24
+ */
25
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
26
+ /**
27
+ * Get text anchor based on horizontal alignment.
28
+ */
29
+ const getTextAnchor = alignment => {
30
+ switch (alignment) {
31
+ case 'left':
32
+ return 'start';
33
+ case 'center':
34
+ return 'middle';
35
+ case 'right':
36
+ return 'end';
37
+ }
38
+ };
39
+
40
+ /**
41
+ * Get dominant baseline based on vertical alignment.
42
+ */
43
+ const getDominantBaseline = alignment => {
44
+ switch (alignment) {
45
+ case 'top':
46
+ return 'hanging';
47
+ case 'middle':
48
+ return 'central';
49
+ case 'bottom':
50
+ return 'ideographic';
51
+ }
52
+ };
53
+ export const ChartText = /*#__PURE__*/memo(_ref => {
54
+ let {
55
+ children,
56
+ x,
57
+ y,
58
+ horizontalAlignment = 'center',
59
+ verticalAlignment = 'middle',
60
+ dx,
61
+ dy,
62
+ disableRepositioning,
63
+ bounds,
64
+ opacity,
65
+ testID,
66
+ font = 'label2',
67
+ fontFamily,
68
+ fontSize,
69
+ fontWeight,
70
+ elevated,
71
+ color = 'var(--color-fgMuted)',
72
+ background = elevated ? 'var(--color-bg)' : 'transparent',
73
+ borderRadius,
74
+ inset: insetInput,
75
+ onDimensionsChange,
76
+ style,
77
+ styles,
78
+ className,
79
+ classNames
80
+ } = _ref;
81
+ const {
82
+ animate,
83
+ width: chartWidth,
84
+ height: chartHeight
85
+ } = useCartesianChartContext();
86
+ const fullChartBounds = useMemo(() => ({
87
+ x: 0,
88
+ y: 0,
89
+ width: chartWidth,
90
+ height: chartHeight
91
+ }), [chartWidth, chartHeight]);
92
+ const textRef = useRef(null);
93
+ const [textBBox, setTextBBox] = useState(null);
94
+ const isDimensionsReady = disableRepositioning || textRef.current !== null;
95
+ const backgroundRectDimensions = useMemo(() => {
96
+ if (!textBBox) {
97
+ return null;
98
+ }
99
+ const inset = getChartInset(insetInput);
100
+ return {
101
+ x: textBBox.x - inset.left,
102
+ y: textBBox.y - inset.top,
103
+ width: textBBox.width + inset.left + inset.right,
104
+ height: textBBox.height + inset.top + inset.bottom
105
+ };
106
+ }, [textBBox, insetInput]);
107
+ const overflowAmount = useMemo(() => {
108
+ if (disableRepositioning) {
109
+ return {
110
+ x: 0,
111
+ y: 0
112
+ };
113
+ }
114
+ const parentBounds = bounds !== null && bounds !== void 0 ? bounds : fullChartBounds;
115
+ if (!backgroundRectDimensions || !parentBounds || parentBounds.width <= 0 || parentBounds.height <= 0) {
116
+ return {
117
+ x: 0,
118
+ y: 0
119
+ };
120
+ }
121
+ let x = 0;
122
+ let y = 0;
123
+ if (backgroundRectDimensions.x < parentBounds.x) {
124
+ x = parentBounds.x - backgroundRectDimensions.x; // positive = shift right
125
+ } else if (backgroundRectDimensions.x + backgroundRectDimensions.width > parentBounds.x + parentBounds.width) {
126
+ x = parentBounds.x + parentBounds.width - (backgroundRectDimensions.x + backgroundRectDimensions.width); // negative = shift left
127
+ }
128
+ if (backgroundRectDimensions.y < parentBounds.y) {
129
+ y = parentBounds.y - backgroundRectDimensions.y; // positive = shift down
130
+ } else if (backgroundRectDimensions.y + backgroundRectDimensions.height > parentBounds.y + parentBounds.height) {
131
+ y = parentBounds.y + parentBounds.height - (backgroundRectDimensions.y + backgroundRectDimensions.height); // negative = shift up
132
+ }
133
+ return {
134
+ x,
135
+ y
136
+ };
137
+ }, [backgroundRectDimensions, fullChartBounds, bounds, disableRepositioning]);
138
+
139
+ // Compose the final reported rect including any overflow translation applied
140
+ const reportedRect = useMemo(() => {
141
+ if (!backgroundRectDimensions) return null;
142
+ return {
143
+ x: backgroundRectDimensions.x + overflowAmount.x,
144
+ y: backgroundRectDimensions.y + overflowAmount.y,
145
+ width: backgroundRectDimensions.width,
146
+ height: backgroundRectDimensions.height
147
+ };
148
+ }, [backgroundRectDimensions, overflowAmount.x, overflowAmount.y]);
149
+ useEffect(() => {
150
+ if (onDimensionsChange && reportedRect !== null) {
151
+ onDimensionsChange(reportedRect);
152
+ }
153
+ }, [reportedRect, onDimensionsChange]);
154
+ useEffect(() => {
155
+ if (textRef.current) {
156
+ const observer = new ResizeObserver(entries => {
157
+ for (const entry of entries) {
158
+ setTextBBox(entry.target.getBBox());
159
+ }
160
+ });
161
+ observer.observe(textRef.current);
162
+
163
+ // Cleanup function
164
+ return () => {
165
+ observer.disconnect();
166
+ };
167
+ }
168
+ }, []);
169
+ const textAnchor = useMemo(() => getTextAnchor(horizontalAlignment), [horizontalAlignment]);
170
+ const dominantBaseline = useMemo(() => getDominantBaseline(verticalAlignment), [verticalAlignment]);
171
+
172
+ // forces state update the bounding box when any properties that can affect the bounding box change
173
+ useEffect(() => {
174
+ if (textRef.current) {
175
+ setTextBBox(textRef.current.getBBox());
176
+ }
177
+ }, [textAnchor, dominantBaseline, dx, dy, x, y]);
178
+ const containerStyle = useMemo(() => _objectSpread(_objectSpread(_objectSpread({}, style), styles === null || styles === void 0 ? void 0 : styles.root), {}, {
179
+ transform: "translate(".concat(overflowAmount.x, "px, ").concat(overflowAmount.y, "px)")
180
+ }), [overflowAmount.x, overflowAmount.y, style, styles === null || styles === void 0 ? void 0 : styles.root]);
181
+ return /*#__PURE__*/_jsx(Box, {
182
+ "aria-hidden": "true",
183
+ as: "g",
184
+ className: cx(className, classNames === null || classNames === void 0 ? void 0 : classNames.root),
185
+ opacity: opacity,
186
+ style: containerStyle,
187
+ testID: testID,
188
+ children: /*#__PURE__*/_jsxs(motion.g, {
189
+ animate: {
190
+ opacity: isDimensionsReady ? 1 : 0
191
+ },
192
+ transition: animate ? {
193
+ duration: 0.2,
194
+ ease: 'easeOut'
195
+ } : undefined,
196
+ children: [/*#__PURE__*/_jsx(Box, {
197
+ as: "rect",
198
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.backgroundRect,
199
+ fill: background,
200
+ filter: elevated ? 'drop-shadow(var(--shadow-elevation1))' : undefined,
201
+ height: backgroundRectDimensions === null || backgroundRectDimensions === void 0 ? void 0 : backgroundRectDimensions.height,
202
+ rx: borderRadius,
203
+ ry: borderRadius,
204
+ style: styles === null || styles === void 0 ? void 0 : styles.backgroundRect,
205
+ width: backgroundRectDimensions === null || backgroundRectDimensions === void 0 ? void 0 : backgroundRectDimensions.width,
206
+ x: backgroundRectDimensions === null || backgroundRectDimensions === void 0 ? void 0 : backgroundRectDimensions.x,
207
+ y: backgroundRectDimensions === null || backgroundRectDimensions === void 0 ? void 0 : backgroundRectDimensions.y
208
+ }), /*#__PURE__*/_jsx(Text, {
209
+ ref: textRef,
210
+ as: "text",
211
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.text,
212
+ dominantBaseline: dominantBaseline,
213
+ dx: dx,
214
+ dy: dy,
215
+ fill: color,
216
+ font: font,
217
+ fontFamily: fontFamily,
218
+ fontSize: fontSize,
219
+ fontWeight: fontWeight,
220
+ style: styles === null || styles === void 0 ? void 0 : styles.text,
221
+ textAnchor: textAnchor,
222
+ x: x,
223
+ y: y,
224
+ children: /*#__PURE__*/_jsx("tspan", {
225
+ children: children
226
+ })
227
+ })]
228
+ })
229
+ });
230
+ });