@coinbase/cds-mobile-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.
- package/CHANGELOG.md +128 -0
- package/dts/chart/CartesianChart.d.ts +92 -34
- package/dts/chart/CartesianChart.d.ts.map +1 -1
- package/dts/chart/ChartContextBridge.d.ts +28 -0
- package/dts/chart/ChartContextBridge.d.ts.map +1 -0
- package/dts/chart/ChartProvider.d.ts +3 -0
- package/dts/chart/ChartProvider.d.ts.map +1 -1
- package/dts/chart/Path.d.ts +97 -32
- package/dts/chart/Path.d.ts.map +1 -1
- package/dts/chart/PeriodSelector.d.ts +6 -13
- package/dts/chart/PeriodSelector.d.ts.map +1 -1
- package/dts/chart/area/Area.d.ts +39 -28
- package/dts/chart/area/Area.d.ts.map +1 -1
- package/dts/chart/area/AreaChart.d.ts +51 -10
- package/dts/chart/area/AreaChart.d.ts.map +1 -1
- package/dts/chart/area/DottedArea.d.ts +21 -2
- package/dts/chart/area/DottedArea.d.ts.map +1 -1
- package/dts/chart/area/GradientArea.d.ts +19 -13
- package/dts/chart/area/GradientArea.d.ts.map +1 -1
- package/dts/chart/area/SolidArea.d.ts +17 -2
- package/dts/chart/area/SolidArea.d.ts.map +1 -1
- package/dts/chart/axis/Axis.d.ts +86 -118
- package/dts/chart/axis/Axis.d.ts.map +1 -1
- package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
- package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
- package/dts/chart/axis/XAxis.d.ts +1 -1
- package/dts/chart/axis/XAxis.d.ts.map +1 -1
- package/dts/chart/axis/YAxis.d.ts +2 -2
- package/dts/chart/axis/YAxis.d.ts.map +1 -1
- package/dts/chart/axis/index.d.ts +1 -0
- package/dts/chart/axis/index.d.ts.map +1 -1
- package/dts/chart/bar/Bar.d.ts +49 -12
- package/dts/chart/bar/Bar.d.ts.map +1 -1
- package/dts/chart/bar/BarChart.d.ts +40 -19
- package/dts/chart/bar/BarChart.d.ts.map +1 -1
- package/dts/chart/bar/BarPlot.d.ts +3 -1
- package/dts/chart/bar/BarPlot.d.ts.map +1 -1
- package/dts/chart/bar/BarStack.d.ts +41 -46
- package/dts/chart/bar/BarStack.d.ts.map +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts +2 -0
- package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
- package/dts/chart/bar/DefaultBar.d.ts +1 -1
- package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
- package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
- package/dts/chart/gradient/Gradient.d.ts +25 -0
- package/dts/chart/gradient/Gradient.d.ts.map +1 -0
- package/dts/chart/gradient/index.d.ts +2 -0
- package/dts/chart/gradient/index.d.ts.map +1 -0
- package/dts/chart/index.d.ts +4 -1
- package/dts/chart/index.d.ts.map +1 -1
- package/dts/chart/legend/DefaultLegendEntry.d.ts +5 -0
- package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
- package/dts/chart/legend/DefaultLegendShape.d.ts +5 -0
- package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
- package/dts/chart/legend/Legend.d.ts +168 -0
- package/dts/chart/legend/Legend.d.ts.map +1 -0
- package/dts/chart/legend/index.d.ts +4 -0
- package/dts/chart/legend/index.d.ts.map +1 -0
- package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
- package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
- package/dts/chart/line/DottedLine.d.ts +13 -5
- package/dts/chart/line/DottedLine.d.ts.map +1 -1
- package/dts/chart/line/Line.d.ts +61 -27
- package/dts/chart/line/Line.d.ts.map +1 -1
- package/dts/chart/line/LineChart.d.ts +43 -9
- package/dts/chart/line/LineChart.d.ts.map +1 -1
- package/dts/chart/line/ReferenceLine.d.ts +68 -20
- package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
- package/dts/chart/line/SolidLine.d.ts +8 -5
- package/dts/chart/line/SolidLine.d.ts.map +1 -1
- package/dts/chart/line/index.d.ts +1 -1
- package/dts/chart/line/index.d.ts.map +1 -1
- package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
- package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
- package/dts/chart/point/Point.d.ts +136 -0
- package/dts/chart/point/Point.d.ts.map +1 -0
- package/dts/chart/point/index.d.ts +3 -0
- package/dts/chart/point/index.d.ts.map +1 -0
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +38 -0
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
- package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
- package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +11 -0
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
- package/dts/chart/scrubber/Scrubber.d.ts +230 -42
- package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +54 -0
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
- package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +46 -0
- package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
- package/dts/chart/scrubber/ScrubberProvider.d.ts +6 -3
- package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
- package/dts/chart/scrubber/index.d.ts +3 -0
- package/dts/chart/scrubber/index.d.ts.map +1 -1
- package/dts/chart/text/ChartText.d.ts +151 -77
- package/dts/chart/text/ChartText.d.ts.map +1 -1
- package/dts/chart/text/{SmartChartTextGroup.d.ts → ChartTextGroup.d.ts} +9 -3
- package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
- package/dts/chart/text/index.d.ts +1 -1
- package/dts/chart/text/index.d.ts.map +1 -1
- package/dts/chart/utils/axis.d.ts +25 -1
- package/dts/chart/utils/axis.d.ts.map +1 -1
- package/dts/chart/utils/bar.d.ts +34 -0
- package/dts/chart/utils/bar.d.ts.map +1 -1
- package/dts/chart/utils/chart.d.ts +52 -7
- package/dts/chart/utils/chart.d.ts.map +1 -1
- package/dts/chart/utils/context.d.ts +28 -7
- package/dts/chart/utils/context.d.ts.map +1 -1
- package/dts/chart/utils/gradient.d.ts +117 -0
- package/dts/chart/utils/gradient.d.ts.map +1 -0
- package/dts/chart/utils/index.d.ts +3 -0
- package/dts/chart/utils/index.d.ts.map +1 -1
- package/dts/chart/utils/path.d.ts +59 -0
- package/dts/chart/utils/path.d.ts.map +1 -1
- package/dts/chart/utils/point.d.ts +71 -7
- package/dts/chart/utils/point.d.ts.map +1 -1
- package/dts/chart/utils/scale.d.ts +102 -0
- package/dts/chart/utils/scale.d.ts.map +1 -1
- package/dts/chart/utils/scrubber.d.ts +40 -0
- package/dts/chart/utils/scrubber.d.ts.map +1 -0
- package/dts/chart/utils/transition.d.ts +178 -0
- package/dts/chart/utils/transition.d.ts.map +1 -0
- package/esm/chart/CartesianChart.js +199 -75
- package/esm/chart/ChartContextBridge.js +159 -0
- package/esm/chart/ChartProvider.js +2 -2
- package/esm/chart/Path.js +200 -114
- package/esm/chart/PeriodSelector.js +7 -3
- package/esm/chart/__stories__/CartesianChart.stories.js +307 -134
- package/esm/chart/__stories__/ChartTransitions.stories.js +629 -0
- package/esm/chart/__stories__/PeriodSelector.stories.js +201 -75
- package/esm/chart/area/Area.js +27 -35
- package/esm/chart/area/AreaChart.js +17 -12
- package/esm/chart/area/DottedArea.js +64 -108
- package/esm/chart/area/GradientArea.js +37 -91
- package/esm/chart/area/SolidArea.js +24 -8
- package/esm/chart/area/__stories__/AreaChart.stories.js +1 -1
- package/esm/chart/axis/Axis.js +5 -39
- package/esm/chart/axis/DefaultAxisTickLabel.js +11 -0
- package/esm/chart/axis/XAxis.js +148 -66
- package/esm/chart/axis/YAxis.js +149 -65
- package/esm/chart/axis/__stories__/Axis.stories.js +259 -1
- package/esm/chart/axis/index.js +1 -0
- package/esm/chart/bar/Bar.js +7 -1
- package/esm/chart/bar/BarChart.js +17 -37
- package/esm/chart/bar/BarPlot.js +43 -35
- package/esm/chart/bar/BarStack.js +84 -37
- package/esm/chart/bar/BarStackGroup.js +7 -17
- package/esm/chart/bar/DefaultBar.js +29 -51
- package/esm/chart/bar/DefaultBarStack.js +34 -58
- package/esm/chart/bar/__stories__/BarChart.stories.js +948 -88
- package/esm/chart/gradient/Gradient.js +53 -0
- package/esm/chart/gradient/index.js +1 -0
- package/esm/chart/index.js +4 -1
- package/esm/chart/legend/DefaultLegendEntry.js +42 -0
- package/esm/chart/legend/DefaultLegendShape.js +64 -0
- package/esm/chart/legend/Legend.js +59 -0
- package/esm/chart/legend/__stories__/Legend.stories.js +574 -0
- package/esm/chart/legend/index.js +3 -0
- package/esm/chart/line/DefaultReferenceLineLabel.js +66 -0
- package/esm/chart/line/DottedLine.js +31 -14
- package/esm/chart/line/Line.js +96 -68
- package/esm/chart/line/LineChart.js +21 -14
- package/esm/chart/line/ReferenceLine.js +80 -63
- package/esm/chart/line/SolidLine.js +27 -10
- package/esm/chart/line/__stories__/LineChart.stories.js +1748 -2048
- package/esm/chart/line/__stories__/ReferenceLine.stories.js +177 -28
- package/esm/chart/line/index.js +1 -1
- package/esm/chart/point/DefaultPointLabel.js +39 -0
- package/esm/chart/point/Point.js +186 -0
- package/esm/chart/point/index.js +2 -0
- package/esm/chart/scrubber/DefaultScrubberBeacon.js +180 -0
- package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +43 -0
- package/esm/chart/scrubber/DefaultScrubberLabel.js +28 -0
- package/esm/chart/scrubber/Scrubber.js +130 -144
- package/esm/chart/scrubber/ScrubberBeaconGroup.js +165 -0
- package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +208 -0
- package/esm/chart/scrubber/ScrubberProvider.js +46 -54
- package/esm/chart/scrubber/__stories__/Scrubber.stories.js +760 -0
- package/esm/chart/scrubber/index.js +3 -1
- package/esm/chart/text/ChartText.js +242 -174
- package/esm/chart/text/{SmartChartTextGroup.js → ChartTextGroup.js} +6 -5
- package/esm/chart/text/index.js +1 -1
- package/esm/chart/utils/axis.js +47 -31
- package/esm/chart/utils/bar.js +43 -0
- package/esm/chart/utils/chart.js +57 -3
- package/esm/chart/utils/gradient.js +305 -0
- package/esm/chart/utils/index.js +3 -0
- package/esm/chart/utils/path.js +84 -8
- package/esm/chart/utils/point.js +171 -17
- package/esm/chart/utils/scale.js +242 -2
- package/esm/chart/utils/scrubber.js +146 -0
- package/esm/chart/utils/transition.js +215 -0
- package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
- package/esm/sparkline/__stories__/Sparkline.stories.js +11 -7
- package/esm/sparkline/__stories__/SparklineGradient.stories.js +7 -4
- package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
- package/esm/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.js +51 -26
- package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
- package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +17 -9
- package/package.json +15 -10
- package/dts/chart/Point.d.ts +0 -103
- package/dts/chart/Point.d.ts.map +0 -1
- package/dts/chart/line/GradientLine.d.ts +0 -45
- package/dts/chart/line/GradientLine.d.ts.map +0 -1
- package/dts/chart/scrubber/ScrubberBeacon.d.ts +0 -75
- package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +0 -1
- package/dts/chart/text/SmartChartTextGroup.d.ts.map +0 -1
- package/esm/chart/Point.js +0 -111
- package/esm/chart/__stories__/Chart.stories.js +0 -79
- package/esm/chart/line/GradientLine.js +0 -62
- package/esm/chart/scrubber/ScrubberBeacon.js +0 -199
package/esm/chart/utils/bar.js
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
const _excluded = ["staggerDelay"];
|
|
2
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
3
|
+
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; }
|
|
4
|
+
import { defaultTransition } from './transition';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A bar-specific transition that extends Transition with stagger support.
|
|
8
|
+
* When `staggerDelay` is provided, bars will animate with increasing delays
|
|
9
|
+
* based on their horizontal position (leftmost starts first, rightmost last).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Bars stagger in from left to right over 250ms, each animating for 750ms
|
|
13
|
+
* { type: 'timing', duration: 750, staggerDelay: 250 }
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Strips `staggerDelay` from a transition and computes a positional delay.
|
|
18
|
+
*
|
|
19
|
+
* @param transition - The transition config (may include staggerDelay)
|
|
20
|
+
* @param normalizedX - The bar's normalized x position (0 = left edge, 1 = right edge)
|
|
21
|
+
* @returns A standard Transition with computed delay
|
|
22
|
+
*/
|
|
23
|
+
export const withStaggerDelayTransition = (transition, normalizedX) => {
|
|
24
|
+
var _baseTransition$delay;
|
|
25
|
+
const {
|
|
26
|
+
staggerDelay
|
|
27
|
+
} = transition,
|
|
28
|
+
baseTransition = _objectWithoutPropertiesLoose(transition, _excluded);
|
|
29
|
+
if (!staggerDelay) return transition;
|
|
30
|
+
return _extends({}, baseTransition, {
|
|
31
|
+
delay: ((_baseTransition$delay = baseTransition == null ? void 0 : baseTransition.delay) != null ? _baseTransition$delay : 0) + normalizedX * staggerDelay
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Default bar enter transition. Uses the default spring with a stagger delay
|
|
37
|
+
* so bars spring into place from left to right.
|
|
38
|
+
* `{ type: 'spring', stiffness: 900, damping: 120, staggerDelay: 250 }`
|
|
39
|
+
*/
|
|
40
|
+
export const defaultBarEnterTransition = _extends({}, defaultTransition, {
|
|
41
|
+
staggerDelay: 250
|
|
42
|
+
});
|
|
43
|
+
|
|
1
44
|
/**
|
|
2
45
|
* Calculates the size adjustment needed for bars when accounting for gaps between them.
|
|
3
46
|
* This function helps determine how much to reduce each bar's width to accommodate
|
package/esm/chart/utils/chart.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
import { isSharedValue } from 'react-native-reanimated';
|
|
1
2
|
import { stack as d3Stack, stackOffsetDiverging, stackOrderNone } from 'd3-shape';
|
|
2
3
|
export const defaultStackId = 'DEFAULT_STACK_ID';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shape variants available for legend items.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Shape for legend items. Can be a preset variant or a custom ReactNode.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Position of the legend relative to the chart.
|
|
15
|
+
*/
|
|
16
|
+
|
|
3
17
|
/**
|
|
4
18
|
* Type guard to check if bounds are complete with both min and max values.
|
|
5
19
|
* @param bounds - The bounds to validate
|
|
@@ -19,13 +33,13 @@ export const getChartDomain = (series, min, max) => {
|
|
|
19
33
|
return domain;
|
|
20
34
|
}
|
|
21
35
|
if (series.length > 0) {
|
|
22
|
-
const
|
|
36
|
+
const dataLength = Math.max(...series.map(s => {
|
|
23
37
|
var _s$data;
|
|
24
38
|
return ((_s$data = s.data) == null ? void 0 : _s$data.length) || 0;
|
|
25
39
|
}));
|
|
26
|
-
if (
|
|
40
|
+
if (dataLength > 0) {
|
|
27
41
|
if (domain.min === undefined) domain.min = 0;
|
|
28
|
-
if (domain.max === undefined) domain.max =
|
|
42
|
+
if (domain.max === undefined) domain.max = dataLength - 1;
|
|
29
43
|
}
|
|
30
44
|
}
|
|
31
45
|
return domain;
|
|
@@ -111,6 +125,32 @@ export const getStackedSeriesData = series => {
|
|
|
111
125
|
return stackedDataMap;
|
|
112
126
|
};
|
|
113
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Extracts line data values from series data that may contain tuples.
|
|
130
|
+
* For tuple data [[baseline, value]], extracts the last value.
|
|
131
|
+
* For numeric data [value], returns as-is.
|
|
132
|
+
*
|
|
133
|
+
* @param data - Array of numbers, tuples, or null values
|
|
134
|
+
* @returns Array of numbers or null values
|
|
135
|
+
*/
|
|
136
|
+
export const getLineData = data => {
|
|
137
|
+
if (!data) return [];
|
|
138
|
+
|
|
139
|
+
// Check if this is tuple data by finding first non-null entry
|
|
140
|
+
const firstNonNull = data.find(d => d !== null);
|
|
141
|
+
if (Array.isArray(firstNonNull)) {
|
|
142
|
+
return data.map(d => {
|
|
143
|
+
var _d$at;
|
|
144
|
+
if (d === null) return null;
|
|
145
|
+
if (Array.isArray(d)) return (_d$at = d.at(-1)) != null ? _d$at : null;
|
|
146
|
+
return d;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Already numeric data
|
|
151
|
+
return data;
|
|
152
|
+
};
|
|
153
|
+
|
|
114
154
|
/**
|
|
115
155
|
* Calculates the range of a chart from series data.
|
|
116
156
|
* Range represents the range of y-values from the data.
|
|
@@ -226,4 +266,18 @@ export const getChartInset = (inset, defaults) => {
|
|
|
226
266
|
bottom: (_inset$bottom = inset == null ? void 0 : inset.bottom) != null ? _inset$bottom : baseDefaults.bottom,
|
|
227
267
|
right: (_inset$right = inset == null ? void 0 : inset.right) != null ? _inset$right : baseDefaults.right
|
|
228
268
|
};
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Unwraps an optionally animated value to get the raw value.
|
|
273
|
+
* @param value - The value to unwrap.
|
|
274
|
+
* @returns The raw value.
|
|
275
|
+
*/
|
|
276
|
+
export const unwrapAnimatedValue = value => {
|
|
277
|
+
'worklet';
|
|
278
|
+
|
|
279
|
+
if (isSharedValue(value)) {
|
|
280
|
+
return value.value;
|
|
281
|
+
}
|
|
282
|
+
return value;
|
|
229
283
|
};
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { Skia } from '@shopify/react-native-skia';
|
|
2
|
+
import { applySerializableScale, isCategoricalScale, isSerializableScale } from './scale';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Defines a color transition point in the gradient
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Defines a gradient.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolves gradient stops, handling both static arrays and function forms.
|
|
14
|
+
* When stops is a function, calls it with the domain bounds.
|
|
15
|
+
*/
|
|
16
|
+
export const getGradientStops = (stops, domain) => {
|
|
17
|
+
if (typeof stops === 'function') {
|
|
18
|
+
return stops(domain);
|
|
19
|
+
}
|
|
20
|
+
return stops;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Processes Gradient to gradient configuration for SVG linearGradient.
|
|
25
|
+
* Colors are smoothly interpolated between stops by the browser.
|
|
26
|
+
* Multiple stops at the same offset create hard color transitions.
|
|
27
|
+
*/
|
|
28
|
+
const processGradientStops = (stops, scale) => {
|
|
29
|
+
if (stops.length === 0) {
|
|
30
|
+
console.warn('Gradient has no stops - falling back to default');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check if stops are in ascending order
|
|
35
|
+
const isOutOfOrder = stops.some((stop, i) => {
|
|
36
|
+
return i > 0 && stop.offset < stops[i - 1].offset;
|
|
37
|
+
});
|
|
38
|
+
if (isOutOfOrder) {
|
|
39
|
+
console.warn("Gradient: stop offsets must be in ascending order");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const [rangeMin, rangeMax] = scale.range();
|
|
43
|
+
const rangeSpan = Math.abs(rangeMax - rangeMin);
|
|
44
|
+
|
|
45
|
+
// Convert data value offsets to normalized positions (0-1) using scale
|
|
46
|
+
const normalizedStops = stops.map(stop => {
|
|
47
|
+
var _stop$opacity;
|
|
48
|
+
const stopPosition = scale(stop.offset);
|
|
49
|
+
const normalized = stopPosition === undefined ? 0 : Math.max(0, Math.min(1, Math.abs(stopPosition - rangeMin) / rangeSpan));
|
|
50
|
+
return {
|
|
51
|
+
offset: normalized,
|
|
52
|
+
// Now 0-1 normalized (not data space)
|
|
53
|
+
color: stop.color,
|
|
54
|
+
opacity: (_stop$opacity = stop.opacity) != null ? _stop$opacity : 1
|
|
55
|
+
};
|
|
56
|
+
}).sort((a, b) => a.offset - b.offset);
|
|
57
|
+
return normalizedStops;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Interpolates between two colors using linear interpolation.
|
|
62
|
+
* Returns an rgba string.
|
|
63
|
+
*/
|
|
64
|
+
const interpolateColor = (color1, color2, t) => {
|
|
65
|
+
'worklet';
|
|
66
|
+
|
|
67
|
+
const c1 = Skia.Color(color1);
|
|
68
|
+
const c2 = Skia.Color(color2);
|
|
69
|
+
const r = Math.round((c1[0] + (c2[0] - c1[0]) * t) * 255);
|
|
70
|
+
const g = Math.round((c1[1] + (c2[1] - c1[1]) * t) * 255);
|
|
71
|
+
const b = Math.round((c1[2] + (c2[2] - c1[2]) * t) * 255);
|
|
72
|
+
const a = c1[3] + (c2[3] - c1[3]) * t;
|
|
73
|
+
return "rgba(" + r + ", " + g + ", " + b + ", " + a + ")";
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Adds an opacity to a color
|
|
78
|
+
* Returns an rgba string.
|
|
79
|
+
*/
|
|
80
|
+
export const getColorWithOpacity = (color1, opacity) => {
|
|
81
|
+
const c = Skia.Color(color1);
|
|
82
|
+
return "rgba(" + c[0] * 255 + ", " + c[1] * 255 + ", " + c[2] * 255 + ", " + opacity + ")";
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Creates a gradient configuration for SVG components.
|
|
87
|
+
* Processes a GradientDefinition into a renderable GradientConfig.
|
|
88
|
+
* Supports both numeric scales (linear, log) and categorical scales (band).
|
|
89
|
+
*
|
|
90
|
+
* @param gradient - GradientDefinition configuration (required)
|
|
91
|
+
* @param xScale - X-axis scale (required)
|
|
92
|
+
* @param yScale - Y-axis scale (required)
|
|
93
|
+
* @returns GradientConfig or null if gradient processing fails
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* const gradientConfig = useMemo(() => {
|
|
97
|
+
* if (!gradient || !xScale || !yScale) return;
|
|
98
|
+
* return getGradientConfig(gradient, xScale, yScale);
|
|
99
|
+
* }, [gradient, xScale, yScale]);
|
|
100
|
+
*
|
|
101
|
+
* if (gradientConfig) {
|
|
102
|
+
* return (
|
|
103
|
+
* <defs>
|
|
104
|
+
* <Gradient
|
|
105
|
+
* config={gradientConfig}
|
|
106
|
+
* direction={gradient.axis === 'x' ? 'horizontal' : 'vertical'}
|
|
107
|
+
* id={gradientId}
|
|
108
|
+
* />
|
|
109
|
+
* </defs>
|
|
110
|
+
* );
|
|
111
|
+
* }
|
|
112
|
+
*/
|
|
113
|
+
export const getGradientConfig = (gradient, xScale, yScale) => {
|
|
114
|
+
if (!gradient) return;
|
|
115
|
+
|
|
116
|
+
// Get the scale based on axis
|
|
117
|
+
const scale = gradient.axis === 'x' ? xScale : yScale;
|
|
118
|
+
if (!scale) return;
|
|
119
|
+
|
|
120
|
+
// Extract domain from scale
|
|
121
|
+
const scaleDomain = scale.domain();
|
|
122
|
+
let domain;
|
|
123
|
+
if (isCategoricalScale(scale)) {
|
|
124
|
+
const domainArray = scaleDomain;
|
|
125
|
+
domain = {
|
|
126
|
+
min: domainArray[0],
|
|
127
|
+
max: domainArray[domainArray.length - 1]
|
|
128
|
+
};
|
|
129
|
+
} else {
|
|
130
|
+
const [min, max] = scaleDomain;
|
|
131
|
+
domain = {
|
|
132
|
+
min,
|
|
133
|
+
max
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const resolvedStops = getGradientStops(gradient.stops, domain);
|
|
137
|
+
return processGradientStops(resolvedStops, scale);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Evaluates the color at a specific data value based on the gradient stops, ignoring opacity.
|
|
142
|
+
* @param stops - The gradient stops configuration
|
|
143
|
+
* @param dataValue - The data value to evaluate (for band scales, this is the index)
|
|
144
|
+
* @param scale - The scale to use for value mapping (handles log scales correctly)
|
|
145
|
+
* @returns The color string at this data value, or undefined if invalid
|
|
146
|
+
*/
|
|
147
|
+
export const evaluateGradientAtValue = (stops, dataValue, scale) => {
|
|
148
|
+
'worklet';
|
|
149
|
+
|
|
150
|
+
if (stops.length === 0) return;
|
|
151
|
+
|
|
152
|
+
// Determine range based on scale type
|
|
153
|
+
let rangeMin;
|
|
154
|
+
let rangeMax;
|
|
155
|
+
if (isSerializableScale(scale)) {
|
|
156
|
+
// SerializableScale has range as [number, number]
|
|
157
|
+
[rangeMin, rangeMax] = scale.range;
|
|
158
|
+
} else {
|
|
159
|
+
// ChartScaleFunction has range() method
|
|
160
|
+
const scaleRange = scale.range();
|
|
161
|
+
[rangeMin, rangeMax] = Array.isArray(scaleRange) ? scaleRange : [scaleRange, scaleRange]; // fallback for band scales
|
|
162
|
+
}
|
|
163
|
+
const rangeSpan = Math.abs(rangeMax - rangeMin);
|
|
164
|
+
if (rangeSpan === 0) return stops[0].color;
|
|
165
|
+
|
|
166
|
+
// Map dataValue through scale to get position
|
|
167
|
+
let dataPosition;
|
|
168
|
+
if (isSerializableScale(scale)) {
|
|
169
|
+
dataPosition = applySerializableScale(dataValue, scale);
|
|
170
|
+
} else {
|
|
171
|
+
const result = scale(dataValue);
|
|
172
|
+
if (result === undefined) return stops[0].color;
|
|
173
|
+
dataPosition = result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Normalize to 0-1 based on range
|
|
177
|
+
const normalizedValue = Math.max(0, Math.min(1, Math.abs(dataPosition - rangeMin) / rangeSpan));
|
|
178
|
+
|
|
179
|
+
// Map stop offsets through scale and normalize to 0-1
|
|
180
|
+
const positions = stops.map(stop => {
|
|
181
|
+
let stopPosition;
|
|
182
|
+
if (isSerializableScale(scale)) {
|
|
183
|
+
stopPosition = applySerializableScale(stop.offset, scale);
|
|
184
|
+
} else {
|
|
185
|
+
const result = scale(stop.offset);
|
|
186
|
+
if (result === undefined) return 0;
|
|
187
|
+
stopPosition = result;
|
|
188
|
+
}
|
|
189
|
+
return Math.max(0, Math.min(1, Math.abs(stopPosition - rangeMin) / rangeSpan));
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Find which segment we're in
|
|
193
|
+
if (normalizedValue < positions[0]) {
|
|
194
|
+
return stops[0].color;
|
|
195
|
+
}
|
|
196
|
+
if (normalizedValue >= positions[positions.length - 1]) {
|
|
197
|
+
return stops[stops.length - 1].color;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Check if dataValue matches any stop offset exactly (for hard transitions)
|
|
201
|
+
for (let i = 0; i < stops.length; i++) {
|
|
202
|
+
if (dataValue === stops[i].offset) {
|
|
203
|
+
// Found exact match - check if there are multiple stops at this offset (hard transition)
|
|
204
|
+
// Use the LAST color at this offset for hard transitions
|
|
205
|
+
let lastIndexAtOffset = i;
|
|
206
|
+
while (lastIndexAtOffset + 1 < stops.length && stops[lastIndexAtOffset + 1].offset === stops[i].offset) {
|
|
207
|
+
lastIndexAtOffset++;
|
|
208
|
+
}
|
|
209
|
+
return stops[lastIndexAtOffset].color;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Find the segment to interpolate between
|
|
214
|
+
for (let i = 0; i < positions.length - 1; i++) {
|
|
215
|
+
if (normalizedValue >= positions[i] && normalizedValue <= positions[i + 1]) {
|
|
216
|
+
const segmentStart = positions[i];
|
|
217
|
+
const segmentEnd = positions[i + 1];
|
|
218
|
+
if (segmentEnd === segmentStart) {
|
|
219
|
+
return stops[i].color;
|
|
220
|
+
}
|
|
221
|
+
const t = (normalizedValue - segmentStart) / (segmentEnd - segmentStart);
|
|
222
|
+
return interpolateColor(stops[i].color, stops[i + 1].color, t);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return stops[0].color;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Determines the baseline value for the gradient area by finding the value
|
|
230
|
+
* within the axis bounds that is closest to the target baseline.
|
|
231
|
+
*
|
|
232
|
+
* @param axisBounds - The min and max bounds of the axis
|
|
233
|
+
* @param baseline - The target baseline value (defaults to 0)
|
|
234
|
+
* @returns The value within bounds closest to the baseline
|
|
235
|
+
*/
|
|
236
|
+
export const getBaseline = function (axisBounds, baseline) {
|
|
237
|
+
if (baseline === void 0) {
|
|
238
|
+
baseline = 0;
|
|
239
|
+
}
|
|
240
|
+
const {
|
|
241
|
+
min,
|
|
242
|
+
max
|
|
243
|
+
} = axisBounds;
|
|
244
|
+
|
|
245
|
+
// Normalize to ensure lowerBound <= upperBound
|
|
246
|
+
const lowerBound = Math.min(min, max);
|
|
247
|
+
const upperBound = Math.max(min, max);
|
|
248
|
+
|
|
249
|
+
// If baseline is within the range, use it
|
|
250
|
+
if (lowerBound <= baseline && baseline <= upperBound) return baseline;
|
|
251
|
+
|
|
252
|
+
// Otherwise, return the bound closest to baseline
|
|
253
|
+
return Math.abs(lowerBound - baseline) < Math.abs(upperBound - baseline) ? lowerBound : upperBound;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Generates a gradient definition for the area chart based on the axis bounds
|
|
258
|
+
* and styling parameters. Ensures gradient stops are in ascending order.
|
|
259
|
+
*
|
|
260
|
+
* @param axisBounds - The min and max bounds of the axis
|
|
261
|
+
* @param baselineValue - The baseline value for the gradient
|
|
262
|
+
* @param fill - The color to use for the gradient
|
|
263
|
+
* @param peakOpacity - Opacity at the peak of the gradient
|
|
264
|
+
* @param baselineOpacity - Opacity at the baseline
|
|
265
|
+
* @returns A gradient definition with y-axis stops in ascending order
|
|
266
|
+
*/
|
|
267
|
+
export const createGradient = (axisBounds, baselineValue, fill, peakOpacity, baselineOpacity) => {
|
|
268
|
+
const {
|
|
269
|
+
min,
|
|
270
|
+
max
|
|
271
|
+
} = axisBounds;
|
|
272
|
+
const lowerBound = Math.min(min, max);
|
|
273
|
+
const upperBound = Math.max(min, max);
|
|
274
|
+
if (lowerBound < baselineValue && baselineValue < upperBound) {
|
|
275
|
+
return {
|
|
276
|
+
axis: 'y',
|
|
277
|
+
stops: [{
|
|
278
|
+
offset: lowerBound,
|
|
279
|
+
color: fill,
|
|
280
|
+
opacity: peakOpacity
|
|
281
|
+
}, {
|
|
282
|
+
offset: baselineValue,
|
|
283
|
+
color: fill,
|
|
284
|
+
opacity: baselineOpacity
|
|
285
|
+
}, {
|
|
286
|
+
offset: upperBound,
|
|
287
|
+
color: fill,
|
|
288
|
+
opacity: peakOpacity
|
|
289
|
+
}]
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const peakValue = Math.abs(min - baselineValue) > Math.abs(max - baselineValue) ? min : max;
|
|
293
|
+
return {
|
|
294
|
+
axis: 'y',
|
|
295
|
+
stops: [{
|
|
296
|
+
offset: peakValue,
|
|
297
|
+
color: fill,
|
|
298
|
+
opacity: peakOpacity
|
|
299
|
+
}, {
|
|
300
|
+
offset: baselineValue,
|
|
301
|
+
color: fill,
|
|
302
|
+
opacity: baselineOpacity
|
|
303
|
+
}].sort((a, b) => a.offset - b.offset)
|
|
304
|
+
};
|
|
305
|
+
};
|
package/esm/chart/utils/index.js
CHANGED
|
@@ -3,7 +3,10 @@ export * from './axis';
|
|
|
3
3
|
export * from './bar';
|
|
4
4
|
export * from './chart';
|
|
5
5
|
export * from './context';
|
|
6
|
+
export * from './gradient';
|
|
6
7
|
export * from './path';
|
|
7
8
|
export * from './point';
|
|
8
9
|
export * from './scale';
|
|
10
|
+
export * from './scrubber';
|
|
11
|
+
export * from './transition';
|
|
9
12
|
// codegen:end
|
package/esm/chart/utils/path.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { area as d3Area, curveBumpX, curveCatmullRom, curveLinear, curveLinearClosed, curveMonotoneX, curveNatural, curveStep, curveStepAfter, curveStepBefore, line as d3Line } from 'd3-shape';
|
|
2
2
|
import { projectPoint, projectPoints } from './point';
|
|
3
3
|
import { isCategoricalScale } from './scale';
|
|
4
|
+
/**
|
|
5
|
+
* Default enter transition for path-based components (Line, Area).
|
|
6
|
+
* `{ type: 'timing', duration: 500 }`
|
|
7
|
+
*/
|
|
8
|
+
export const defaultPathEnterTransition = {
|
|
9
|
+
type: 'timing',
|
|
10
|
+
duration: 500
|
|
11
|
+
};
|
|
4
12
|
/**
|
|
5
13
|
* Get the d3 curve function for a path.
|
|
6
14
|
* See https://d3js.org/d3-shape/curve
|
|
@@ -49,10 +57,11 @@ export const getLinePath = _ref => {
|
|
|
49
57
|
var _pathGenerator;
|
|
50
58
|
let {
|
|
51
59
|
data,
|
|
52
|
-
curve = '
|
|
60
|
+
curve = 'bump',
|
|
53
61
|
xScale,
|
|
54
62
|
yScale,
|
|
55
|
-
xData
|
|
63
|
+
xData,
|
|
64
|
+
connectNulls = false
|
|
56
65
|
} = _ref;
|
|
57
66
|
if (data.length === 0) {
|
|
58
67
|
return '';
|
|
@@ -64,9 +73,12 @@ export const getLinePath = _ref => {
|
|
|
64
73
|
yScale,
|
|
65
74
|
xData
|
|
66
75
|
});
|
|
67
|
-
const pathGenerator = d3Line().x(d => d.x).y(d => d.y).curve(curveFunction).defined(d => d !== null); // Only draw lines where point is not null
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
// When connectNulls is true, filter out null values before rendering
|
|
78
|
+
// When false, use defined() to create gaps in the line
|
|
79
|
+
const filteredPoints = connectNulls ? dataPoints.filter(d => d !== null) : dataPoints;
|
|
80
|
+
const pathGenerator = d3Line().x(d => d.x).y(d => d.y).curve(curveFunction).defined(d => connectNulls || d !== null);
|
|
81
|
+
return (_pathGenerator = pathGenerator(filteredPoints)) != null ? _pathGenerator : '';
|
|
70
82
|
};
|
|
71
83
|
|
|
72
84
|
/**
|
|
@@ -94,10 +106,11 @@ export const getLinePath = _ref => {
|
|
|
94
106
|
export const getAreaPath = _ref2 => {
|
|
95
107
|
let {
|
|
96
108
|
data,
|
|
97
|
-
curve = '
|
|
109
|
+
curve = 'bump',
|
|
98
110
|
xScale,
|
|
99
111
|
yScale,
|
|
100
|
-
xData
|
|
112
|
+
xData,
|
|
113
|
+
connectNulls = false
|
|
101
114
|
} = _ref2;
|
|
102
115
|
if (data.length === 0) {
|
|
103
116
|
return '';
|
|
@@ -158,6 +171,10 @@ export const getAreaPath = _ref2 => {
|
|
|
158
171
|
isValid: true
|
|
159
172
|
};
|
|
160
173
|
});
|
|
174
|
+
|
|
175
|
+
// When connectNulls is true, filter out invalid points before rendering
|
|
176
|
+
// When false, use defined() to create gaps in the area
|
|
177
|
+
const filteredPoints = connectNulls ? dataPoints.filter(d => d.isValid) : dataPoints;
|
|
161
178
|
const areaGenerator = d3Area().x(d => d.x).y0(d => {
|
|
162
179
|
var _d$low;
|
|
163
180
|
return (_d$low = d.low) != null ? _d$low : 0;
|
|
@@ -166,12 +183,26 @@ export const getAreaPath = _ref2 => {
|
|
|
166
183
|
var _d$high;
|
|
167
184
|
return (_d$high = d.high) != null ? _d$high : 0;
|
|
168
185
|
}) // Top boundary (high values), fallback to 0
|
|
169
|
-
.curve(curveFunction).defined(d => d.isValid && d.low != null && d.high != null); // Only draw where both values exist
|
|
186
|
+
.curve(curveFunction).defined(d => connectNulls || d.isValid && d.low != null && d.high != null); // Only draw where both values exist
|
|
170
187
|
|
|
171
|
-
const result = areaGenerator(
|
|
188
|
+
const result = areaGenerator(filteredPoints);
|
|
172
189
|
return result != null ? result : '';
|
|
173
190
|
};
|
|
174
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Converts line coordinates to an SVG path string.
|
|
194
|
+
* Useful for rendering axis lines and tick marks.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const path = lineToPath(0, 0, 100, 100);
|
|
199
|
+
* // Returns: "M 0 0 L 100 100"
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
export const lineToPath = (x1, y1, x2, y2) => {
|
|
203
|
+
return "M " + x1 + " " + y1 + " L " + x2 + " " + y2;
|
|
204
|
+
};
|
|
205
|
+
|
|
175
206
|
/**
|
|
176
207
|
* Creates an SVG path string for a rectangle with selective corner rounding.
|
|
177
208
|
* Useful for creating bars in charts with optional rounded corners.
|
|
@@ -203,4 +234,49 @@ export const getBarPath = (x, y, width, height, radius, roundTop, roundBottom) =
|
|
|
203
234
|
path += " A " + topR + " " + topR + " 0 0 1 " + (x + topR) + " " + y;
|
|
204
235
|
path += ' Z';
|
|
205
236
|
return path;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Generates an SVG path string with circles arranged in a dotted pattern within a bounding area.
|
|
241
|
+
* Creates circles at regular intervals based on the pattern size and dot size parameters.
|
|
242
|
+
*
|
|
243
|
+
* @param bounds - The bounding rectangle to fill with dots
|
|
244
|
+
* @param patternSize - Size of the pattern unit (spacing between dots)
|
|
245
|
+
* @param dotSize - Radius of each dot
|
|
246
|
+
* @returns SVG path string containing all the circles
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* const dotsPath = getDottedAreaPath(
|
|
251
|
+
* { x: 0, y: 0, width: 100, height: 50 },
|
|
252
|
+
* 8, // 8px spacing
|
|
253
|
+
* 2 // 2px radius dots
|
|
254
|
+
* );
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
export const getDottedAreaPath = (bounds, patternSize, dotSize) => {
|
|
258
|
+
if (bounds.width <= 0 || bounds.height <= 0 || patternSize <= 0 || dotSize <= 0) {
|
|
259
|
+
return '';
|
|
260
|
+
}
|
|
261
|
+
let path = '';
|
|
262
|
+
|
|
263
|
+
// Calculate the number of dots that fit in each dimension
|
|
264
|
+
const dotsX = Math.ceil(bounds.width / patternSize);
|
|
265
|
+
const dotsY = Math.ceil(bounds.height / patternSize);
|
|
266
|
+
|
|
267
|
+
// Generate circles in a grid pattern
|
|
268
|
+
for (let row = 0; row < dotsY; row++) {
|
|
269
|
+
for (let col = 0; col < dotsX; col++) {
|
|
270
|
+
const centerX = bounds.x + col * patternSize + patternSize / 2;
|
|
271
|
+
const centerY = bounds.y + row * patternSize + patternSize / 2;
|
|
272
|
+
|
|
273
|
+
// Only draw dots that are within the bounds
|
|
274
|
+
if (centerX >= bounds.x && centerX <= bounds.x + bounds.width && centerY >= bounds.y && centerY <= bounds.y + bounds.height) {
|
|
275
|
+
// Create circle using SVG arc commands
|
|
276
|
+
// M cx,cy-r a r,r 0 1,0 0,2r a r,r 0 1,0 0,-2r
|
|
277
|
+
path += "M " + centerX + "," + (centerY - dotSize) + " a " + dotSize + "," + dotSize + " 0 1,0 0," + dotSize * 2 + " a " + dotSize + "," + dotSize + " 0 1,0 0," + -dotSize * 2 + " ";
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return path.trim();
|
|
206
282
|
};
|