@coinbase/cds-mobile-visualization 3.4.0-beta.10 → 3.4.0-beta.11
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/axis/Axis.d.ts +19 -41
- package/dts/chart/axis/Axis.d.ts.map +1 -1
- package/dts/chart/axis/XAxis.d.ts.map +1 -1
- package/dts/chart/axis/YAxis.d.ts.map +1 -1
- package/dts/chart/line/Line.d.ts +13 -11
- package/dts/chart/line/Line.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/point.d.ts +17 -12
- package/dts/chart/utils/point.d.ts.map +1 -1
- package/dts/chart/utils/scale.d.ts +11 -0
- package/dts/chart/utils/scale.d.ts.map +1 -1
- package/esm/chart/axis/Axis.js +5 -41
- package/esm/chart/axis/XAxis.js +102 -27
- package/esm/chart/axis/YAxis.js +100 -23
- package/esm/chart/axis/__stories__/Axis.stories.js +259 -0
- package/esm/chart/bar/__stories__/BarChart.stories.js +39 -0
- package/esm/chart/utils/axis.js +45 -29
- package/esm/chart/utils/point.js +64 -21
- package/esm/chart/utils/scale.js +13 -2
- package/package.json +5 -5
package/esm/chart/axis/YAxis.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
const _excluded = ["axisId", "position", "showGrid", "requestedTickCount", "ticks", "tickLabelFormatter", "TickLabelComponent", "GridLineComponent", "LineComponent", "TickMarkLineComponent", "tickMarkLabelGap", "minTickLabelGap", "showTickMarks", "showLine", "tickMarkSize", "tickInterval", "label", "labelGap", "width"];
|
|
1
|
+
const _excluded = ["axisId", "position", "showGrid", "requestedTickCount", "ticks", "tickLabelFormatter", "TickLabelComponent", "GridLineComponent", "LineComponent", "TickMarkLineComponent", "tickMarkLabelGap", "minTickLabelGap", "showTickMarks", "showLine", "tickMarkSize", "tickInterval", "label", "labelGap", "width", "bandGridLinePlacement", "bandTickMarkPlacement"];
|
|
2
2
|
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; }
|
|
3
3
|
import { memo, useCallback, useEffect, useId, useMemo } from 'react';
|
|
4
4
|
import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
|
|
5
5
|
import { Group, vec } from '@shopify/react-native-skia';
|
|
6
6
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
7
7
|
import { DottedLine } from '../line/DottedLine';
|
|
8
|
-
import { ReferenceLine } from '../line/ReferenceLine';
|
|
9
8
|
import { SolidLine } from '../line/SolidLine';
|
|
10
9
|
import { ChartText } from '../text/ChartText';
|
|
11
10
|
import { ChartTextGroup } from '../text/ChartTextGroup';
|
|
12
|
-
import { getAxisTicksData, isCategoricalScale, lineToPath } from '../utils';
|
|
11
|
+
import { getAxisTicksData, getPointOnScale, isCategoricalScale, lineToPath, toPointAnchor } from '../utils';
|
|
13
12
|
import { DefaultAxisTickLabel } from './DefaultAxisTickLabel';
|
|
14
13
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
15
14
|
const AXIS_WIDTH = 44;
|
|
@@ -34,13 +33,16 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
|
|
|
34
33
|
tickInterval,
|
|
35
34
|
label,
|
|
36
35
|
labelGap = 4,
|
|
37
|
-
width = label ? AXIS_WIDTH + LABEL_SIZE : AXIS_WIDTH
|
|
36
|
+
width = label ? AXIS_WIDTH + LABEL_SIZE : AXIS_WIDTH,
|
|
37
|
+
bandGridLinePlacement = 'edges',
|
|
38
|
+
bandTickMarkPlacement = 'middle'
|
|
38
39
|
} = _ref,
|
|
39
40
|
props = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
40
41
|
const theme = useTheme();
|
|
41
42
|
const registrationId = useId();
|
|
42
43
|
const {
|
|
43
44
|
animate,
|
|
45
|
+
drawingArea,
|
|
44
46
|
getYScale,
|
|
45
47
|
getYAxis,
|
|
46
48
|
registerAxis,
|
|
@@ -50,10 +52,6 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
|
|
|
50
52
|
const yScale = getYScale(axisId);
|
|
51
53
|
const yAxis = getYAxis(axisId);
|
|
52
54
|
const axisBounds = getAxisBounds(registrationId);
|
|
53
|
-
|
|
54
|
-
// Note: gridOpacity not currently used in Skia version
|
|
55
|
-
// const gridOpacity = useSharedValue(1);
|
|
56
|
-
|
|
57
55
|
useEffect(() => {
|
|
58
56
|
registerAxis(registrationId, position, width);
|
|
59
57
|
return () => unregisterAxis(registrationId);
|
|
@@ -102,6 +100,72 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
|
|
|
102
100
|
tickInterval: tickInterval
|
|
103
101
|
});
|
|
104
102
|
}, [ticks, yScale, requestedTickCount, tickInterval, yAxis == null ? void 0 : yAxis.data]);
|
|
103
|
+
const isBandScale = useMemo(() => {
|
|
104
|
+
if (!yScale) return false;
|
|
105
|
+
return isCategoricalScale(yScale);
|
|
106
|
+
}, [yScale]);
|
|
107
|
+
|
|
108
|
+
// Compute grid line positions (including bounds closing line for band scales)
|
|
109
|
+
const gridLinePositions = useMemo(() => {
|
|
110
|
+
if (!yScale) return [];
|
|
111
|
+
return ticksData.flatMap((tick, index) => {
|
|
112
|
+
if (!isBandScale) {
|
|
113
|
+
return [{
|
|
114
|
+
y: tick.position,
|
|
115
|
+
key: "grid-" + tick.tick + "-" + index
|
|
116
|
+
}];
|
|
117
|
+
}
|
|
118
|
+
const bandScale = yScale;
|
|
119
|
+
const isLastTick = index === ticksData.length - 1;
|
|
120
|
+
const isEdges = bandGridLinePlacement === 'edges';
|
|
121
|
+
const startY = getPointOnScale(tick.tick, bandScale, toPointAnchor(bandGridLinePlacement));
|
|
122
|
+
const positions = [{
|
|
123
|
+
y: startY,
|
|
124
|
+
key: "grid-" + tick.tick + "-" + index
|
|
125
|
+
}];
|
|
126
|
+
|
|
127
|
+
// For edges on last tick, add the closing line at stepEnd
|
|
128
|
+
if (isLastTick && isEdges) {
|
|
129
|
+
const endY = getPointOnScale(tick.tick, bandScale, 'stepEnd');
|
|
130
|
+
positions.push({
|
|
131
|
+
y: endY,
|
|
132
|
+
key: "grid-" + tick.tick + "-" + index + "-end"
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return positions;
|
|
136
|
+
});
|
|
137
|
+
}, [ticksData, yScale, isBandScale, bandGridLinePlacement]);
|
|
138
|
+
|
|
139
|
+
// Compute tick mark positions (including bounds closing tick for band scales)
|
|
140
|
+
const tickMarkPositions = useMemo(() => {
|
|
141
|
+
if (!yScale) return [];
|
|
142
|
+
return ticksData.flatMap((tick, index) => {
|
|
143
|
+
if (!isBandScale) {
|
|
144
|
+
return [{
|
|
145
|
+
y: tick.position,
|
|
146
|
+
key: "tick-mark-" + tick.tick + "-" + index
|
|
147
|
+
}];
|
|
148
|
+
}
|
|
149
|
+
const bandScale = yScale;
|
|
150
|
+
const isLastTick = index === ticksData.length - 1;
|
|
151
|
+
const isEdges = bandTickMarkPlacement === 'edges';
|
|
152
|
+
const startY = getPointOnScale(tick.tick, bandScale, toPointAnchor(bandTickMarkPlacement));
|
|
153
|
+
const positions = [{
|
|
154
|
+
y: startY,
|
|
155
|
+
key: "tick-mark-" + tick.tick + "-" + index
|
|
156
|
+
}];
|
|
157
|
+
|
|
158
|
+
// For edges on last tick, add the closing tick mark at stepEnd
|
|
159
|
+
if (isLastTick && isEdges) {
|
|
160
|
+
const endY = getPointOnScale(tick.tick, bandScale, 'stepEnd');
|
|
161
|
+
positions.push({
|
|
162
|
+
y: endY,
|
|
163
|
+
key: "tick-mark-" + tick.tick + "-" + index + "-end"
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return positions;
|
|
167
|
+
});
|
|
168
|
+
}, [ticksData, yScale, isBandScale, bandTickMarkPlacement]);
|
|
105
169
|
const chartTextData = useMemo(() => {
|
|
106
170
|
if (!axisBounds) return null;
|
|
107
171
|
return ticksData.map(tick => {
|
|
@@ -122,17 +186,29 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
|
|
|
122
186
|
if (!yScale || !axisBounds) return;
|
|
123
187
|
const labelX = position === 'left' ? axisBounds.x + LABEL_SIZE / 2 : axisBounds.x + axisBounds.width - LABEL_SIZE / 2;
|
|
124
188
|
const labelY = axisBounds.y + axisBounds.height / 2;
|
|
189
|
+
|
|
190
|
+
// Pre-compute tick mark X coordinates
|
|
191
|
+
const tickXLeft = axisBounds.x;
|
|
192
|
+
const tickXRight = axisBounds.x + axisBounds.width;
|
|
193
|
+
const tickXStart = position === 'left' ? tickXRight : tickXLeft;
|
|
194
|
+
const tickXEnd = position === 'left' ? tickXRight - tickMarkSize : tickXLeft + tickMarkSize;
|
|
195
|
+
|
|
196
|
+
// Note: Unlike web, mobile renders grid lines and tick marks immediately without fade animation.
|
|
197
|
+
// This is because Skia can measure text dimensions synchronously, so there's no need to hide
|
|
198
|
+
// elements while waiting for measurements (web uses async ResizeObserver).
|
|
125
199
|
return /*#__PURE__*/_jsxs(Group, {
|
|
126
200
|
children: [showGrid && /*#__PURE__*/_jsx(Group, {
|
|
127
|
-
children:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
201
|
+
children: gridLinePositions.map(_ref2 => {
|
|
202
|
+
let {
|
|
203
|
+
y,
|
|
204
|
+
key
|
|
205
|
+
} = _ref2;
|
|
206
|
+
return /*#__PURE__*/_jsx(GridLineComponent, {
|
|
207
|
+
animate: false,
|
|
208
|
+
clipPath: null,
|
|
209
|
+
d: lineToPath(drawingArea.x, y, drawingArea.x + drawingArea.width, y),
|
|
210
|
+
stroke: theme.color.bgLine
|
|
211
|
+
}, key);
|
|
136
212
|
})
|
|
137
213
|
}), chartTextData && /*#__PURE__*/_jsx(ChartTextGroup, {
|
|
138
214
|
prioritizeEndLabels: true,
|
|
@@ -140,18 +216,19 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
|
|
|
140
216
|
labels: chartTextData,
|
|
141
217
|
minGap: minTickLabelGap
|
|
142
218
|
}), axisBounds && showTickMarks && /*#__PURE__*/_jsx(Group, {
|
|
143
|
-
children:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
219
|
+
children: tickMarkPositions.map(_ref3 => {
|
|
220
|
+
let {
|
|
221
|
+
y,
|
|
222
|
+
key
|
|
223
|
+
} = _ref3;
|
|
147
224
|
return /*#__PURE__*/_jsx(TickMarkLineComponent, {
|
|
148
225
|
animate: false,
|
|
149
226
|
clipPath: null,
|
|
150
|
-
d: lineToPath(
|
|
227
|
+
d: lineToPath(tickXStart, y, tickXEnd, y),
|
|
151
228
|
stroke: theme.color.fg,
|
|
152
229
|
strokeCap: "square",
|
|
153
230
|
strokeWidth: 1
|
|
154
|
-
},
|
|
231
|
+
}, key);
|
|
155
232
|
})
|
|
156
233
|
}), showLine && /*#__PURE__*/_jsx(LineComponent, {
|
|
157
234
|
animate: false,
|
|
@@ -2,6 +2,7 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
|
|
|
2
2
|
import { memo, useCallback, useMemo } from 'react';
|
|
3
3
|
import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
|
|
4
4
|
import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
|
|
5
|
+
import { BarPlot } from '../../bar';
|
|
5
6
|
import { CartesianChart } from '../../CartesianChart';
|
|
6
7
|
import { LineChart, SolidLine } from '../../line';
|
|
7
8
|
import { Line } from '../../line/Line';
|
|
@@ -194,6 +195,105 @@ const MultipleYAxesExample = () => /*#__PURE__*/_jsxs(CartesianChart, {
|
|
|
194
195
|
seriesId: "log"
|
|
195
196
|
}), /*#__PURE__*/_jsx(Scrubber, {})]
|
|
196
197
|
});
|
|
198
|
+
const AxesOnAllSides = () => {
|
|
199
|
+
const theme = useTheme();
|
|
200
|
+
const data = [30, 45, 60, 80, 55, 40, 65];
|
|
201
|
+
const labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
|
202
|
+
return /*#__PURE__*/_jsxs(CartesianChart, {
|
|
203
|
+
height: defaultChartHeight,
|
|
204
|
+
series: [{
|
|
205
|
+
id: 'data',
|
|
206
|
+
data,
|
|
207
|
+
color: theme.color.accentBoldBlue
|
|
208
|
+
}],
|
|
209
|
+
xAxis: {
|
|
210
|
+
data: labels
|
|
211
|
+
},
|
|
212
|
+
yAxis: {
|
|
213
|
+
domain: {
|
|
214
|
+
min: 0,
|
|
215
|
+
max: 100
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
219
|
+
showLine: true,
|
|
220
|
+
showTickMarks: true,
|
|
221
|
+
label: "Bottom Axis",
|
|
222
|
+
position: "bottom",
|
|
223
|
+
ticks: labels.map((label, index) => index)
|
|
224
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
225
|
+
showLine: true,
|
|
226
|
+
showTickMarks: true,
|
|
227
|
+
label: "Top Axis",
|
|
228
|
+
position: "top",
|
|
229
|
+
ticks: labels.map((label, index) => index)
|
|
230
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
231
|
+
showLine: true,
|
|
232
|
+
showTickMarks: true,
|
|
233
|
+
label: "Left Axis",
|
|
234
|
+
position: "left"
|
|
235
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
236
|
+
showLine: true,
|
|
237
|
+
showTickMarks: true,
|
|
238
|
+
label: "Right Axis",
|
|
239
|
+
position: "right"
|
|
240
|
+
}), /*#__PURE__*/_jsx(Line, {
|
|
241
|
+
curve: "natural",
|
|
242
|
+
seriesId: "data"
|
|
243
|
+
})]
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
const CustomTickMarkSizes = () => {
|
|
247
|
+
const theme = useTheme();
|
|
248
|
+
const data = [25, 50, 75, 60, 45, 80, 35];
|
|
249
|
+
return /*#__PURE__*/_jsxs(CartesianChart, {
|
|
250
|
+
height: 300,
|
|
251
|
+
series: [{
|
|
252
|
+
id: 'data',
|
|
253
|
+
data,
|
|
254
|
+
color: theme.color.accentBoldGreen
|
|
255
|
+
}],
|
|
256
|
+
xAxis: {
|
|
257
|
+
data: ['A', 'B', 'C', 'D', 'E', 'F', 'G']
|
|
258
|
+
},
|
|
259
|
+
yAxis: {
|
|
260
|
+
domain: {
|
|
261
|
+
min: 0,
|
|
262
|
+
max: 100
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
266
|
+
showLine: true,
|
|
267
|
+
showTickMarks: true,
|
|
268
|
+
label: "tickMarkSize=4 (default)",
|
|
269
|
+
tickMarkSize: 4
|
|
270
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
271
|
+
showLine: true,
|
|
272
|
+
showTickMarks: true,
|
|
273
|
+
height: 60,
|
|
274
|
+
label: "tickMarkSize=8",
|
|
275
|
+
position: "top",
|
|
276
|
+
tickMarkSize: 8
|
|
277
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
278
|
+
showLine: true,
|
|
279
|
+
showTickMarks: true,
|
|
280
|
+
label: "tickMarkSize=16",
|
|
281
|
+
position: "left",
|
|
282
|
+
tickMarkSize: 16,
|
|
283
|
+
width: 76
|
|
284
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
285
|
+
showLine: true,
|
|
286
|
+
showTickMarks: true,
|
|
287
|
+
label: "tickMarkSize=24",
|
|
288
|
+
position: "right",
|
|
289
|
+
tickMarkSize: 24,
|
|
290
|
+
width: 84
|
|
291
|
+
}), /*#__PURE__*/_jsx(Line, {
|
|
292
|
+
curve: "monotone",
|
|
293
|
+
seriesId: "data"
|
|
294
|
+
})]
|
|
295
|
+
});
|
|
296
|
+
};
|
|
197
297
|
const DomainLimitType = _ref => {
|
|
198
298
|
let {
|
|
199
299
|
limit
|
|
@@ -249,6 +349,97 @@ const DomainLimitType = _ref => {
|
|
|
249
349
|
}), /*#__PURE__*/_jsx(Scrubber, {})]
|
|
250
350
|
});
|
|
251
351
|
};
|
|
352
|
+
|
|
353
|
+
// Band scale with tick filtering - show every other tick
|
|
354
|
+
const BandScaleTickFiltering = () => /*#__PURE__*/_jsxs(CartesianChart, {
|
|
355
|
+
height: defaultChartHeight,
|
|
356
|
+
inset: 8,
|
|
357
|
+
series: [{
|
|
358
|
+
id: 'data',
|
|
359
|
+
data: [10, 22, 29, 45, 98, 45, 22, 35, 42, 18, 55, 67]
|
|
360
|
+
}],
|
|
361
|
+
xAxis: {
|
|
362
|
+
scaleType: 'band',
|
|
363
|
+
data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
364
|
+
},
|
|
365
|
+
yAxis: {
|
|
366
|
+
domain: {
|
|
367
|
+
min: 0
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
371
|
+
showGrid: true,
|
|
372
|
+
showLine: true,
|
|
373
|
+
showTickMarks: true,
|
|
374
|
+
label: "ticks={(i) => i % 2 === 0}",
|
|
375
|
+
ticks: i => i % 2 === 0
|
|
376
|
+
}), /*#__PURE__*/_jsx(BarPlot, {})]
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Band scale with explicit ticks array
|
|
380
|
+
const BandScaleExplicitTicks = () => /*#__PURE__*/_jsxs(CartesianChart, {
|
|
381
|
+
height: defaultChartHeight,
|
|
382
|
+
inset: 8,
|
|
383
|
+
series: [{
|
|
384
|
+
id: 'data',
|
|
385
|
+
data: [10, 22, 29, 45, 98, 45, 22]
|
|
386
|
+
}],
|
|
387
|
+
xAxis: {
|
|
388
|
+
scaleType: 'band',
|
|
389
|
+
data: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
390
|
+
},
|
|
391
|
+
yAxis: {
|
|
392
|
+
domain: {
|
|
393
|
+
min: 0
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
397
|
+
showGrid: true,
|
|
398
|
+
showLine: true,
|
|
399
|
+
showTickMarks: true,
|
|
400
|
+
label: "ticks={[0, 3, 6]} (first, middle, last)",
|
|
401
|
+
ticks: [0, 3, 6]
|
|
402
|
+
}), /*#__PURE__*/_jsx(BarPlot, {})]
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// Line chart on band scale - comparing grid placements
|
|
406
|
+
const LineChartOnBandScale = _ref2 => {
|
|
407
|
+
let {
|
|
408
|
+
bandGridLinePlacement
|
|
409
|
+
} = _ref2;
|
|
410
|
+
const theme = useTheme();
|
|
411
|
+
return /*#__PURE__*/_jsxs(CartesianChart, {
|
|
412
|
+
height: 180,
|
|
413
|
+
inset: 8,
|
|
414
|
+
series: [{
|
|
415
|
+
id: 'line1',
|
|
416
|
+
data: [10, 22, 29, 45, 98, 45, 22],
|
|
417
|
+
color: theme.color.accentBoldBlue
|
|
418
|
+
}],
|
|
419
|
+
xAxis: {
|
|
420
|
+
scaleType: 'band',
|
|
421
|
+
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
422
|
+
},
|
|
423
|
+
yAxis: {
|
|
424
|
+
domain: {
|
|
425
|
+
min: 0
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
429
|
+
showGrid: true,
|
|
430
|
+
showLine: true,
|
|
431
|
+
showTickMarks: true,
|
|
432
|
+
bandGridLinePlacement: bandGridLinePlacement,
|
|
433
|
+
bandTickMarkPlacement: bandGridLinePlacement,
|
|
434
|
+
label: "bandGridLinePlacement: " + bandGridLinePlacement
|
|
435
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
436
|
+
showGrid: true,
|
|
437
|
+
position: "left"
|
|
438
|
+
}), /*#__PURE__*/_jsx(Line, {
|
|
439
|
+
seriesId: "line1"
|
|
440
|
+
})]
|
|
441
|
+
});
|
|
442
|
+
};
|
|
252
443
|
const AxisStories = () => {
|
|
253
444
|
return /*#__PURE__*/_jsxs(ExampleScreen, {
|
|
254
445
|
children: [/*#__PURE__*/_jsx(Example, {
|
|
@@ -270,6 +461,74 @@ const AxisStories = () => {
|
|
|
270
461
|
children: /*#__PURE__*/_jsx(DomainLimitType, {
|
|
271
462
|
limit: "nice"
|
|
272
463
|
})
|
|
464
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
465
|
+
title: "Band Axis Grid Alignment",
|
|
466
|
+
children: /*#__PURE__*/_jsxs(CartesianChart, {
|
|
467
|
+
height: 350,
|
|
468
|
+
inset: 8,
|
|
469
|
+
series: [{
|
|
470
|
+
id: 'prices',
|
|
471
|
+
data: [10, 22, 29, 45, 98, 45, 22]
|
|
472
|
+
}],
|
|
473
|
+
xAxis: {
|
|
474
|
+
scaleType: 'band',
|
|
475
|
+
data: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
476
|
+
},
|
|
477
|
+
yAxis: {
|
|
478
|
+
domain: {
|
|
479
|
+
min: 0
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
483
|
+
showGrid: true,
|
|
484
|
+
showLine: true,
|
|
485
|
+
showTickMarks: true,
|
|
486
|
+
label: "Default"
|
|
487
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
488
|
+
showLine: true,
|
|
489
|
+
showTickMarks: true,
|
|
490
|
+
bandTickMarkPlacement: "start",
|
|
491
|
+
label: "Start"
|
|
492
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
493
|
+
showLine: true,
|
|
494
|
+
showTickMarks: true,
|
|
495
|
+
bandTickMarkPlacement: "end",
|
|
496
|
+
label: "End"
|
|
497
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
498
|
+
showLine: true,
|
|
499
|
+
showTickMarks: true,
|
|
500
|
+
bandTickMarkPlacement: "middle",
|
|
501
|
+
label: "Middle"
|
|
502
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
503
|
+
showLine: true,
|
|
504
|
+
showTickMarks: true,
|
|
505
|
+
bandTickMarkPlacement: "edges",
|
|
506
|
+
label: "Edges"
|
|
507
|
+
}), /*#__PURE__*/_jsx(BarPlot, {})]
|
|
508
|
+
})
|
|
509
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
510
|
+
title: "Band Scale - Tick Filtering",
|
|
511
|
+
children: /*#__PURE__*/_jsx(BandScaleTickFiltering, {})
|
|
512
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
513
|
+
title: "Band Scale - Explicit Ticks",
|
|
514
|
+
children: /*#__PURE__*/_jsx(BandScaleExplicitTicks, {})
|
|
515
|
+
}), /*#__PURE__*/_jsxs(Example, {
|
|
516
|
+
title: "Line Chart on Band Scale - Grid Positions",
|
|
517
|
+
children: [/*#__PURE__*/_jsx(LineChartOnBandScale, {
|
|
518
|
+
bandGridLinePlacement: "edges"
|
|
519
|
+
}), /*#__PURE__*/_jsx(LineChartOnBandScale, {
|
|
520
|
+
bandGridLinePlacement: "start"
|
|
521
|
+
}), /*#__PURE__*/_jsx(LineChartOnBandScale, {
|
|
522
|
+
bandGridLinePlacement: "middle"
|
|
523
|
+
}), /*#__PURE__*/_jsx(LineChartOnBandScale, {
|
|
524
|
+
bandGridLinePlacement: "end"
|
|
525
|
+
})]
|
|
526
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
527
|
+
title: "Axes on All Sides",
|
|
528
|
+
children: /*#__PURE__*/_jsx(AxesOnAllSides, {})
|
|
529
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
530
|
+
title: "Custom Tick Mark Sizes",
|
|
531
|
+
children: /*#__PURE__*/_jsx(CustomTickMarkSizes, {})
|
|
273
532
|
})]
|
|
274
533
|
});
|
|
275
534
|
};
|
|
@@ -621,6 +621,34 @@ const ColorMapWithOpacity = () => {
|
|
|
621
621
|
}
|
|
622
622
|
});
|
|
623
623
|
};
|
|
624
|
+
const BandGridPositionExample = _ref6 => {
|
|
625
|
+
let {
|
|
626
|
+
position
|
|
627
|
+
} = _ref6;
|
|
628
|
+
return /*#__PURE__*/_jsxs(CartesianChart, {
|
|
629
|
+
height: 180,
|
|
630
|
+
inset: 4,
|
|
631
|
+
series: [{
|
|
632
|
+
id: 'data',
|
|
633
|
+
data: [30, 50, 40, 60, 35]
|
|
634
|
+
}],
|
|
635
|
+
xAxis: {
|
|
636
|
+
scaleType: 'band',
|
|
637
|
+
data: ['A', 'B', 'C', 'D', 'E']
|
|
638
|
+
},
|
|
639
|
+
yAxis: {
|
|
640
|
+
domain: {
|
|
641
|
+
min: 0
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
645
|
+
showGrid: true,
|
|
646
|
+
showLine: true,
|
|
647
|
+
bandGridLinePlacement: position,
|
|
648
|
+
label: position
|
|
649
|
+
}), /*#__PURE__*/_jsx(BarPlot, {})]
|
|
650
|
+
});
|
|
651
|
+
};
|
|
624
652
|
const BarChartStories = () => {
|
|
625
653
|
return /*#__PURE__*/_jsxs(ExampleScreen, {
|
|
626
654
|
children: [/*#__PURE__*/_jsx(Example, {
|
|
@@ -662,6 +690,17 @@ const BarChartStories = () => {
|
|
|
662
690
|
}), /*#__PURE__*/_jsx(Example, {
|
|
663
691
|
title: "ColorMap with Opacity",
|
|
664
692
|
children: /*#__PURE__*/_jsx(ColorMapWithOpacity, {})
|
|
693
|
+
}), /*#__PURE__*/_jsxs(Example, {
|
|
694
|
+
title: "Band Grid Position",
|
|
695
|
+
children: [/*#__PURE__*/_jsx(BandGridPositionExample, {
|
|
696
|
+
position: "edges"
|
|
697
|
+
}), /*#__PURE__*/_jsx(BandGridPositionExample, {
|
|
698
|
+
position: "start"
|
|
699
|
+
}), /*#__PURE__*/_jsx(BandGridPositionExample, {
|
|
700
|
+
position: "middle"
|
|
701
|
+
}), /*#__PURE__*/_jsx(BandGridPositionExample, {
|
|
702
|
+
position: "end"
|
|
703
|
+
})]
|
|
665
704
|
})]
|
|
666
705
|
});
|
|
667
706
|
};
|
package/esm/chart/utils/axis.js
CHANGED
|
@@ -3,10 +3,41 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
|
|
|
3
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
4
|
import { useCallback, useMemo, useState } from 'react';
|
|
5
5
|
import { getChartDomain, getChartRange, isValidBounds } from './chart';
|
|
6
|
+
import { getPointOnScale } from './point';
|
|
6
7
|
import { getCategoricalScale, getNumericScale, isCategoricalScale, isNumericScale } from './scale';
|
|
7
8
|
export const defaultAxisId = 'DEFAULT_AXIS_ID';
|
|
8
9
|
export const defaultAxisScaleType = 'linear';
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Position options for band scale axis elements (grid lines, tick marks, labels).
|
|
13
|
+
*
|
|
14
|
+
* - `'start'` - At the start of each step (before bar padding)
|
|
15
|
+
* - `'middle'` - At the center of each band
|
|
16
|
+
* - `'end'` - At the end of each step (after bar padding)
|
|
17
|
+
* - `'edges'` - At start of each tick, plus end for the last tick (encloses the chart)
|
|
18
|
+
*
|
|
19
|
+
* @note These properties only apply when using a band scale (`scaleType: 'band'`).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Converts an AxisBandPlacement to the corresponding PointAnchor for use with getPointOnScale.
|
|
24
|
+
*
|
|
25
|
+
* @param placement - The axis placement value
|
|
26
|
+
* @returns The corresponding PointAnchor for scale calculations
|
|
27
|
+
*/
|
|
28
|
+
export const toPointAnchor = placement => {
|
|
29
|
+
switch (placement) {
|
|
30
|
+
case 'edges': // edges uses stepStart for each tick, stepEnd handled separately
|
|
31
|
+
case 'start':
|
|
32
|
+
return 'stepStart';
|
|
33
|
+
case 'end':
|
|
34
|
+
return 'stepEnd';
|
|
35
|
+
case 'middle':
|
|
36
|
+
default:
|
|
37
|
+
return 'middle';
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
10
41
|
/**
|
|
11
42
|
* Axis configuration with computed bounds
|
|
12
43
|
*/
|
|
@@ -457,6 +488,7 @@ const generateEvenlyDistributedTicks = (scale, tickInterval, possibleTickValues,
|
|
|
457
488
|
* // Returns tick positions centered in each selected band
|
|
458
489
|
*/
|
|
459
490
|
export const getAxisTicksData = _ref4 => {
|
|
491
|
+
var _options$anchor;
|
|
460
492
|
let {
|
|
461
493
|
ticks,
|
|
462
494
|
scaleFunction,
|
|
@@ -466,53 +498,37 @@ export const getAxisTicksData = _ref4 => {
|
|
|
466
498
|
tickInterval,
|
|
467
499
|
options
|
|
468
500
|
} = _ref4;
|
|
501
|
+
const anchor = (_options$anchor = options == null ? void 0 : options.anchor) != null ? _options$anchor : 'middle';
|
|
502
|
+
|
|
469
503
|
// Handle band scales
|
|
470
504
|
if (isCategoricalScale(scaleFunction)) {
|
|
505
|
+
const bandScale = scaleFunction;
|
|
506
|
+
|
|
471
507
|
// If explicit ticks are provided as array, use them
|
|
472
508
|
if (Array.isArray(ticks)) {
|
|
473
|
-
return ticks.filter(index => index >= 0 && index < categories.length).map(index => {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
if (position === undefined) return null;
|
|
478
|
-
return {
|
|
479
|
-
tick: index,
|
|
480
|
-
position: position + ((_bandwidth = scaleFunction.bandwidth == null ? void 0 : scaleFunction.bandwidth()) != null ? _bandwidth : 0) / 2
|
|
481
|
-
};
|
|
482
|
-
}).filter(Boolean);
|
|
509
|
+
return ticks.filter(index => index >= 0 && index < categories.length).map(index => ({
|
|
510
|
+
tick: index,
|
|
511
|
+
position: getPointOnScale(index, bandScale, anchor)
|
|
512
|
+
}));
|
|
483
513
|
}
|
|
484
514
|
|
|
485
515
|
// If a tick function is provided, use it to filter
|
|
486
516
|
if (typeof ticks === 'function') {
|
|
487
517
|
return categories.map((category, index) => {
|
|
488
|
-
var _bandwidth2;
|
|
489
518
|
if (!ticks(index)) return null;
|
|
490
|
-
|
|
491
|
-
// Band scales expect numeric indices, not category strings
|
|
492
|
-
const position = scaleFunction(index);
|
|
493
|
-
if (position === undefined) return null;
|
|
494
519
|
return {
|
|
495
520
|
tick: index,
|
|
496
|
-
position:
|
|
521
|
+
position: getPointOnScale(index, bandScale, anchor)
|
|
497
522
|
};
|
|
498
523
|
}).filter(Boolean);
|
|
499
524
|
}
|
|
500
|
-
if (typeof ticks === 'boolean' && !ticks) {
|
|
501
|
-
return [];
|
|
502
|
-
}
|
|
503
525
|
|
|
504
526
|
// For band scales without explicit ticks, show all categories
|
|
505
527
|
// requestedTickCount is ignored for categorical scales - use ticks parameter to control visibility
|
|
506
|
-
return categories.map((
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
if (position === undefined) return null;
|
|
511
|
-
return {
|
|
512
|
-
tick: index,
|
|
513
|
-
position: position + ((_bandwidth3 = scaleFunction.bandwidth == null ? void 0 : scaleFunction.bandwidth()) != null ? _bandwidth3 : 0) / 2
|
|
514
|
-
};
|
|
515
|
-
}).filter(Boolean);
|
|
528
|
+
return categories.map((_, index) => ({
|
|
529
|
+
tick: index,
|
|
530
|
+
position: getPointOnScale(index, bandScale, anchor)
|
|
531
|
+
}));
|
|
516
532
|
}
|
|
517
533
|
|
|
518
534
|
// Handle numeric scales
|