@coinbase/cds-mobile-visualization 3.4.0-beta.22 → 3.4.0-beta.23
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 +6 -0
- package/dts/chart/CartesianChart.d.ts +39 -7
- package/dts/chart/CartesianChart.d.ts.map +1 -1
- package/dts/chart/Path.d.ts.map +1 -1
- package/dts/chart/area/Area.d.ts +7 -0
- package/dts/chart/area/Area.d.ts.map +1 -1
- package/dts/chart/area/AreaChart.d.ts +5 -5
- package/dts/chart/area/AreaChart.d.ts.map +1 -1
- package/dts/chart/area/DottedArea.d.ts.map +1 -1
- package/dts/chart/area/GradientArea.d.ts.map +1 -1
- package/dts/chart/area/SolidArea.d.ts.map +1 -1
- package/dts/chart/axis/Axis.d.ts +3 -1
- package/dts/chart/axis/Axis.d.ts.map +1 -1
- package/dts/chart/axis/XAxis.d.ts +6 -0
- package/dts/chart/axis/XAxis.d.ts.map +1 -1
- package/dts/chart/axis/YAxis.d.ts +1 -0
- package/dts/chart/axis/YAxis.d.ts.map +1 -1
- package/dts/chart/bar/Bar.d.ts +4 -2
- package/dts/chart/bar/Bar.d.ts.map +1 -1
- package/dts/chart/bar/BarChart.d.ts +49 -9
- package/dts/chart/bar/BarChart.d.ts.map +1 -1
- package/dts/chart/bar/BarPlot.d.ts.map +1 -1
- package/dts/chart/bar/BarStack.d.ts +30 -9
- package/dts/chart/bar/BarStack.d.ts.map +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts.map +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 +5 -0
- package/dts/chart/gradient/Gradient.d.ts.map +1 -1
- package/dts/chart/line/DottedLine.d.ts.map +1 -1
- package/dts/chart/line/Line.d.ts +7 -0
- package/dts/chart/line/Line.d.ts.map +1 -1
- package/dts/chart/line/LineChart.d.ts +5 -5
- package/dts/chart/line/LineChart.d.ts.map +1 -1
- package/dts/chart/line/ReferenceLine.d.ts +1 -0
- package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
- package/dts/chart/line/SolidLine.d.ts.map +1 -1
- package/dts/chart/point/Point.d.ts +7 -0
- package/dts/chart/point/Point.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +2 -1
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -1
- package/dts/chart/scrubber/Scrubber.d.ts +8 -0
- package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
- package/dts/chart/utils/axis.d.ts +20 -9
- package/dts/chart/utils/axis.d.ts.map +1 -1
- package/dts/chart/utils/bar.d.ts +4 -3
- package/dts/chart/utils/bar.d.ts.map +1 -1
- package/dts/chart/utils/chart.d.ts +13 -0
- package/dts/chart/utils/chart.d.ts.map +1 -1
- package/dts/chart/utils/context.d.ts +21 -6
- package/dts/chart/utils/context.d.ts.map +1 -1
- package/dts/chart/utils/gradient.d.ts +3 -1
- package/dts/chart/utils/gradient.d.ts.map +1 -1
- package/dts/chart/utils/path.d.ts +20 -0
- package/dts/chart/utils/path.d.ts.map +1 -1
- package/dts/chart/utils/point.d.ts +7 -0
- package/dts/chart/utils/point.d.ts.map +1 -1
- package/esm/chart/CartesianChart.js +107 -68
- package/esm/chart/Path.js +10 -7
- package/esm/chart/area/Area.js +19 -9
- package/esm/chart/area/AreaChart.js +11 -9
- package/esm/chart/area/DottedArea.js +11 -6
- package/esm/chart/area/GradientArea.js +11 -6
- package/esm/chart/area/SolidArea.js +3 -1
- package/esm/chart/area/__stories__/AreaChart.stories.js +30 -2
- package/esm/chart/axis/XAxis.js +14 -21
- package/esm/chart/axis/YAxis.js +4 -3
- package/esm/chart/bar/Bar.js +9 -5
- package/esm/chart/bar/BarChart.js +34 -31
- package/esm/chart/bar/BarPlot.js +7 -5
- package/esm/chart/bar/BarStack.js +176 -36
- package/esm/chart/bar/BarStackGroup.js +37 -27
- package/esm/chart/bar/DefaultBar.js +24 -8
- package/esm/chart/bar/DefaultBarStack.js +24 -10
- package/esm/chart/bar/__stories__/BarChart.stories.js +99 -3
- package/esm/chart/gradient/Gradient.js +2 -1
- package/esm/chart/line/DottedLine.js +3 -1
- package/esm/chart/line/Line.js +32 -19
- package/esm/chart/line/LineChart.js +9 -8
- package/esm/chart/line/SolidLine.js +3 -1
- package/esm/chart/line/__stories__/LineChart.stories.js +31 -0
- package/esm/chart/point/Point.js +2 -1
- package/esm/chart/scrubber/DefaultScrubberBeacon.js +1 -1
- package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -10
- package/esm/chart/scrubber/Scrubber.js +47 -21
- package/esm/chart/scrubber/ScrubberBeaconGroup.js +24 -20
- package/esm/chart/scrubber/ScrubberProvider.js +29 -24
- package/esm/chart/scrubber/__stories__/Scrubber.stories.js +135 -1
- package/esm/chart/utils/axis.js +42 -14
- package/esm/chart/utils/bar.js +5 -4
- package/esm/chart/utils/chart.js +18 -5
- package/esm/chart/utils/context.js +7 -0
- package/esm/chart/utils/gradient.js +8 -4
- package/esm/chart/utils/path.js +90 -61
- package/esm/chart/utils/point.js +28 -18
- package/package.json +5 -5
package/esm/chart/utils/axis.js
CHANGED
|
@@ -47,7 +47,7 @@ export const toPointAnchor = placement => {
|
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* Gets a D3 scale based on the axis configuration.
|
|
50
|
+
* Gets a D3 scale based on the cartesian axis configuration.
|
|
51
51
|
* Handles both numeric (linear/log) and categorical (band) scales.
|
|
52
52
|
*
|
|
53
53
|
* For numeric scales, the domain limit controls whether bounds are "nice" (human-friendly)
|
|
@@ -57,19 +57,27 @@ export const toPointAnchor = placement => {
|
|
|
57
57
|
* @returns The D3 scale function
|
|
58
58
|
* @throws An Error if bounds are invalid
|
|
59
59
|
*/
|
|
60
|
-
export const
|
|
60
|
+
export const getCartesianAxisScale = _ref => {
|
|
61
61
|
var _config$scaleType;
|
|
62
62
|
let {
|
|
63
63
|
config,
|
|
64
64
|
type,
|
|
65
65
|
range,
|
|
66
|
-
dataDomain
|
|
66
|
+
dataDomain,
|
|
67
|
+
layout = 'vertical'
|
|
67
68
|
} = _ref;
|
|
68
69
|
const scaleType = (_config$scaleType = config == null ? void 0 : config.scaleType) != null ? _config$scaleType : 'linear';
|
|
69
70
|
let adjustedRange = range;
|
|
70
71
|
|
|
71
|
-
//
|
|
72
|
-
|
|
72
|
+
// Determine if this axis needs range inversion for SVG coordinate system.
|
|
73
|
+
// SVG Y coordinates increase downward, so we need to invert for value axes
|
|
74
|
+
// where we want higher values at the top.
|
|
75
|
+
//
|
|
76
|
+
// For vertical layout: Y axis is the value axis -> invert (higher values at top)
|
|
77
|
+
// For horizontal layout: Y axis is the category axis -> don't invert (first category at top is natural)
|
|
78
|
+
// X axis never needs inversion (left-to-right is natural for both layouts)
|
|
79
|
+
const shouldInvertRange = type === 'y' && layout !== 'horizontal';
|
|
80
|
+
if (shouldInvertRange) {
|
|
73
81
|
adjustedRange = {
|
|
74
82
|
min: adjustedRange.max,
|
|
75
83
|
max: adjustedRange.min
|
|
@@ -121,6 +129,8 @@ export const getAxisConfig = function (type, axes, defaultId, defaultScaleType)
|
|
|
121
129
|
defaultScaleType = defaultAxisScaleType;
|
|
122
130
|
}
|
|
123
131
|
const defaultDomainLimit = type === 'x' ? 'strict' : 'nice';
|
|
132
|
+
const axisName = type === 'x' ? 'x-axis' : 'y-axis';
|
|
133
|
+
const axisDocUrl = type === 'x' ? 'https://cds.coinbase.com/components/charts/XAxis' : 'https://cds.coinbase.com/components/charts/YAxis';
|
|
124
134
|
if (!axes) {
|
|
125
135
|
return [{
|
|
126
136
|
id: defaultId,
|
|
@@ -137,16 +147,27 @@ export const getAxisConfig = function (type, axes, defaultId, defaultScaleType)
|
|
|
137
147
|
} = _ref2;
|
|
138
148
|
return id === undefined;
|
|
139
149
|
})) {
|
|
140
|
-
throw new Error(
|
|
150
|
+
throw new Error("When defining multiple " + axisName + ", each must have a unique id. See " + axisDocUrl + ".");
|
|
141
151
|
}
|
|
142
|
-
|
|
152
|
+
if (axesLength > 1) {
|
|
153
|
+
const ids = axes.map(_ref3 => {
|
|
154
|
+
let {
|
|
155
|
+
id
|
|
156
|
+
} = _ref3;
|
|
157
|
+
return id;
|
|
158
|
+
}).filter(id => id !== undefined);
|
|
159
|
+
if (new Set(ids).size !== ids.length) {
|
|
160
|
+
throw new Error("When defining multiple " + axisName + ", each must have a unique id. See " + axisDocUrl + ".");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return axes.map(_ref4 => {
|
|
143
164
|
let {
|
|
144
165
|
id
|
|
145
|
-
} =
|
|
146
|
-
axis = _objectWithoutPropertiesLoose(
|
|
166
|
+
} = _ref4,
|
|
167
|
+
axis = _objectWithoutPropertiesLoose(_ref4, _excluded);
|
|
147
168
|
return _extends({
|
|
148
169
|
// defaults the axis id if only a single axis is provided
|
|
149
|
-
id: axesLength > 1 ? id != null ? id : defaultAxisId : id,
|
|
170
|
+
id: axesLength > 1 ? id != null ? id : defaultAxisId : id != null ? id : defaultId,
|
|
150
171
|
scaleType: defaultScaleType,
|
|
151
172
|
domainLimit: defaultDomainLimit
|
|
152
173
|
}, axis);
|
|
@@ -168,10 +189,14 @@ export const getAxisConfig = function (type, axes, defaultId, defaultScaleType)
|
|
|
168
189
|
* @param axisParam - The axis configuration
|
|
169
190
|
* @param series - Array of series objects (for stacking support)
|
|
170
191
|
* @param axisType - Whether this is an 'x' or 'y' axis
|
|
192
|
+
* @param layout - The chart layout orientation
|
|
171
193
|
* @returns The calculated axis bounds
|
|
172
194
|
*/
|
|
173
|
-
export const
|
|
195
|
+
export const getCartesianAxisDomain = function (axisParam, series, axisType, layout) {
|
|
174
196
|
var _finalDomain$min, _finalDomain$max;
|
|
197
|
+
if (layout === void 0) {
|
|
198
|
+
layout = 'vertical';
|
|
199
|
+
}
|
|
175
200
|
let dataDomain = null;
|
|
176
201
|
if (axisParam.data && Array.isArray(axisParam.data) && axisParam.data.length > 0) {
|
|
177
202
|
const firstItem = axisParam.data[0];
|
|
@@ -193,7 +218,10 @@ export const getAxisDomain = (axisParam, series, axisType) => {
|
|
|
193
218
|
}
|
|
194
219
|
|
|
195
220
|
// Calculate domain from series data
|
|
196
|
-
|
|
221
|
+
// In vertical layout: X is category (index), Y is value (value)
|
|
222
|
+
// In horizontal layout: Y is category (index), X is value (value)
|
|
223
|
+
const isCategoryAxis = layout !== 'horizontal' && axisType === 'x' || layout === 'horizontal' && axisType === 'y';
|
|
224
|
+
const seriesDomain = isCategoryAxis ? getChartDomain(series) : getChartRange(series);
|
|
197
225
|
|
|
198
226
|
// If data sets the domain, use that instead of the series domain
|
|
199
227
|
const preferredDataDomain = dataDomain != null ? dataDomain : seriesDomain;
|
|
@@ -487,7 +515,7 @@ const generateEvenlyDistributedTicks = (scale, tickInterval, possibleTickValues,
|
|
|
487
515
|
* });
|
|
488
516
|
* // Returns tick positions centered in each selected band
|
|
489
517
|
*/
|
|
490
|
-
export const getAxisTicksData =
|
|
518
|
+
export const getAxisTicksData = _ref5 => {
|
|
491
519
|
var _options$anchor;
|
|
492
520
|
let {
|
|
493
521
|
ticks,
|
|
@@ -497,7 +525,7 @@ export const getAxisTicksData = _ref4 => {
|
|
|
497
525
|
possibleTickValues,
|
|
498
526
|
tickInterval,
|
|
499
527
|
options
|
|
500
|
-
} =
|
|
528
|
+
} = _ref5;
|
|
501
529
|
const anchor = (_options$anchor = options == null ? void 0 : options.anchor) != null ? _options$anchor : 'middle';
|
|
502
530
|
|
|
503
531
|
// Handle band scales
|
package/esm/chart/utils/bar.js
CHANGED
|
@@ -6,7 +6,8 @@ import { defaultTransition } from './transition';
|
|
|
6
6
|
/**
|
|
7
7
|
* A bar-specific transition that extends Transition with stagger support.
|
|
8
8
|
* When `staggerDelay` is provided, bars will animate with increasing delays
|
|
9
|
-
* based on their
|
|
9
|
+
* based on their position along the category axis (vertical: left-to-right,
|
|
10
|
+
* horizontal: top-to-bottom).
|
|
10
11
|
*
|
|
11
12
|
* @example
|
|
12
13
|
* // Bars stagger in from left to right over 250ms, each animating for 750ms
|
|
@@ -17,10 +18,10 @@ import { defaultTransition } from './transition';
|
|
|
17
18
|
* Strips `staggerDelay` from a transition and computes a positional delay.
|
|
18
19
|
*
|
|
19
20
|
* @param transition - The transition config (may include staggerDelay)
|
|
20
|
-
* @param
|
|
21
|
+
* @param normalizedPosition - The bar's normalized position along the category axis (0–1)
|
|
21
22
|
* @returns A standard Transition with computed delay
|
|
22
23
|
*/
|
|
23
|
-
export const withStaggerDelayTransition = (transition,
|
|
24
|
+
export const withStaggerDelayTransition = (transition, normalizedPosition) => {
|
|
24
25
|
var _baseTransition$delay;
|
|
25
26
|
if (!transition) return null;
|
|
26
27
|
const {
|
|
@@ -29,7 +30,7 @@ export const withStaggerDelayTransition = (transition, normalizedX) => {
|
|
|
29
30
|
baseTransition = _objectWithoutPropertiesLoose(transition, _excluded);
|
|
30
31
|
if (!staggerDelay) return transition;
|
|
31
32
|
return _extends({}, baseTransition, {
|
|
32
|
-
delay: ((_baseTransition$delay = baseTransition == null ? void 0 : baseTransition.delay) != null ? _baseTransition$delay : 0) +
|
|
33
|
+
delay: ((_baseTransition$delay = baseTransition == null ? void 0 : baseTransition.delay) != null ? _baseTransition$delay : 0) + normalizedPosition * staggerDelay
|
|
33
34
|
});
|
|
34
35
|
};
|
|
35
36
|
|
package/esm/chart/utils/chart.js
CHANGED
|
@@ -46,15 +46,16 @@ export const getChartDomain = (series, min, max) => {
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Creates a composite stack key that includes
|
|
50
|
-
* This ensures series with different
|
|
49
|
+
* Creates a composite stack key that includes stack ID and axis IDs.
|
|
50
|
+
* This ensures series with different scales don't get stacked together.
|
|
51
51
|
*/
|
|
52
52
|
const createStackKey = series => {
|
|
53
53
|
if (series.stackId === undefined) return undefined;
|
|
54
54
|
|
|
55
|
-
// Include
|
|
55
|
+
// Include axis IDs to prevent cross-scale stacking
|
|
56
|
+
const xAxisId = series.xAxisId || 'default';
|
|
56
57
|
const yAxisId = series.yAxisId || 'default';
|
|
57
|
-
return series.stackId + ":" + yAxisId;
|
|
58
|
+
return series.stackId + ":" + xAxisId + ":" + yAxisId;
|
|
58
59
|
};
|
|
59
60
|
|
|
60
61
|
/**
|
|
@@ -225,12 +226,24 @@ export const getChartRange = (series, min, max) => {
|
|
|
225
226
|
}
|
|
226
227
|
return range;
|
|
227
228
|
};
|
|
228
|
-
export const
|
|
229
|
+
export const defaultVerticalLayoutChartInset = {
|
|
229
230
|
top: 32,
|
|
230
231
|
left: 16,
|
|
231
232
|
bottom: 16,
|
|
232
233
|
right: 16
|
|
233
234
|
};
|
|
235
|
+
export const defaultHorizontalLayoutChartInset = {
|
|
236
|
+
top: 16,
|
|
237
|
+
left: 16,
|
|
238
|
+
bottom: 16,
|
|
239
|
+
right: 48
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @deprecated Use `defaultVerticalLayoutChartInset` for vertical layout charts or
|
|
244
|
+
* `defaultHorizontalLayoutChartInset` for horizontal layout charts.
|
|
245
|
+
*/
|
|
246
|
+
export const defaultChartInset = defaultVerticalLayoutChartInset;
|
|
234
247
|
|
|
235
248
|
/**
|
|
236
249
|
* Normalize padding to include all sides with a value.
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Chart layout for Cartesian charts.
|
|
5
|
+
* Describes the direction bars/areas grow.
|
|
6
|
+
* - 'vertical': Bars grow vertically (up/down). X is category axis, Y is value axis.
|
|
7
|
+
* - 'horizontal': Bars grow horizontally (left/right). Y is category axis, X is value axis.
|
|
8
|
+
*/
|
|
9
|
+
|
|
3
10
|
/**
|
|
4
11
|
* Context value for Cartesian (X/Y) coordinate charts.
|
|
5
12
|
* Contains axis-specific methods and properties for rectangular coordinate systems.
|
|
@@ -262,9 +262,13 @@ export const getBaseline = function (axisBounds, baseline) {
|
|
|
262
262
|
* @param fill - The color to use for the gradient
|
|
263
263
|
* @param peakOpacity - Opacity at the peak of the gradient
|
|
264
264
|
* @param baselineOpacity - Opacity at the baseline
|
|
265
|
-
* @
|
|
265
|
+
* @param axis - The axis the gradient maps to ('y' for vertical, 'x' for horizontal layout)
|
|
266
|
+
* @returns A gradient definition with stops in ascending order
|
|
266
267
|
*/
|
|
267
|
-
export const createGradient = (axisBounds, baselineValue, fill, peakOpacity, baselineOpacity)
|
|
268
|
+
export const createGradient = function (axisBounds, baselineValue, fill, peakOpacity, baselineOpacity, axis) {
|
|
269
|
+
if (axis === void 0) {
|
|
270
|
+
axis = 'y';
|
|
271
|
+
}
|
|
268
272
|
const {
|
|
269
273
|
min,
|
|
270
274
|
max
|
|
@@ -273,7 +277,7 @@ export const createGradient = (axisBounds, baselineValue, fill, peakOpacity, bas
|
|
|
273
277
|
const upperBound = Math.max(min, max);
|
|
274
278
|
if (lowerBound < baselineValue && baselineValue < upperBound) {
|
|
275
279
|
return {
|
|
276
|
-
axis
|
|
280
|
+
axis,
|
|
277
281
|
stops: [{
|
|
278
282
|
offset: lowerBound,
|
|
279
283
|
color: fill,
|
|
@@ -291,7 +295,7 @@ export const createGradient = (axisBounds, baselineValue, fill, peakOpacity, bas
|
|
|
291
295
|
}
|
|
292
296
|
const peakValue = Math.abs(min - baselineValue) > Math.abs(max - baselineValue) ? min : max;
|
|
293
297
|
return {
|
|
294
|
-
axis
|
|
298
|
+
axis,
|
|
295
299
|
stops: [{
|
|
296
300
|
offset: peakValue,
|
|
297
301
|
color: fill,
|
package/esm/chart/utils/path.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { area as d3Area, curveBumpX, curveCatmullRom, curveLinear, curveLinearClosed, curveMonotoneX, curveNatural, curveStep, curveStepAfter, curveStepBefore, line as d3Line } from 'd3-shape';
|
|
2
|
-
import {
|
|
1
|
+
import { area as d3Area, curveBumpX, curveBumpY, curveCatmullRom, curveLinear, curveLinearClosed, curveMonotoneX, curveMonotoneY, curveNatural, curveStep, curveStepAfter, curveStepBefore, line as d3Line } from 'd3-shape';
|
|
2
|
+
import { getPointOnScale, projectPoints } from './point';
|
|
3
3
|
import { isCategoricalScale } from './scale';
|
|
4
4
|
/**
|
|
5
5
|
* Default enter transition for path-based components (Line, Area).
|
|
@@ -13,18 +13,23 @@ export const defaultPathEnterTransition = {
|
|
|
13
13
|
* Get the d3 curve function for a path.
|
|
14
14
|
* See https://d3js.org/d3-shape/curve
|
|
15
15
|
* @param curve - The curve type. Defaults to 'linear'.
|
|
16
|
+
* @param layout - The chart layout. Defaults to 'vertical'.
|
|
16
17
|
* @returns The d3 curve function.
|
|
17
18
|
*/
|
|
18
|
-
export const getPathCurveFunction = function (curve) {
|
|
19
|
+
export const getPathCurveFunction = function (curve, layout) {
|
|
19
20
|
if (curve === void 0) {
|
|
20
21
|
curve = 'linear';
|
|
21
22
|
}
|
|
23
|
+
if (layout === void 0) {
|
|
24
|
+
layout = 'vertical';
|
|
25
|
+
}
|
|
22
26
|
switch (curve) {
|
|
23
27
|
case 'catmullRom':
|
|
24
28
|
return curveCatmullRom;
|
|
25
29
|
case 'monotone':
|
|
26
|
-
//
|
|
27
|
-
|
|
30
|
+
// For vertical layout, X is the independent axis (category/index), so use MonotoneX.
|
|
31
|
+
// For horizontal layout, Y is the independent axis (category/index), so use MonotoneY.
|
|
32
|
+
return layout !== 'horizontal' ? curveMonotoneX : curveMonotoneY;
|
|
28
33
|
case 'natural':
|
|
29
34
|
return curveNatural;
|
|
30
35
|
case 'step':
|
|
@@ -34,8 +39,9 @@ export const getPathCurveFunction = function (curve) {
|
|
|
34
39
|
case 'stepAfter':
|
|
35
40
|
return curveStepAfter;
|
|
36
41
|
case 'bump':
|
|
37
|
-
//
|
|
38
|
-
|
|
42
|
+
// For vertical layout, X is the independent axis (category/index), so use BumpX.
|
|
43
|
+
// For horizontal layout, Y is the independent axis (category/index), so use BumpY.
|
|
44
|
+
return layout !== 'horizontal' ? curveBumpX : curveBumpY;
|
|
39
45
|
case 'linearClosed':
|
|
40
46
|
return curveLinearClosed;
|
|
41
47
|
case 'linear':
|
|
@@ -61,17 +67,21 @@ export const getLinePath = _ref => {
|
|
|
61
67
|
xScale,
|
|
62
68
|
yScale,
|
|
63
69
|
xData,
|
|
64
|
-
|
|
70
|
+
yData,
|
|
71
|
+
connectNulls = false,
|
|
72
|
+
layout = 'vertical'
|
|
65
73
|
} = _ref;
|
|
66
74
|
if (data.length === 0) {
|
|
67
75
|
return '';
|
|
68
76
|
}
|
|
69
|
-
const curveFunction = getPathCurveFunction(curve);
|
|
77
|
+
const curveFunction = getPathCurveFunction(curve, layout);
|
|
70
78
|
const dataPoints = projectPoints({
|
|
71
79
|
data,
|
|
72
80
|
xScale,
|
|
73
81
|
yScale,
|
|
74
|
-
xData
|
|
82
|
+
xData,
|
|
83
|
+
yData,
|
|
84
|
+
layout
|
|
75
85
|
});
|
|
76
86
|
|
|
77
87
|
// When connectNulls is true, filter out null values before rendering
|
|
@@ -110,14 +120,20 @@ export const getAreaPath = _ref2 => {
|
|
|
110
120
|
xScale,
|
|
111
121
|
yScale,
|
|
112
122
|
xData,
|
|
113
|
-
|
|
123
|
+
yData,
|
|
124
|
+
connectNulls = false,
|
|
125
|
+
layout = 'vertical'
|
|
114
126
|
} = _ref2;
|
|
115
127
|
if (data.length === 0) {
|
|
116
128
|
return '';
|
|
117
129
|
}
|
|
118
|
-
const curveFunction = getPathCurveFunction(curve);
|
|
119
|
-
const
|
|
120
|
-
|
|
130
|
+
const curveFunction = getPathCurveFunction(curve, layout);
|
|
131
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
132
|
+
|
|
133
|
+
// Determine baseline from the value scale.
|
|
134
|
+
const valueScale = categoryAxisIsX ? yScale : xScale;
|
|
135
|
+
const domain = valueScale.domain();
|
|
136
|
+
const min = Math.min(...domain);
|
|
121
137
|
const normalizedData = data.map((item, index) => {
|
|
122
138
|
if (item === null) {
|
|
123
139
|
return null;
|
|
@@ -129,7 +145,7 @@ export const getAreaPath = _ref2 => {
|
|
|
129
145
|
return null;
|
|
130
146
|
}
|
|
131
147
|
if (typeof item === 'number') {
|
|
132
|
-
return [
|
|
148
|
+
return [min, item];
|
|
133
149
|
}
|
|
134
150
|
return null;
|
|
135
151
|
});
|
|
@@ -137,37 +153,28 @@ export const getAreaPath = _ref2 => {
|
|
|
137
153
|
if (range === null) {
|
|
138
154
|
return {
|
|
139
155
|
x: 0,
|
|
156
|
+
y: 0,
|
|
140
157
|
low: null,
|
|
141
158
|
high: null,
|
|
142
159
|
isValid: false
|
|
143
160
|
};
|
|
144
161
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
162
|
+
|
|
163
|
+
// Determine the position along the independent (index/category) axis.
|
|
164
|
+
let indexValue = index;
|
|
165
|
+
const indexScale = categoryAxisIsX ? xScale : yScale;
|
|
166
|
+
const indexData = categoryAxisIsX ? xData : yData;
|
|
167
|
+
if (!isCategoricalScale(indexScale) && indexData && indexData[index] !== undefined) {
|
|
168
|
+
indexValue = indexData[index];
|
|
148
169
|
}
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
xScale,
|
|
153
|
-
yScale
|
|
154
|
-
});
|
|
155
|
-
const lowPoint = projectPoint({
|
|
156
|
-
x: xValue,
|
|
157
|
-
y: range[0],
|
|
158
|
-
xScale,
|
|
159
|
-
yScale
|
|
160
|
-
});
|
|
161
|
-
const highPoint = projectPoint({
|
|
162
|
-
x: xValue,
|
|
163
|
-
y: range[1],
|
|
164
|
-
xScale,
|
|
165
|
-
yScale
|
|
166
|
-
});
|
|
170
|
+
const position = getPointOnScale(indexValue, indexScale);
|
|
171
|
+
const low = getPointOnScale(range[0], valueScale);
|
|
172
|
+
const high = getPointOnScale(range[1], valueScale);
|
|
167
173
|
return {
|
|
168
|
-
x:
|
|
169
|
-
|
|
170
|
-
|
|
174
|
+
x: categoryAxisIsX ? position : 0,
|
|
175
|
+
y: !categoryAxisIsX ? position : 0,
|
|
176
|
+
low,
|
|
177
|
+
high,
|
|
171
178
|
isValid: true
|
|
172
179
|
};
|
|
173
180
|
});
|
|
@@ -175,16 +182,25 @@ export const getAreaPath = _ref2 => {
|
|
|
175
182
|
// When connectNulls is true, filter out invalid points before rendering
|
|
176
183
|
// When false, use defined() to create gaps in the area
|
|
177
184
|
const filteredPoints = connectNulls ? dataPoints.filter(d => d.isValid) : dataPoints;
|
|
178
|
-
const areaGenerator = d3Area()
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
const areaGenerator = d3Area();
|
|
186
|
+
if (categoryAxisIsX) {
|
|
187
|
+
areaGenerator.x(d => d.x).y0(d => {
|
|
188
|
+
var _d$low;
|
|
189
|
+
return (_d$low = d.low) != null ? _d$low : 0;
|
|
190
|
+
}).y1(d => {
|
|
191
|
+
var _d$high;
|
|
192
|
+
return (_d$high = d.high) != null ? _d$high : 0;
|
|
193
|
+
});
|
|
194
|
+
} else {
|
|
195
|
+
areaGenerator.y(d => d.y).x0(d => {
|
|
196
|
+
var _d$low2;
|
|
197
|
+
return (_d$low2 = d.low) != null ? _d$low2 : 0;
|
|
198
|
+
}).x1(d => {
|
|
199
|
+
var _d$high2;
|
|
200
|
+
return (_d$high2 = d.high) != null ? _d$high2 : 0;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
areaGenerator.curve(curveFunction).defined(d => connectNulls || d.isValid && d.low != null && d.high != null);
|
|
188
204
|
const result = areaGenerator(filteredPoints);
|
|
189
205
|
return result != null ? result : '';
|
|
190
206
|
};
|
|
@@ -216,22 +232,35 @@ export const lineToPath = (x1, y1, x2, y2) => {
|
|
|
216
232
|
* const roundedPath = getBarPath(10, 20, 50, 100, 8, true, false);
|
|
217
233
|
* ```
|
|
218
234
|
*/
|
|
219
|
-
export const getBarPath = (x, y, width, height, radius, roundTop, roundBottom)
|
|
235
|
+
export const getBarPath = function (x, y, width, height, radius, roundTop, roundBottom, layout) {
|
|
236
|
+
if (layout === void 0) {
|
|
237
|
+
layout = 'vertical';
|
|
238
|
+
}
|
|
239
|
+
const barsGrowVertically = layout !== 'horizontal';
|
|
220
240
|
const roundBothSides = roundTop && roundBottom;
|
|
221
241
|
const r = Math.min(radius, width / 2, roundBothSides ? height / 2 : height);
|
|
222
|
-
|
|
223
|
-
|
|
242
|
+
|
|
243
|
+
// In vertical layout (bars grow up/down):
|
|
244
|
+
// - roundTop rounds the top face (min Y)
|
|
245
|
+
// - roundBottom rounds the bottom face (max Y)
|
|
246
|
+
// In horizontal layout (bars grow left/right):
|
|
247
|
+
// - roundTop rounds the right face (max X)
|
|
248
|
+
// - roundBottom rounds the left face (min X)
|
|
249
|
+
const rTL = barsGrowVertically ? roundTop ? r : 0 : roundBottom ? r : 0;
|
|
250
|
+
const rTR = barsGrowVertically ? roundTop ? r : 0 : roundTop ? r : 0;
|
|
251
|
+
const rBR = barsGrowVertically ? roundBottom ? r : 0 : roundTop ? r : 0;
|
|
252
|
+
const rBL = barsGrowVertically ? roundBottom ? r : 0 : roundBottom ? r : 0;
|
|
224
253
|
|
|
225
254
|
// Build path with selective rounding
|
|
226
|
-
let path = "M " + (x +
|
|
227
|
-
path += " L " + (x + width -
|
|
228
|
-
path += " A " +
|
|
229
|
-
path += " L " + (x + width) + " " + (y + height -
|
|
230
|
-
path += " A " +
|
|
231
|
-
path += " L " + (x +
|
|
232
|
-
path += " A " +
|
|
233
|
-
path += " L " + x + " " + (y +
|
|
234
|
-
path += " A " +
|
|
255
|
+
let path = "M " + (x + rTL) + " " + y;
|
|
256
|
+
path += " L " + (x + width - rTR) + " " + y;
|
|
257
|
+
path += " A " + rTR + " " + rTR + " 0 0 1 " + (x + width) + " " + (y + rTR);
|
|
258
|
+
path += " L " + (x + width) + " " + (y + height - rBR);
|
|
259
|
+
path += " A " + rBR + " " + rBR + " 0 0 1 " + (x + width - rBR) + " " + (y + height);
|
|
260
|
+
path += " L " + (x + rBL) + " " + (y + height);
|
|
261
|
+
path += " A " + rBL + " " + rBL + " 0 0 1 " + x + " " + (y + height - rBL);
|
|
262
|
+
path += " L " + x + " " + (y + rTL);
|
|
263
|
+
path += " A " + rTL + " " + rTL + " 0 0 1 " + (x + rTL) + " " + y;
|
|
235
264
|
path += ' Z';
|
|
236
265
|
return path;
|
|
237
266
|
};
|
package/esm/chart/utils/point.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { applyBandScale, applySerializableScale, isCategoricalScale, isLogScale
|
|
1
|
+
import { applyBandScale, applySerializableScale, isCategoricalScale, isLogScale } from './scale';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Position a label should be placed relative to the point
|
|
@@ -164,7 +164,8 @@ export const projectPoints = _ref3 => {
|
|
|
164
164
|
xScale,
|
|
165
165
|
yScale,
|
|
166
166
|
xData,
|
|
167
|
-
yData
|
|
167
|
+
yData,
|
|
168
|
+
layout = 'vertical'
|
|
168
169
|
} = _ref3;
|
|
169
170
|
if (data.length === 0) {
|
|
170
171
|
return [];
|
|
@@ -182,28 +183,37 @@ export const projectPoints = _ref3 => {
|
|
|
182
183
|
});
|
|
183
184
|
}
|
|
184
185
|
|
|
185
|
-
//
|
|
186
|
-
|
|
186
|
+
// Determine values/scales based on role (index vs value) and layout.
|
|
187
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
188
|
+
const indexScale = categoryAxisIsX ? xScale : yScale;
|
|
189
|
+
const indexData = categoryAxisIsX ? xData : yData;
|
|
187
190
|
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const numericXData = xData;
|
|
196
|
-
xValue = (_numericXData$index = numericXData[index]) != null ? _numericXData$index : index;
|
|
191
|
+
// 1. Calculate position along the index axis (categorical or numeric domain).
|
|
192
|
+
let indexValue = index;
|
|
193
|
+
if (!isCategoricalScale(indexScale)) {
|
|
194
|
+
if (indexData && Array.isArray(indexData) && indexData.length > 0) {
|
|
195
|
+
if (typeof indexData[0] === 'number') {
|
|
196
|
+
var _indexData$index;
|
|
197
|
+
indexValue = (_indexData$index = indexData[index]) != null ? _indexData$index : index;
|
|
197
198
|
}
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
201
|
+
|
|
202
|
+
// 2. Calculate position along the value axis (measured magnitude).
|
|
203
|
+
const valueAsNumber = value;
|
|
204
|
+
|
|
205
|
+
// 3. Project final coordinates based on layout.
|
|
206
|
+
if (categoryAxisIsX) {
|
|
207
|
+
return projectPoint({
|
|
208
|
+
x: indexValue,
|
|
209
|
+
y: valueAsNumber,
|
|
210
|
+
xScale,
|
|
211
|
+
yScale
|
|
212
|
+
});
|
|
203
213
|
}
|
|
204
214
|
return projectPoint({
|
|
205
|
-
x:
|
|
206
|
-
y:
|
|
215
|
+
x: valueAsNumber,
|
|
216
|
+
y: indexValue,
|
|
207
217
|
xScale,
|
|
208
218
|
yScale
|
|
209
219
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coinbase/cds-mobile-visualization",
|
|
3
|
-
"version": "3.4.0-beta.
|
|
3
|
+
"version": "3.4.0-beta.23",
|
|
4
4
|
"description": "Coinbase Design System - Mobile Visualization Native",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"CHANGELOG"
|
|
37
37
|
],
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@coinbase/cds-common": "^8.
|
|
39
|
+
"@coinbase/cds-common": "^8.52.0",
|
|
40
40
|
"@coinbase/cds-lottie-files": "^3.3.4",
|
|
41
|
-
"@coinbase/cds-mobile": "^8.
|
|
41
|
+
"@coinbase/cds-mobile": "^8.52.0",
|
|
42
42
|
"@coinbase/cds-utils": "^2.3.5",
|
|
43
43
|
"@shopify/react-native-skia": "^1.12.4 || ^2.0.0",
|
|
44
44
|
"react": "^18.3.1",
|
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
"@babel/preset-env": "^7.28.0",
|
|
58
58
|
"@babel/preset-react": "^7.27.1",
|
|
59
59
|
"@babel/preset-typescript": "^7.27.1",
|
|
60
|
-
"@coinbase/cds-common": "^8.
|
|
60
|
+
"@coinbase/cds-common": "^8.52.0",
|
|
61
61
|
"@coinbase/cds-lottie-files": "^3.3.4",
|
|
62
|
-
"@coinbase/cds-mobile": "^8.
|
|
62
|
+
"@coinbase/cds-mobile": "^8.52.0",
|
|
63
63
|
"@coinbase/cds-utils": "^2.3.5",
|
|
64
64
|
"@shopify/react-native-skia": "1.12.4",
|
|
65
65
|
"@types/react": "^18.3.12",
|