@coinbase/cds-web-visualization 3.3.0 → 3.4.0-beta.1
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.
- package/CHANGELOG.md +26 -0
- package/dts/chart/CartesianChart.d.ts +36 -0
- package/dts/chart/CartesianChart.d.ts.map +1 -0
- package/dts/chart/ChartProvider.d.ts +6 -0
- package/dts/chart/ChartProvider.d.ts.map +1 -0
- package/dts/chart/Path.d.ts +34 -0
- package/dts/chart/Path.d.ts.map +1 -0
- package/dts/chart/PeriodSelector.d.ts +61 -0
- package/dts/chart/PeriodSelector.d.ts.map +1 -0
- package/dts/chart/Point.d.ts +153 -0
- package/dts/chart/Point.d.ts.map +1 -0
- package/dts/chart/area/Area.d.ts +48 -0
- package/dts/chart/area/Area.d.ts.map +1 -0
- package/dts/chart/area/AreaChart.d.ts +52 -0
- package/dts/chart/area/AreaChart.d.ts.map +1 -0
- package/dts/chart/area/DottedArea.d.ts +68 -0
- package/dts/chart/area/DottedArea.d.ts.map +1 -0
- package/dts/chart/area/GradientArea.d.ts +30 -0
- package/dts/chart/area/GradientArea.d.ts.map +1 -0
- package/dts/chart/area/SolidArea.d.ts +8 -0
- package/dts/chart/area/SolidArea.d.ts.map +1 -0
- package/dts/chart/area/index.d.ts +6 -0
- package/dts/chart/area/index.d.ts.map +1 -0
- package/dts/chart/axis/Axis.d.ts +208 -0
- package/dts/chart/axis/Axis.d.ts.map +1 -0
- package/dts/chart/axis/XAxis.d.ts +16 -0
- package/dts/chart/axis/XAxis.d.ts.map +1 -0
- package/dts/chart/axis/YAxis.d.ts +21 -0
- package/dts/chart/axis/YAxis.d.ts.map +1 -0
- package/dts/chart/axis/index.d.ts +4 -0
- package/dts/chart/axis/index.d.ts.map +1 -0
- package/dts/chart/bar/Bar.d.ts +91 -0
- package/dts/chart/bar/Bar.d.ts.map +1 -0
- package/dts/chart/bar/BarChart.d.ts +53 -0
- package/dts/chart/bar/BarChart.d.ts.map +1 -0
- package/dts/chart/bar/BarPlot.d.ts +29 -0
- package/dts/chart/bar/BarPlot.d.ts.map +1 -0
- package/dts/chart/bar/BarStack.d.ts +111 -0
- package/dts/chart/bar/BarStack.d.ts.map +1 -0
- package/dts/chart/bar/BarStackGroup.d.ts +35 -0
- package/dts/chart/bar/BarStackGroup.d.ts.map +1 -0
- package/dts/chart/bar/DefaultBar.d.ts +17 -0
- package/dts/chart/bar/DefaultBar.d.ts.map +1 -0
- package/dts/chart/bar/DefaultBarStack.d.ts +16 -0
- package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -0
- package/dts/chart/bar/index.d.ts +8 -0
- package/dts/chart/bar/index.d.ts.map +1 -0
- package/dts/chart/index.d.ts +13 -0
- package/dts/chart/index.d.ts.map +1 -0
- package/dts/chart/line/DottedLine.d.ts +14 -0
- package/dts/chart/line/DottedLine.d.ts.map +1 -0
- package/dts/chart/line/GradientLine.d.ts +42 -0
- package/dts/chart/line/GradientLine.d.ts.map +1 -0
- package/dts/chart/line/Line.d.ts +80 -0
- package/dts/chart/line/Line.d.ts.map +1 -0
- package/dts/chart/line/LineChart.d.ts +59 -0
- package/dts/chart/line/LineChart.d.ts.map +1 -0
- package/dts/chart/line/ReferenceLine.d.ts +131 -0
- package/dts/chart/line/ReferenceLine.d.ts.map +1 -0
- package/dts/chart/line/SolidLine.d.ts +14 -0
- package/dts/chart/line/SolidLine.d.ts.map +1 -0
- package/dts/chart/line/index.d.ts +7 -0
- package/dts/chart/line/index.d.ts.map +1 -0
- package/dts/chart/scrubber/Scrubber.d.ts +149 -0
- package/dts/chart/scrubber/Scrubber.d.ts.map +1 -0
- package/dts/chart/scrubber/ScrubberBeacon.d.ts +93 -0
- package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +1 -0
- package/dts/chart/scrubber/ScrubberBeaconLabel.d.ts +7 -0
- package/dts/chart/scrubber/ScrubberBeaconLabel.d.ts.map +1 -0
- package/dts/chart/scrubber/ScrubberProvider.d.ts +17 -0
- package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -0
- package/dts/chart/scrubber/index.d.ts +2 -0
- package/dts/chart/scrubber/index.d.ts.map +1 -0
- package/dts/chart/text/ChartText.d.ts +114 -0
- package/dts/chart/text/ChartText.d.ts.map +1 -0
- package/dts/chart/text/SmartChartTextGroup.d.ts +55 -0
- package/dts/chart/text/SmartChartTextGroup.d.ts.map +1 -0
- package/dts/chart/text/index.d.ts +3 -0
- package/dts/chart/text/index.d.ts.map +1 -0
- package/dts/chart/utils/axis.d.ts +342 -0
- package/dts/chart/utils/axis.d.ts.map +1 -0
- package/dts/chart/utils/bar.d.ts +20 -0
- package/dts/chart/utils/bar.d.ts.map +1 -0
- package/dts/chart/utils/chart.d.ts +97 -0
- package/dts/chart/utils/chart.d.ts.map +1 -0
- package/dts/chart/utils/context.d.ts +95 -0
- package/dts/chart/utils/context.d.ts.map +1 -0
- package/dts/chart/utils/index.d.ts +8 -0
- package/dts/chart/utils/index.d.ts.map +1 -0
- package/dts/chart/utils/path.d.ts +107 -0
- package/dts/chart/utils/path.d.ts.map +1 -0
- package/dts/chart/utils/point.d.ts +75 -0
- package/dts/chart/utils/point.d.ts.map +1 -0
- package/dts/chart/utils/scale.d.ts +43 -0
- package/dts/chart/utils/scale.d.ts.map +1 -0
- package/dts/index.d.ts +2 -1
- package/dts/index.d.ts.map +1 -1
- package/dts/sparkline/Counter.d.ts +7 -2
- package/dts/sparkline/Sparkline.d.ts +61 -16
- package/dts/sparkline/Sparkline.d.ts.map +1 -1
- package/dts/sparkline/SparklineArea.d.ts +12 -4
- package/dts/sparkline/SparklineArea.d.ts.map +1 -1
- package/dts/sparkline/SparklineAreaPattern.d.ts +12 -4
- package/dts/sparkline/SparklineAreaPattern.d.ts.map +1 -1
- package/dts/sparkline/SparklineGradient.d.ts +21 -10
- package/dts/sparkline/SparklineGradient.d.ts.map +1 -1
- package/dts/sparkline/SparklinePath.d.ts +8 -6
- package/dts/sparkline/__figma__/Sparkline.figma.d.ts +1 -1
- package/dts/sparkline/generateSparklineWithId.d.ts +8 -2
- package/dts/sparkline/generateSparklineWithId.d.ts.map +1 -1
- package/dts/sparkline/index.d.ts +1 -1
- package/dts/sparkline/sparkline-interactive/InnerSparklineInteractiveProvider.d.ts +9 -5
- package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts +168 -118
- package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts.map +1 -1
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.d.ts +22 -9
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.d.ts.map +1 -1
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveHoverDate.d.ts +4 -2
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveHoverPrice.d.ts +4 -2
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveLineVertical.d.ts +5 -3
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveMarkerDates.d.ts +11 -6
- package/dts/sparkline/sparkline-interactive/SparklineInteractivePaths.d.ts +21 -7
- package/dts/sparkline/sparkline-interactive/SparklineInteractivePaths.d.ts.map +1 -1
- package/dts/sparkline/sparkline-interactive/SparklineInteractivePeriodSelector.d.ts +21 -16
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveProvider.d.ts +17 -12
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveScrubHandler.d.ts +23 -10
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveScrubProvider.d.ts +12 -12
- package/dts/sparkline/sparkline-interactive/SparklineInteractiveTimeseriesPaths.d.ts +22 -14
- package/dts/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.d.ts +1 -1
- package/dts/sparkline/sparkline-interactive/fade.d.ts +1 -1
- package/dts/sparkline/sparkline-interactive/useSparklineInteractiveConstants.d.ts +11 -11
- package/dts/sparkline/sparkline-interactive-header/SparklineInteractiveHeader.d.ts +101 -93
- package/dts/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.d.ts +1 -1
- package/esm/chart/CartesianChart.css +1 -0
- package/esm/chart/CartesianChart.js +258 -0
- package/esm/chart/ChartProvider.js +10 -0
- package/esm/chart/Path.js +89 -0
- package/esm/chart/PeriodSelector.css +1 -0
- package/esm/chart/PeriodSelector.js +126 -0
- package/esm/chart/Point.css +2 -0
- package/esm/chart/Point.js +171 -0
- package/esm/chart/area/Area.js +85 -0
- package/esm/chart/area/AreaChart.js +164 -0
- package/esm/chart/area/DottedArea.js +141 -0
- package/esm/chart/area/GradientArea.js +111 -0
- package/esm/chart/area/SolidArea.js +29 -0
- package/esm/chart/area/index.js +7 -0
- package/esm/chart/axis/Axis.js +46 -0
- package/esm/chart/axis/XAxis.css +2 -0
- package/esm/chart/axis/XAxis.js +195 -0
- package/esm/chart/axis/YAxis.css +2 -0
- package/esm/chart/axis/YAxis.js +183 -0
- package/esm/chart/axis/index.js +5 -0
- package/esm/chart/bar/Bar.js +59 -0
- package/esm/chart/bar/BarChart.js +147 -0
- package/esm/chart/bar/BarPlot.js +96 -0
- package/esm/chart/bar/BarStack.js +519 -0
- package/esm/chart/bar/BarStackGroup.js +96 -0
- package/esm/chart/bar/DefaultBar.js +64 -0
- package/esm/chart/bar/DefaultBarStack.js +60 -0
- package/esm/chart/bar/index.js +9 -0
- package/esm/chart/index.js +14 -0
- package/esm/chart/line/DottedLine.js +38 -0
- package/esm/chart/line/GradientLine.js +58 -0
- package/esm/chart/line/Line.js +159 -0
- package/esm/chart/line/LineChart.js +120 -0
- package/esm/chart/line/ReferenceLine.js +142 -0
- package/esm/chart/line/SolidLine.js +34 -0
- package/esm/chart/line/index.js +8 -0
- package/esm/chart/scrubber/Scrubber.js +483 -0
- package/esm/chart/scrubber/ScrubberBeacon.js +195 -0
- package/esm/chart/scrubber/ScrubberBeaconLabel.js +33 -0
- package/esm/chart/scrubber/ScrubberProvider.js +228 -0
- package/esm/chart/scrubber/index.js +2 -0
- package/esm/chart/text/ChartText.js +236 -0
- package/esm/chart/text/SmartChartTextGroup.js +226 -0
- package/esm/chart/text/index.js +4 -0
- package/esm/chart/utils/axis.js +593 -0
- package/esm/chart/utils/bar.js +24 -0
- package/esm/chart/utils/chart.js +229 -0
- package/esm/chart/utils/context.js +15 -0
- package/esm/chart/utils/index.js +9 -0
- package/esm/chart/utils/path.js +204 -0
- package/esm/chart/utils/point.js +118 -0
- package/esm/chart/utils/scale.js +48 -0
- package/esm/index.js +4 -1
- package/esm/sparkline/Sparkline.js +129 -15
- package/esm/sparkline/SparklineArea.js +7 -2
- package/esm/sparkline/SparklineAreaPattern.js +4 -2
- package/esm/sparkline/SparklineGradient.js +16 -58
- package/esm/sparkline/generateSparklineWithId.js +3 -2
- package/esm/sparkline/sparkline-interactive/SparklineInteractive.js +5 -1
- package/esm/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.js +5 -2
- package/esm/sparkline/sparkline-interactive/SparklineInteractivePaths.js +4 -0
- package/package.json +15 -11
- package/dts/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.d.ts +0 -374
- package/dts/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractive.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractive.test.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractivePeriodSelector.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive/__tests__/SparklineInteractivePeriodSelector.test.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.d.ts +0 -87
- package/dts/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.d.ts.map +0 -1
- package/dts/sparkline/sparkline-interactive-header/__tests__/SparklineInteractiveHeader.test.d.ts +0 -2
- package/dts/sparkline/sparkline-interactive-header/__tests__/SparklineInteractiveHeader.test.d.ts.map +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const _excluded = ["background", "color", "elevation", "borderRadius", "font", "verticalAlignment"];
|
|
2
|
+
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; }
|
|
3
|
+
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; }
|
|
4
|
+
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; }
|
|
5
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
6
|
+
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); }
|
|
7
|
+
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
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
|
+
import { memo } from 'react';
|
|
10
|
+
import { ChartText } from '../text';
|
|
11
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
12
|
+
/**
|
|
13
|
+
* The ScrubberBeaconLabel is a special instance of ChartText used to label a series' scrubber beacon (i.e. a point on the series pinned to the scrubber position).
|
|
14
|
+
*/
|
|
15
|
+
export const ScrubberBeaconLabel = /*#__PURE__*/memo(_ref => {
|
|
16
|
+
let {
|
|
17
|
+
background = 'white',
|
|
18
|
+
color = 'var(--color-fgPrimary)',
|
|
19
|
+
elevation = background !== undefined ? 1 : undefined,
|
|
20
|
+
borderRadius = background !== undefined ? 4 : undefined,
|
|
21
|
+
font = 'label1',
|
|
22
|
+
verticalAlignment = 'middle'
|
|
23
|
+
} = _ref,
|
|
24
|
+
chartTextProps = _objectWithoutProperties(_ref, _excluded);
|
|
25
|
+
return /*#__PURE__*/_jsx(ChartText, _objectSpread({
|
|
26
|
+
background: background,
|
|
27
|
+
borderRadius: borderRadius,
|
|
28
|
+
color: color,
|
|
29
|
+
elevation: elevation,
|
|
30
|
+
font: font,
|
|
31
|
+
verticalAlignment: verticalAlignment
|
|
32
|
+
}, chartTextProps));
|
|
33
|
+
});
|
|
@@ -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,236 @@
|
|
|
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
|
+
elevation,
|
|
71
|
+
color = 'var(--color-fgMuted)',
|
|
72
|
+
background = elevation && elevation > 0 ? '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 (!textBBox || !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
|
+
|
|
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
|
|
129
|
+
}
|
|
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
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
x,
|
|
139
|
+
y
|
|
140
|
+
};
|
|
141
|
+
}, [textBBox, fullChartBounds, bounds, disableRepositioning]);
|
|
142
|
+
|
|
143
|
+
// Compose the final reported rect including any overflow translation applied
|
|
144
|
+
const reportedRect = useMemo(() => {
|
|
145
|
+
if (!backgroundRectDimensions) return null;
|
|
146
|
+
return {
|
|
147
|
+
x: backgroundRectDimensions.x + overflowAmount.x,
|
|
148
|
+
y: backgroundRectDimensions.y + overflowAmount.y,
|
|
149
|
+
width: backgroundRectDimensions.width,
|
|
150
|
+
height: backgroundRectDimensions.height
|
|
151
|
+
};
|
|
152
|
+
}, [backgroundRectDimensions, overflowAmount.x, overflowAmount.y]);
|
|
153
|
+
|
|
154
|
+
// send latest calculated dimensions (adjusted for translation) to parent
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (onDimensionsChange && reportedRect !== null) {
|
|
157
|
+
onDimensionsChange(reportedRect);
|
|
158
|
+
}
|
|
159
|
+
}, [reportedRect, onDimensionsChange]);
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (textRef.current) {
|
|
162
|
+
const observer = new ResizeObserver(entries => {
|
|
163
|
+
for (const entry of entries) {
|
|
164
|
+
setTextBBox(entry.target.getBBox());
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
observer.observe(textRef.current);
|
|
168
|
+
|
|
169
|
+
// Cleanup function
|
|
170
|
+
return () => {
|
|
171
|
+
observer.disconnect();
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}, []);
|
|
175
|
+
const textAnchor = useMemo(() => getTextAnchor(horizontalAlignment), [horizontalAlignment]);
|
|
176
|
+
const dominantBaseline = useMemo(() => getDominantBaseline(verticalAlignment), [verticalAlignment]);
|
|
177
|
+
|
|
178
|
+
// forces state update the bounding box when any properties that can affect the bounding box change
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
if (textRef.current) {
|
|
181
|
+
setTextBBox(textRef.current.getBBox());
|
|
182
|
+
}
|
|
183
|
+
}, [textAnchor, dominantBaseline, dx, dy, x, y]);
|
|
184
|
+
const containerStyle = useMemo(() => _objectSpread(_objectSpread(_objectSpread({}, style), styles === null || styles === void 0 ? void 0 : styles.root), {}, {
|
|
185
|
+
transform: "translate(".concat(overflowAmount.x, "px, ").concat(overflowAmount.y, "px)")
|
|
186
|
+
}), [overflowAmount.x, overflowAmount.y, style, styles === null || styles === void 0 ? void 0 : styles.root]);
|
|
187
|
+
return /*#__PURE__*/_jsx(Box, {
|
|
188
|
+
"aria-hidden": "true",
|
|
189
|
+
as: "g",
|
|
190
|
+
className: cx(className, classNames === null || classNames === void 0 ? void 0 : classNames.root),
|
|
191
|
+
opacity: opacity,
|
|
192
|
+
style: containerStyle,
|
|
193
|
+
testID: testID,
|
|
194
|
+
children: /*#__PURE__*/_jsxs(motion.g, {
|
|
195
|
+
animate: {
|
|
196
|
+
opacity: isDimensionsReady ? 1 : 0
|
|
197
|
+
},
|
|
198
|
+
transition: animate ? {
|
|
199
|
+
duration: 0.2,
|
|
200
|
+
ease: 'easeOut'
|
|
201
|
+
} : undefined,
|
|
202
|
+
children: [/*#__PURE__*/_jsx(Box, {
|
|
203
|
+
as: "rect",
|
|
204
|
+
className: classNames === null || classNames === void 0 ? void 0 : classNames.backgroundRect,
|
|
205
|
+
fill: background,
|
|
206
|
+
filter: elevation && elevation > 0 ? "drop-shadow(var(--shadow-elevation".concat(elevation, "))") : undefined,
|
|
207
|
+
height: backgroundRectDimensions === null || backgroundRectDimensions === void 0 ? void 0 : backgroundRectDimensions.height,
|
|
208
|
+
rx: borderRadius,
|
|
209
|
+
ry: borderRadius,
|
|
210
|
+
style: styles === null || styles === void 0 ? void 0 : styles.backgroundRect,
|
|
211
|
+
width: backgroundRectDimensions === null || backgroundRectDimensions === void 0 ? void 0 : backgroundRectDimensions.width,
|
|
212
|
+
x: backgroundRectDimensions === null || backgroundRectDimensions === void 0 ? void 0 : backgroundRectDimensions.x,
|
|
213
|
+
y: backgroundRectDimensions === null || backgroundRectDimensions === void 0 ? void 0 : backgroundRectDimensions.y
|
|
214
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
215
|
+
ref: textRef,
|
|
216
|
+
as: "text",
|
|
217
|
+
className: classNames === null || classNames === void 0 ? void 0 : classNames.text,
|
|
218
|
+
dominantBaseline: dominantBaseline,
|
|
219
|
+
dx: dx,
|
|
220
|
+
dy: dy,
|
|
221
|
+
fill: color,
|
|
222
|
+
font: font,
|
|
223
|
+
fontFamily: fontFamily,
|
|
224
|
+
fontSize: fontSize,
|
|
225
|
+
fontWeight: fontWeight,
|
|
226
|
+
style: styles === null || styles === void 0 ? void 0 : styles.text,
|
|
227
|
+
textAnchor: textAnchor,
|
|
228
|
+
x: x,
|
|
229
|
+
y: y,
|
|
230
|
+
children: /*#__PURE__*/_jsx("tspan", {
|
|
231
|
+
children: children
|
|
232
|
+
})
|
|
233
|
+
})]
|
|
234
|
+
})
|
|
235
|
+
});
|
|
236
|
+
});
|