@coinbase/cds-mobile-visualization 3.4.0-beta.13 → 3.4.0-beta.15
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 +14 -0
- package/dts/chart/CartesianChart.d.ts +35 -1
- package/dts/chart/CartesianChart.d.ts.map +1 -1
- package/dts/chart/bar/Bar.d.ts +4 -0
- package/dts/chart/bar/Bar.d.ts.map +1 -1
- package/dts/chart/bar/BarChart.d.ts +6 -3
- package/dts/chart/bar/BarChart.d.ts.map +1 -1
- package/dts/chart/bar/BarStack.d.ts +11 -2
- package/dts/chart/bar/BarStack.d.ts.map +1 -1
- package/dts/chart/index.d.ts +1 -0
- 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/LineChart.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/utils/chart.d.ts +18 -0
- package/dts/chart/utils/chart.d.ts.map +1 -1
- package/esm/chart/CartesianChart.js +40 -10
- package/esm/chart/PeriodSelector.js +5 -1
- package/esm/chart/__stories__/CartesianChart.stories.js +3 -3
- package/esm/chart/__stories__/PeriodSelector.stories.js +24 -0
- package/esm/chart/bar/Bar.js +2 -0
- package/esm/chart/bar/BarStack.js +8 -1
- package/esm/chart/index.js +1 -0
- 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/LineChart.js +2 -1
- package/esm/chart/line/__stories__/LineChart.stories.js +37 -4
- package/esm/chart/scrubber/Scrubber.js +2 -1
- package/esm/chart/utils/chart.js +13 -0
- package/package.json +5 -5
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
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); }
|
|
2
|
+
import { memo, useCallback, useMemo, useState } from 'react';
|
|
3
|
+
import { ScrollView } from 'react-native';
|
|
4
|
+
import { Chip } from '@coinbase/cds-mobile/chips';
|
|
5
|
+
import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
|
|
6
|
+
import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
|
|
7
|
+
import { Box, HStack, VStack } from '@coinbase/cds-mobile/layout';
|
|
8
|
+
import { TextLabel1, TextLabel2 } from '@coinbase/cds-mobile/typography';
|
|
9
|
+
import { Canvas, Group, Path as SkiaPath, Skia } from '@shopify/react-native-skia';
|
|
10
|
+
import { XAxis, YAxis } from '../../axis';
|
|
11
|
+
import { BarChart, BarPlot, DefaultBar } from '../../bar';
|
|
12
|
+
import { CartesianChart } from '../../CartesianChart';
|
|
13
|
+
import { LineChart } from '../../line';
|
|
14
|
+
import { Scrubber } from '../../scrubber';
|
|
15
|
+
import { getDottedAreaPath } from '../../utils/path';
|
|
16
|
+
import { DefaultLegendShape } from '../DefaultLegendShape';
|
|
17
|
+
import { Legend } from '../Legend';
|
|
18
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
19
|
+
const spectrumColors = ['blue40', 'green40', 'orange40', 'yellow40', 'gray40', 'indigo40', 'pink40', 'purple40', 'red40', 'teal40'];
|
|
20
|
+
const shapes = ['pill', 'circle', 'squircle', 'square'];
|
|
21
|
+
const Shapes = () => {
|
|
22
|
+
const theme = useTheme();
|
|
23
|
+
return /*#__PURE__*/_jsx(VStack, {
|
|
24
|
+
gap: 2,
|
|
25
|
+
children: shapes.map(shape => /*#__PURE__*/_jsx(HStack, {
|
|
26
|
+
gap: 1,
|
|
27
|
+
children: spectrumColors.map(color => /*#__PURE__*/_jsx(Box, {
|
|
28
|
+
style: {
|
|
29
|
+
width: 10,
|
|
30
|
+
justifyContent: 'center'
|
|
31
|
+
},
|
|
32
|
+
children: /*#__PURE__*/_jsx(DefaultLegendShape, {
|
|
33
|
+
color: "rgb(" + theme.spectrum[color] + ")",
|
|
34
|
+
shape: shape
|
|
35
|
+
})
|
|
36
|
+
}, color))
|
|
37
|
+
}, shape))
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
const BasicLegend = () => {
|
|
41
|
+
const theme = useTheme();
|
|
42
|
+
const pages = useMemo(() => ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G'], []);
|
|
43
|
+
const pageViews = useMemo(() => [2400, 1398, 9800, 3908, 4800, 3800, 4300], []);
|
|
44
|
+
const uniqueVisitors = useMemo(() => [4000, 3000, 2000, 2780, 1890, 2390, 3490], []);
|
|
45
|
+
const numberFormatter = useCallback(value => new Intl.NumberFormat('en-US', {
|
|
46
|
+
maximumFractionDigits: 0
|
|
47
|
+
}).format(value), []);
|
|
48
|
+
const chartAccessibilityLabel = "Website traffic across " + pages.length + " pages showing page views and unique visitors.";
|
|
49
|
+
return /*#__PURE__*/_jsx(LineChart, {
|
|
50
|
+
enableScrubbing: true,
|
|
51
|
+
legend: true,
|
|
52
|
+
showArea: true,
|
|
53
|
+
accessibilityLabel: chartAccessibilityLabel,
|
|
54
|
+
height: 150,
|
|
55
|
+
legendPosition: "right",
|
|
56
|
+
series: [{
|
|
57
|
+
id: 'pageViews',
|
|
58
|
+
data: pageViews,
|
|
59
|
+
color: "rgb(" + theme.spectrum.green40 + ")",
|
|
60
|
+
label: 'Page Views'
|
|
61
|
+
}, {
|
|
62
|
+
id: 'uniqueVisitors',
|
|
63
|
+
data: uniqueVisitors,
|
|
64
|
+
color: "rgb(" + theme.spectrum.purple40 + ")",
|
|
65
|
+
label: 'Unique Visitors',
|
|
66
|
+
areaType: 'dotted'
|
|
67
|
+
}],
|
|
68
|
+
width: "100%",
|
|
69
|
+
xAxis: {
|
|
70
|
+
data: pages
|
|
71
|
+
},
|
|
72
|
+
yAxis: {
|
|
73
|
+
showGrid: true,
|
|
74
|
+
tickLabelFormatter: numberFormatter
|
|
75
|
+
},
|
|
76
|
+
children: /*#__PURE__*/_jsx(Scrubber, {})
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
const Position = () => {
|
|
80
|
+
const theme = useTheme();
|
|
81
|
+
return /*#__PURE__*/_jsxs(CartesianChart, {
|
|
82
|
+
height: 200,
|
|
83
|
+
inset: {
|
|
84
|
+
bottom: 8,
|
|
85
|
+
left: 0,
|
|
86
|
+
right: 0,
|
|
87
|
+
top: 8
|
|
88
|
+
},
|
|
89
|
+
legend: /*#__PURE__*/_jsx(Legend, {
|
|
90
|
+
justifyContent: "flex-end"
|
|
91
|
+
}),
|
|
92
|
+
legendPosition: "bottom",
|
|
93
|
+
series: [{
|
|
94
|
+
id: 'revenue',
|
|
95
|
+
label: 'Revenue',
|
|
96
|
+
data: [455, 520, 380, 455, 285, 235],
|
|
97
|
+
yAxisId: 'revenue',
|
|
98
|
+
color: "rgb(" + theme.spectrum.yellow40 + ")",
|
|
99
|
+
legendShape: 'squircle'
|
|
100
|
+
}, {
|
|
101
|
+
id: 'profitMargin',
|
|
102
|
+
label: 'Profit Margin',
|
|
103
|
+
data: [23, 20, 16, 38, 12, 9],
|
|
104
|
+
yAxisId: 'profitMargin',
|
|
105
|
+
color: theme.color.fgPositive,
|
|
106
|
+
legendShape: 'squircle'
|
|
107
|
+
}],
|
|
108
|
+
width: "100%",
|
|
109
|
+
xAxis: {
|
|
110
|
+
data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
|
|
111
|
+
scaleType: 'band'
|
|
112
|
+
},
|
|
113
|
+
yAxis: [{
|
|
114
|
+
id: 'revenue',
|
|
115
|
+
domain: {
|
|
116
|
+
min: 0
|
|
117
|
+
}
|
|
118
|
+
}, {
|
|
119
|
+
id: 'profitMargin',
|
|
120
|
+
domain: {
|
|
121
|
+
max: 100,
|
|
122
|
+
min: 0
|
|
123
|
+
}
|
|
124
|
+
}],
|
|
125
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
126
|
+
showLine: true,
|
|
127
|
+
showTickMarks: true
|
|
128
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
129
|
+
showGrid: true,
|
|
130
|
+
showLine: true,
|
|
131
|
+
showTickMarks: true,
|
|
132
|
+
axisId: "revenue",
|
|
133
|
+
position: "left",
|
|
134
|
+
requestedTickCount: 5,
|
|
135
|
+
tickLabelFormatter: value => "$" + value + "k",
|
|
136
|
+
width: 60
|
|
137
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
138
|
+
showLine: true,
|
|
139
|
+
showTickMarks: true,
|
|
140
|
+
axisId: "profitMargin",
|
|
141
|
+
position: "right",
|
|
142
|
+
requestedTickCount: 5,
|
|
143
|
+
tickLabelFormatter: value => value + "%"
|
|
144
|
+
}), /*#__PURE__*/_jsx(BarPlot, {})]
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
const ShapeVariants = () => {
|
|
148
|
+
const theme = useTheme();
|
|
149
|
+
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
|
|
150
|
+
return /*#__PURE__*/_jsx(LineChart, {
|
|
151
|
+
legend: true,
|
|
152
|
+
showArea: true,
|
|
153
|
+
height: 200,
|
|
154
|
+
legendPosition: "left",
|
|
155
|
+
series: [{
|
|
156
|
+
id: 'pill',
|
|
157
|
+
label: 'Pill',
|
|
158
|
+
data: [120, 150, 130, 170, 160, 190],
|
|
159
|
+
color: "rgb(" + theme.spectrum.blue40 + ")",
|
|
160
|
+
legendShape: 'pill'
|
|
161
|
+
}, {
|
|
162
|
+
id: 'circle',
|
|
163
|
+
label: 'Circle',
|
|
164
|
+
data: [80, 110, 95, 125, 115, 140],
|
|
165
|
+
color: "rgb(" + theme.spectrum.green40 + ")",
|
|
166
|
+
legendShape: 'circle'
|
|
167
|
+
}, {
|
|
168
|
+
id: 'square',
|
|
169
|
+
label: 'Square',
|
|
170
|
+
data: [60, 85, 70, 100, 90, 115],
|
|
171
|
+
color: "rgb(" + theme.spectrum.orange40 + ")",
|
|
172
|
+
legendShape: 'square'
|
|
173
|
+
}, {
|
|
174
|
+
id: 'squircle',
|
|
175
|
+
label: 'Squircle',
|
|
176
|
+
data: [40, 60, 50, 75, 65, 85],
|
|
177
|
+
color: "rgb(" + theme.spectrum.purple40 + ")",
|
|
178
|
+
legendShape: 'squircle'
|
|
179
|
+
}],
|
|
180
|
+
width: "100%",
|
|
181
|
+
xAxis: {
|
|
182
|
+
data: months
|
|
183
|
+
},
|
|
184
|
+
yAxis: {
|
|
185
|
+
domain: {
|
|
186
|
+
min: 0
|
|
187
|
+
},
|
|
188
|
+
showGrid: true
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
const DynamicData = () => {
|
|
193
|
+
var _seriesConfig$0$data$, _seriesConfig$0$data;
|
|
194
|
+
const theme = useTheme();
|
|
195
|
+
const [scrubberPosition, setScrubberPosition] = useState();
|
|
196
|
+
const timeLabels = useMemo(() => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], []);
|
|
197
|
+
const seriesConfig = useMemo(() => [{
|
|
198
|
+
id: 'candidate-a',
|
|
199
|
+
label: 'Candidate A',
|
|
200
|
+
data: [48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 38],
|
|
201
|
+
color: "rgb(" + theme.spectrum.blue40 + ")",
|
|
202
|
+
legendShape: 'circle'
|
|
203
|
+
}, {
|
|
204
|
+
id: 'candidate-b',
|
|
205
|
+
label: 'Candidate B',
|
|
206
|
+
data: [null, null, null, 6, 10, 14, 18, 22, 26, 29, 32, 35],
|
|
207
|
+
color: "rgb(" + theme.spectrum.orange40 + ")",
|
|
208
|
+
legendShape: 'circle'
|
|
209
|
+
}, {
|
|
210
|
+
id: 'candidate-c',
|
|
211
|
+
label: 'Candidate C',
|
|
212
|
+
data: [52, 53, 54, 49, 46, 43, 40, 37, 34, 32, 30, 27],
|
|
213
|
+
color: "rgb(" + theme.spectrum.gray40 + ")",
|
|
214
|
+
legendShape: 'circle'
|
|
215
|
+
}], [theme.spectrum.blue40, theme.spectrum.gray40, theme.spectrum.orange40]);
|
|
216
|
+
const dataLength = (_seriesConfig$0$data$ = (_seriesConfig$0$data = seriesConfig[0].data) == null ? void 0 : _seriesConfig$0$data.length) != null ? _seriesConfig$0$data$ : 0;
|
|
217
|
+
const dataIndex = scrubberPosition != null ? scrubberPosition : dataLength - 1;
|
|
218
|
+
const chartAccessibilityLabel = "Candidate polling data over " + timeLabels.length + " months showing support percentages for 3 candidates.";
|
|
219
|
+
const ValueLegendEntry = useCallback(_ref => {
|
|
220
|
+
var _seriesData$data;
|
|
221
|
+
let {
|
|
222
|
+
seriesId,
|
|
223
|
+
label,
|
|
224
|
+
color,
|
|
225
|
+
shape
|
|
226
|
+
} = _ref;
|
|
227
|
+
const seriesData = seriesConfig.find(s => s.id === seriesId);
|
|
228
|
+
const rawValue = seriesData == null || (_seriesData$data = seriesData.data) == null ? void 0 : _seriesData$data[dataIndex];
|
|
229
|
+
const formattedValue = rawValue === null || rawValue === undefined ? '--' : Math.round(rawValue) + "%";
|
|
230
|
+
return /*#__PURE__*/_jsxs(HStack, {
|
|
231
|
+
gap: 1,
|
|
232
|
+
style: {
|
|
233
|
+
alignItems: 'center'
|
|
234
|
+
},
|
|
235
|
+
children: [/*#__PURE__*/_jsx(DefaultLegendShape, {
|
|
236
|
+
color: color,
|
|
237
|
+
shape: shape
|
|
238
|
+
}), /*#__PURE__*/_jsx(TextLabel2, {
|
|
239
|
+
children: label
|
|
240
|
+
}), /*#__PURE__*/_jsx(TextLabel1, {
|
|
241
|
+
children: formattedValue
|
|
242
|
+
})]
|
|
243
|
+
});
|
|
244
|
+
}, [seriesConfig, dataIndex]);
|
|
245
|
+
return /*#__PURE__*/_jsx(LineChart, {
|
|
246
|
+
enableScrubbing: true,
|
|
247
|
+
showArea: true,
|
|
248
|
+
accessibilityLabel: chartAccessibilityLabel,
|
|
249
|
+
height: 250,
|
|
250
|
+
legend: /*#__PURE__*/_jsx(Legend, {
|
|
251
|
+
EntryComponent: ValueLegendEntry,
|
|
252
|
+
justifyContent: "flex-start",
|
|
253
|
+
paddingX: 2
|
|
254
|
+
}),
|
|
255
|
+
legendPosition: "top",
|
|
256
|
+
onScrubberPositionChange: setScrubberPosition,
|
|
257
|
+
series: seriesConfig,
|
|
258
|
+
width: "100%",
|
|
259
|
+
xAxis: {
|
|
260
|
+
data: timeLabels
|
|
261
|
+
},
|
|
262
|
+
yAxis: {
|
|
263
|
+
domain: {
|
|
264
|
+
max: 100,
|
|
265
|
+
min: 0
|
|
266
|
+
},
|
|
267
|
+
showGrid: true,
|
|
268
|
+
tickLabelFormatter: value => value + "%"
|
|
269
|
+
},
|
|
270
|
+
children: /*#__PURE__*/_jsx(Scrubber, {})
|
|
271
|
+
});
|
|
272
|
+
};
|
|
273
|
+
const Interactive = () => {
|
|
274
|
+
const theme = useTheme();
|
|
275
|
+
const [emphasizedId, setEmphasizedId] = useState(null);
|
|
276
|
+
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
277
|
+
const seriesConfig = useMemo(() => [{
|
|
278
|
+
id: 'revenue',
|
|
279
|
+
label: 'Revenue',
|
|
280
|
+
data: [120, 150, 180, 165, 190, 210, 240, 220, 260, 280, 310, 350],
|
|
281
|
+
baseColor: 'blue'
|
|
282
|
+
}, {
|
|
283
|
+
id: 'expenses',
|
|
284
|
+
label: 'Expenses',
|
|
285
|
+
data: [80, 95, 110, 105, 120, 130, 145, 140, 155, 165, 180, 195],
|
|
286
|
+
baseColor: 'orange'
|
|
287
|
+
}, {
|
|
288
|
+
id: 'profit',
|
|
289
|
+
label: 'Profit',
|
|
290
|
+
data: [40, 55, 70, 60, 70, 80, 95, 80, 105, 115, 130, 155],
|
|
291
|
+
baseColor: 'green'
|
|
292
|
+
}], []);
|
|
293
|
+
const handleToggle = useCallback(seriesId => {
|
|
294
|
+
setEmphasizedId(prev => prev === seriesId ? null : seriesId);
|
|
295
|
+
}, []);
|
|
296
|
+
const ChipLegendEntry = /*#__PURE__*/memo(function ChipLegendEntry(_ref2) {
|
|
297
|
+
var _config$baseColor;
|
|
298
|
+
let {
|
|
299
|
+
seriesId,
|
|
300
|
+
label
|
|
301
|
+
} = _ref2;
|
|
302
|
+
const isEmphasized = emphasizedId === seriesId;
|
|
303
|
+
const config = seriesConfig.find(s => s.id === seriesId);
|
|
304
|
+
const baseColor = (_config$baseColor = config == null ? void 0 : config.baseColor) != null ? _config$baseColor : 'gray';
|
|
305
|
+
const color10 = theme.spectrum[baseColor + "10"];
|
|
306
|
+
const color50 = theme.spectrum[baseColor + "50"];
|
|
307
|
+
const color90 = theme.spectrum[baseColor + "90"];
|
|
308
|
+
return /*#__PURE__*/_jsx(Chip, {
|
|
309
|
+
compact: true,
|
|
310
|
+
accessibilityLabel: (isEmphasized ? 'Remove emphasis from' : 'Emphasize') + " " + label + " series",
|
|
311
|
+
background: "transparent",
|
|
312
|
+
onPress: () => handleToggle(seriesId),
|
|
313
|
+
style: {
|
|
314
|
+
backgroundColor: "rgb(" + (isEmphasized ? color90 : color10) + ")",
|
|
315
|
+
borderWidth: 0,
|
|
316
|
+
borderRadius: theme.borderRadius[1000]
|
|
317
|
+
},
|
|
318
|
+
children: /*#__PURE__*/_jsxs(HStack, {
|
|
319
|
+
gap: 1,
|
|
320
|
+
style: {
|
|
321
|
+
alignItems: 'center'
|
|
322
|
+
},
|
|
323
|
+
children: [/*#__PURE__*/_jsx(DefaultLegendShape, {
|
|
324
|
+
color: "rgb(" + color50 + ")"
|
|
325
|
+
}), /*#__PURE__*/_jsx(TextLabel2, {
|
|
326
|
+
color: isEmphasized ? 'bg' : 'fg',
|
|
327
|
+
children: label
|
|
328
|
+
})]
|
|
329
|
+
})
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
const series = useMemo(() => {
|
|
333
|
+
return seriesConfig.map(config => {
|
|
334
|
+
const isEmphasized = emphasizedId === config.id;
|
|
335
|
+
const isDimmed = emphasizedId !== null && !isEmphasized;
|
|
336
|
+
return {
|
|
337
|
+
id: config.id,
|
|
338
|
+
label: config.label,
|
|
339
|
+
data: config.data,
|
|
340
|
+
color: "rgb(" + theme.spectrum[config.baseColor + "40"] + ")",
|
|
341
|
+
opacity: isDimmed ? 0.3 : 1
|
|
342
|
+
};
|
|
343
|
+
});
|
|
344
|
+
}, [emphasizedId, seriesConfig, theme]);
|
|
345
|
+
return /*#__PURE__*/_jsx(LineChart, {
|
|
346
|
+
showArea: true,
|
|
347
|
+
height: 300,
|
|
348
|
+
legend: /*#__PURE__*/_jsx(Legend, {
|
|
349
|
+
EntryComponent: ChipLegendEntry,
|
|
350
|
+
gap: 1,
|
|
351
|
+
paddingTop: 1
|
|
352
|
+
}),
|
|
353
|
+
legendPosition: "top",
|
|
354
|
+
series: series,
|
|
355
|
+
width: "100%",
|
|
356
|
+
xAxis: {
|
|
357
|
+
data: months
|
|
358
|
+
},
|
|
359
|
+
yAxis: {
|
|
360
|
+
domain: {
|
|
361
|
+
min: 0
|
|
362
|
+
},
|
|
363
|
+
showGrid: true,
|
|
364
|
+
tickLabelFormatter: value => "$" + value + "k"
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
};
|
|
368
|
+
const Accessible = () => {
|
|
369
|
+
const theme = useTheme();
|
|
370
|
+
const months = useMemo(() => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], []);
|
|
371
|
+
return /*#__PURE__*/_jsx(LineChart, {
|
|
372
|
+
legend: true,
|
|
373
|
+
showArea: true,
|
|
374
|
+
height: 200,
|
|
375
|
+
legendAccessibilityLabel: "Financial performance chart, legend",
|
|
376
|
+
legendPosition: "bottom",
|
|
377
|
+
series: [{
|
|
378
|
+
id: 'revenue',
|
|
379
|
+
label: 'Revenue',
|
|
380
|
+
data: [120, 150, 180, 165, 190, 210],
|
|
381
|
+
color: "rgb(" + theme.spectrum.green40 + ")"
|
|
382
|
+
}, {
|
|
383
|
+
id: 'expenses',
|
|
384
|
+
label: 'Expenses',
|
|
385
|
+
data: [80, 95, 110, 105, 120, 130],
|
|
386
|
+
color: "rgb(" + theme.spectrum.orange40 + ")"
|
|
387
|
+
}],
|
|
388
|
+
width: "100%",
|
|
389
|
+
xAxis: {
|
|
390
|
+
data: months
|
|
391
|
+
},
|
|
392
|
+
yAxis: {
|
|
393
|
+
domain: {
|
|
394
|
+
min: 0
|
|
395
|
+
},
|
|
396
|
+
showGrid: true
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
};
|
|
400
|
+
const LegendShapes = () => {
|
|
401
|
+
const theme = useTheme();
|
|
402
|
+
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
403
|
+
|
|
404
|
+
// Actual revenue (first 9 months)
|
|
405
|
+
const actualRevenue = [320, 380, 420, 390, 450, 480, 520, 490, 540, null, null, null];
|
|
406
|
+
|
|
407
|
+
// Forecasted revenue (last 3 months)
|
|
408
|
+
const forecastRevenue = [null, null, null, null, null, null, null, null, null, 580, 620, 680];
|
|
409
|
+
const numberFormatter = useCallback(value => "$" + new Intl.NumberFormat('en-US', {
|
|
410
|
+
maximumFractionDigits: 0
|
|
411
|
+
}).format(value) + "k", []);
|
|
412
|
+
|
|
413
|
+
// Pattern settings for dotted fill
|
|
414
|
+
const patternSize = 4;
|
|
415
|
+
const dotSize = 1;
|
|
416
|
+
|
|
417
|
+
// Custom legend indicator that matches the dotted bar pattern
|
|
418
|
+
const DottedLegendIndicator = useMemo(() => {
|
|
419
|
+
// Create a small dotted pattern for the legend indicator
|
|
420
|
+
const indicatorSize = 10;
|
|
421
|
+
const legendPatternSize = patternSize / 2;
|
|
422
|
+
const legendDotSize = dotSize / 2;
|
|
423
|
+
const dottedPath = getDottedAreaPath({
|
|
424
|
+
x: 1,
|
|
425
|
+
y: 1,
|
|
426
|
+
width: indicatorSize - 2,
|
|
427
|
+
height: indicatorSize - 2
|
|
428
|
+
}, legendPatternSize, legendDotSize);
|
|
429
|
+
const skiaPath = Skia.Path.MakeFromSVGString(dottedPath);
|
|
430
|
+
// Create squircle path for clipping
|
|
431
|
+
const squirclePath = Skia.Path.Make();
|
|
432
|
+
squirclePath.addRRect(Skia.RRectXY(Skia.XYWHRect(1, 1, 8, 8), 2, 2));
|
|
433
|
+
return /*#__PURE__*/_jsxs(Canvas, {
|
|
434
|
+
style: {
|
|
435
|
+
width: indicatorSize,
|
|
436
|
+
height: indicatorSize
|
|
437
|
+
},
|
|
438
|
+
children: [/*#__PURE__*/_jsx(Group, {
|
|
439
|
+
clip: squirclePath,
|
|
440
|
+
children: skiaPath && /*#__PURE__*/_jsx(SkiaPath, {
|
|
441
|
+
color: theme.color.fgPositive,
|
|
442
|
+
path: skiaPath,
|
|
443
|
+
style: "fill"
|
|
444
|
+
})
|
|
445
|
+
}), /*#__PURE__*/_jsx(SkiaPath, {
|
|
446
|
+
color: theme.color.fgPositive,
|
|
447
|
+
path: squirclePath,
|
|
448
|
+
strokeWidth: 2,
|
|
449
|
+
style: "stroke"
|
|
450
|
+
})]
|
|
451
|
+
});
|
|
452
|
+
}, [theme.color.fgPositive]);
|
|
453
|
+
|
|
454
|
+
// Custom bar component that renders bars with dotted pattern fill
|
|
455
|
+
const DottedBarComponent = useMemo(() => {
|
|
456
|
+
return /*#__PURE__*/memo(function DottedBar(props) {
|
|
457
|
+
const {
|
|
458
|
+
x,
|
|
459
|
+
y,
|
|
460
|
+
width,
|
|
461
|
+
height,
|
|
462
|
+
fill,
|
|
463
|
+
d
|
|
464
|
+
} = props;
|
|
465
|
+
|
|
466
|
+
// Generate dotted path for this bar's bounds
|
|
467
|
+
const dottedPath = useMemo(() => {
|
|
468
|
+
return getDottedAreaPath({
|
|
469
|
+
x,
|
|
470
|
+
y,
|
|
471
|
+
width,
|
|
472
|
+
height
|
|
473
|
+
}, patternSize, dotSize);
|
|
474
|
+
}, [x, y, width, height]);
|
|
475
|
+
|
|
476
|
+
// Create Skia paths
|
|
477
|
+
const barClipPath = useMemo(() => {
|
|
478
|
+
var _Skia$Path$MakeFromSV;
|
|
479
|
+
return d ? (_Skia$Path$MakeFromSV = Skia.Path.MakeFromSVGString(d)) != null ? _Skia$Path$MakeFromSV : undefined : undefined;
|
|
480
|
+
}, [d]);
|
|
481
|
+
const dotsSkiaPath = useMemo(() => {
|
|
482
|
+
var _Skia$Path$MakeFromSV2;
|
|
483
|
+
return (_Skia$Path$MakeFromSV2 = Skia.Path.MakeFromSVGString(dottedPath)) != null ? _Skia$Path$MakeFromSV2 : undefined;
|
|
484
|
+
}, [dottedPath]);
|
|
485
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
486
|
+
children: [/*#__PURE__*/_jsx(Group, {
|
|
487
|
+
clip: barClipPath,
|
|
488
|
+
children: dotsSkiaPath && /*#__PURE__*/_jsx(SkiaPath, {
|
|
489
|
+
color: fill,
|
|
490
|
+
path: dotsSkiaPath,
|
|
491
|
+
style: "fill"
|
|
492
|
+
})
|
|
493
|
+
}), /*#__PURE__*/_jsx(DefaultBar, _extends({}, props, {
|
|
494
|
+
fill: undefined,
|
|
495
|
+
stroke: fill,
|
|
496
|
+
strokeWidth: 2
|
|
497
|
+
}))]
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
}, []);
|
|
501
|
+
return /*#__PURE__*/_jsx(BarChart, {
|
|
502
|
+
legend: true,
|
|
503
|
+
showXAxis: true,
|
|
504
|
+
showYAxis: true,
|
|
505
|
+
height: 250,
|
|
506
|
+
inset: 0,
|
|
507
|
+
legendPosition: "top",
|
|
508
|
+
series: [{
|
|
509
|
+
id: 'actual',
|
|
510
|
+
label: 'Historical',
|
|
511
|
+
data: actualRevenue,
|
|
512
|
+
color: theme.color.fgPositive,
|
|
513
|
+
legendShape: 'squircle',
|
|
514
|
+
stackId: 'revenue'
|
|
515
|
+
}, {
|
|
516
|
+
id: 'forecast',
|
|
517
|
+
label: 'Forecasted',
|
|
518
|
+
data: forecastRevenue,
|
|
519
|
+
color: theme.color.fgPositive,
|
|
520
|
+
legendShape: DottedLegendIndicator,
|
|
521
|
+
stackId: 'revenue',
|
|
522
|
+
BarComponent: DottedBarComponent
|
|
523
|
+
}],
|
|
524
|
+
xAxis: {
|
|
525
|
+
data: months,
|
|
526
|
+
scaleType: 'band',
|
|
527
|
+
showLine: true,
|
|
528
|
+
showTickMarks: true
|
|
529
|
+
},
|
|
530
|
+
yAxis: {
|
|
531
|
+
domain: {
|
|
532
|
+
min: 0
|
|
533
|
+
},
|
|
534
|
+
showGrid: true,
|
|
535
|
+
showLine: true,
|
|
536
|
+
showTickMarks: true,
|
|
537
|
+
position: 'left',
|
|
538
|
+
tickLabelFormatter: numberFormatter,
|
|
539
|
+
width: 60
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
};
|
|
543
|
+
const LegendStories = () => {
|
|
544
|
+
return /*#__PURE__*/_jsx(ScrollView, {
|
|
545
|
+
children: /*#__PURE__*/_jsxs(ExampleScreen, {
|
|
546
|
+
children: [/*#__PURE__*/_jsx(Example, {
|
|
547
|
+
title: "Shapes",
|
|
548
|
+
children: /*#__PURE__*/_jsx(Shapes, {})
|
|
549
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
550
|
+
title: "Basic Legend",
|
|
551
|
+
children: /*#__PURE__*/_jsx(BasicLegend, {})
|
|
552
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
553
|
+
title: "Position",
|
|
554
|
+
children: /*#__PURE__*/_jsx(Position, {})
|
|
555
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
556
|
+
title: "Shape Variants",
|
|
557
|
+
children: /*#__PURE__*/_jsx(ShapeVariants, {})
|
|
558
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
559
|
+
title: "Dynamic Data",
|
|
560
|
+
children: /*#__PURE__*/_jsx(DynamicData, {})
|
|
561
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
562
|
+
title: "Interactive Legend",
|
|
563
|
+
children: /*#__PURE__*/_jsx(Interactive, {})
|
|
564
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
565
|
+
title: "Legend Shapes",
|
|
566
|
+
children: /*#__PURE__*/_jsx(LegendShapes, {})
|
|
567
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
568
|
+
title: "Accessible Legend",
|
|
569
|
+
children: /*#__PURE__*/_jsx(Accessible, {})
|
|
570
|
+
})]
|
|
571
|
+
})
|
|
572
|
+
});
|
|
573
|
+
};
|
|
574
|
+
export default LegendStories;
|
|
@@ -717,6 +717,36 @@ function StylingScrubber() {
|
|
|
717
717
|
})
|
|
718
718
|
});
|
|
719
719
|
}
|
|
720
|
+
function HideBeaconLabels() {
|
|
721
|
+
const theme = useTheme();
|
|
722
|
+
const pageViews = [2400, 1398, 9800, 3908, 4800, 3800, 4300];
|
|
723
|
+
const uniqueVisitors = [4000, 3000, 2000, 2780, 1890, 2390, 3490];
|
|
724
|
+
return /*#__PURE__*/_jsx(LineChart, {
|
|
725
|
+
enableScrubbing: true,
|
|
726
|
+
legend: true,
|
|
727
|
+
showArea: true,
|
|
728
|
+
height: 200,
|
|
729
|
+
inset: {
|
|
730
|
+
top: 60
|
|
731
|
+
},
|
|
732
|
+
series: [{
|
|
733
|
+
id: 'pageViews',
|
|
734
|
+
data: pageViews,
|
|
735
|
+
color: theme.color.accentBoldGreen,
|
|
736
|
+
label: 'Page Views'
|
|
737
|
+
}, {
|
|
738
|
+
id: 'uniqueVisitors',
|
|
739
|
+
data: uniqueVisitors,
|
|
740
|
+
color: theme.color.accentBoldPurple,
|
|
741
|
+
label: 'Unique Visitors'
|
|
742
|
+
}],
|
|
743
|
+
children: /*#__PURE__*/_jsx(Scrubber, {
|
|
744
|
+
hideBeaconLabels: true,
|
|
745
|
+
labelElevated: true,
|
|
746
|
+
label: dataIndex => "Day " + (dataIndex + 1)
|
|
747
|
+
})
|
|
748
|
+
});
|
|
749
|
+
}
|
|
720
750
|
function Compact() {
|
|
721
751
|
const theme = useTheme();
|
|
722
752
|
const dimensions = {
|
|
@@ -992,7 +1022,7 @@ const LegendDot = /*#__PURE__*/memo(props => {
|
|
|
992
1022
|
width: 10
|
|
993
1023
|
}, props));
|
|
994
1024
|
});
|
|
995
|
-
const
|
|
1025
|
+
const LegendEntry = /*#__PURE__*/memo(_ref0 => {
|
|
996
1026
|
let {
|
|
997
1027
|
color = assets.btc.color,
|
|
998
1028
|
label,
|
|
@@ -1037,15 +1067,15 @@ const PerformanceHeader = /*#__PURE__*/memo(_ref1 => {
|
|
|
1037
1067
|
return /*#__PURE__*/_jsxs(HStack, {
|
|
1038
1068
|
gap: 1,
|
|
1039
1069
|
paddingX: 1,
|
|
1040
|
-
children: [/*#__PURE__*/_jsx(
|
|
1070
|
+
children: [/*#__PURE__*/_jsx(LegendEntry, {
|
|
1041
1071
|
color: theme.color.fgPositive,
|
|
1042
1072
|
label: "High Price",
|
|
1043
1073
|
value: formatPriceThousands(sparklineTimePeriodDataValues[shownPosition] * 1.2)
|
|
1044
|
-
}), /*#__PURE__*/_jsx(
|
|
1074
|
+
}), /*#__PURE__*/_jsx(LegendEntry, {
|
|
1045
1075
|
color: assets.btc.color,
|
|
1046
1076
|
label: "Actual Price",
|
|
1047
1077
|
value: formatPriceThousands(sparklineTimePeriodDataValues[shownPosition])
|
|
1048
|
-
}), /*#__PURE__*/_jsx(
|
|
1078
|
+
}), /*#__PURE__*/_jsx(LegendEntry, {
|
|
1049
1079
|
color: theme.color.fgNegative,
|
|
1050
1080
|
label: "Low Price",
|
|
1051
1081
|
value: formatPriceThousands(sparklineTimePeriodDataValues[shownPosition] * 0.8)
|
|
@@ -2435,6 +2465,9 @@ function ExampleNavigator() {
|
|
|
2435
2465
|
}, {
|
|
2436
2466
|
title: 'Two-Line Scrubber Label',
|
|
2437
2467
|
component: /*#__PURE__*/_jsx(TwoLineScrubberLabel, {})
|
|
2468
|
+
}, {
|
|
2469
|
+
title: 'Hide Beacon Labels',
|
|
2470
|
+
component: /*#__PURE__*/_jsx(HideBeaconLabels, {})
|
|
2438
2471
|
}, {
|
|
2439
2472
|
title: 'Custom Beacon Stroke',
|
|
2440
2473
|
component: /*#__PURE__*/_jsx(CustomBeaconStroke, {})
|
|
@@ -16,6 +16,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
16
16
|
export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
|
|
17
17
|
let {
|
|
18
18
|
seriesIds,
|
|
19
|
+
hideBeaconLabels,
|
|
19
20
|
hideLine,
|
|
20
21
|
label,
|
|
21
22
|
lineStroke,
|
|
@@ -157,7 +158,7 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
157
158
|
seriesIds: filteredSeriesIds,
|
|
158
159
|
stroke: beaconStroke,
|
|
159
160
|
transitions: beaconTransitions
|
|
160
|
-
}), beaconLabels.length > 0 && /*#__PURE__*/_jsx(ScrubberBeaconLabelGroup, {
|
|
161
|
+
}), !hideBeaconLabels && beaconLabels.length > 0 && /*#__PURE__*/_jsx(ScrubberBeaconLabelGroup, {
|
|
161
162
|
BeaconLabelComponent: BeaconLabelComponent,
|
|
162
163
|
labelFont: beaconLabelFont,
|
|
163
164
|
labelHorizontalOffset: beaconLabelHorizontalOffset,
|
package/esm/chart/utils/chart.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { isSharedValue } from 'react-native-reanimated';
|
|
2
2
|
import { stack as d3Stack, stackOffsetDiverging, stackOrderNone } from 'd3-shape';
|
|
3
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
|
+
|
|
4
17
|
/**
|
|
5
18
|
* Type guard to check if bounds are complete with both min and max values.
|
|
6
19
|
* @param bounds - The bounds to validate
|