@pushwoosh/kit-charts 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/ai-documentation.md +378 -0
- package/core/Chart/Axes.d.ts +19 -0
- package/core/Chart/Axes.js +204 -0
- package/core/Chart/Chart.d.ts +33 -0
- package/core/Chart/Chart.js +616 -0
- package/core/Chart/Chart.styles.d.ts +109 -0
- package/core/Chart/Chart.styles.js +227 -0
- package/core/Chart/ChartErrorBoundary.d.ts +15 -0
- package/core/Chart/ChartErrorBoundary.js +27 -0
- package/core/Chart/Hinted.d.ts +11 -0
- package/core/Chart/Hinted.js +63 -0
- package/core/Chart/Legend.d.ts +15 -0
- package/core/Chart/Legend.js +92 -0
- package/core/Chart/PlotDecorations.d.ts +17 -0
- package/core/Chart/PlotDecorations.js +172 -0
- package/core/Chart/StackLabels.d.ts +6 -0
- package/core/Chart/StackLabels.js +85 -0
- package/core/Chart/Titles.d.ts +6 -0
- package/core/Chart/Titles.js +67 -0
- package/core/Chart/Tooltip.d.ts +13 -0
- package/core/Chart/Tooltip.js +87 -0
- package/core/Chart/useSize.d.ts +5 -0
- package/core/Chart/useSize.js +45 -0
- package/core/animation/motion.d.ts +20 -0
- package/core/animation/motion.js +20 -0
- package/core/animation/useTweenedNumbers.d.ts +1 -0
- package/core/animation/useTweenedNumbers.js +51 -0
- package/core/data/points.d.ts +14 -0
- package/core/data/points.js +73 -0
- package/core/format/time.d.ts +2 -0
- package/core/format/time.js +38 -0
- package/core/geometry/path.d.ts +19 -0
- package/core/geometry/path.js +76 -0
- package/core/layout/legend-layout.d.ts +21 -0
- package/core/layout/legend-layout.js +103 -0
- package/core/layout/measure-text.d.ts +9 -0
- package/core/layout/measure-text.js +57 -0
- package/core/layout/metrics.d.ts +63 -0
- package/core/layout/metrics.js +66 -0
- package/core/layout/point-width.d.ts +11 -0
- package/core/layout/point-width.js +86 -0
- package/core/layout/title-layout.d.ts +30 -0
- package/core/layout/title-layout.js +99 -0
- package/core/model/build-model.d.ts +69 -0
- package/core/model/build-model.js +556 -0
- package/core/model/palette.d.ts +15 -0
- package/core/model/palette.js +17 -0
- package/core/options/normalize.d.ts +8 -0
- package/core/options/normalize.js +44 -0
- package/core/registry/registry.d.ts +5 -0
- package/core/registry/registry.js +32 -0
- package/core/scale/band.d.ts +10 -0
- package/core/scale/band.js +33 -0
- package/core/scale/color.d.ts +9 -0
- package/core/scale/color.js +69 -0
- package/core/scale/linear.d.ts +17 -0
- package/core/scale/linear.js +64 -0
- package/core/scale/time.d.ts +19 -0
- package/core/scale/time.js +187 -0
- package/core/stack/stack.d.ts +9 -0
- package/core/stack/stack.js +76 -0
- package/core/types/formatter.d.ts +59 -0
- package/core/types/formatter.js +1 -0
- package/core/types/geometry.d.ts +16 -0
- package/core/types/geometry.js +1 -0
- package/core/types/options.d.ts +331 -0
- package/core/types/options.js +2 -0
- package/core/types/renderer.d.ts +101 -0
- package/core/types/renderer.js +1 -0
- package/core/types/scale.d.ts +34 -0
- package/core/types/scale.js +1 -0
- package/index.d.ts +16 -0
- package/index.js +10 -0
- package/package.json +64 -0
- package/presets/AreaChart.d.ts +3 -0
- package/presets/AreaChart.js +13 -0
- package/presets/ColumnChart.d.ts +3 -0
- package/presets/ColumnChart.js +13 -0
- package/presets/LineChart.d.ts +3 -0
- package/presets/LineChart.js +13 -0
- package/presets/PieChart.d.ts +3 -0
- package/presets/PieChart.js +13 -0
- package/presets/StackedColumnChart.d.ts +7 -0
- package/presets/StackedColumnChart.js +31 -0
- package/presets/index.d.ts +5 -0
- package/presets/index.js +5 -0
- package/series/area/area.d.ts +2 -0
- package/series/area/area.js +57 -0
- package/series/area/index.d.ts +1 -0
- package/series/area/index.js +1 -0
- package/series/column/column.d.ts +2 -0
- package/series/column/column.js +163 -0
- package/series/column/index.d.ts +1 -0
- package/series/column/index.js +1 -0
- package/series/heatmap/heatmap.d.ts +2 -0
- package/series/heatmap/heatmap.js +172 -0
- package/series/heatmap/index.d.ts +1 -0
- package/series/heatmap/index.js +1 -0
- package/series/line/index.d.ts +1 -0
- package/series/line/index.js +1 -0
- package/series/line/line.d.ts +2 -0
- package/series/line/line.js +49 -0
- package/series/pie/index.d.ts +1 -0
- package/series/pie/index.js +1 -0
- package/series/pie/pie.d.ts +2 -0
- package/series/pie/pie.js +224 -0
- package/series/shared/DataLabels.d.ts +14 -0
- package/series/shared/DataLabels.js +124 -0
- package/series/shared/Markers.d.ts +9 -0
- package/series/shared/Markers.js +34 -0
- package/series/shared/color.d.ts +1 -0
- package/series/shared/color.js +19 -0
- package/series/shared/domain.d.ts +3 -0
- package/series/shared/domain.js +14 -0
- package/series/shared/hit.d.ts +3 -0
- package/series/shared/hit.js +33 -0
- package/series/shared/legend.d.ts +3 -0
- package/series/shared/legend.js +12 -0
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
import { colorAt, DEFAULT_COLORS, DEFAULT_SURFACE_COLOR } from './palette';
|
|
2
|
+
import { normalizePoints } from '../data/points';
|
|
3
|
+
import { applyValueFormat, formatTime } from '../format/time';
|
|
4
|
+
import { measureLegend } from '../layout/legend-layout';
|
|
5
|
+
import { measureText, textHeight } from '../layout/measure-text';
|
|
6
|
+
import { AXIS_GUTTER_PADDING, AXIS_LABEL_GAP, AXIS_TITLE_GAP, MARKER_REACH, VALUE_LABEL_HEADROOM, X_AXIS_LABEL_DROP } from '../layout/metrics';
|
|
7
|
+
import { computeSlots } from '../layout/point-width';
|
|
8
|
+
import { layoutTitles } from '../layout/title-layout';
|
|
9
|
+
import { resolveAxes, resolveSeries, isSeriesVisible } from '../options/normalize';
|
|
10
|
+
import { createBandScale } from '../scale/band';
|
|
11
|
+
import { createColorScale } from '../scale/color';
|
|
12
|
+
import { createLinearScale } from '../scale/linear';
|
|
13
|
+
import { createTimeScale, defaultTimeFormat } from '../scale/time';
|
|
14
|
+
import { computeStacks } from '../stack/stack';
|
|
15
|
+
/** Key for a switched-off series, or for one of its points. */
|
|
16
|
+
export const hiddenKey = (seriesIndex, pointIndex) => pointIndex === undefined ? `${seriesIndex}` : `${seriesIndex}:${pointIndex}`;
|
|
17
|
+
const DEFAULT_SPACING = [10, 10, 15, 10];
|
|
18
|
+
const isBand = scale => scale.kind === 'band';
|
|
19
|
+
const isContinuous = scale => scale.kind === 'linear' || scale.kind === 'time';
|
|
20
|
+
function tickLabel(value, axis, timePattern, index, count, kind, numberFormat) {
|
|
21
|
+
var _axis$labels, _axis$labels2;
|
|
22
|
+
const formatter = (_axis$labels = axis.labels) === null || _axis$labels === void 0 ? void 0 : _axis$labels.formatter;
|
|
23
|
+
if (formatter) {
|
|
24
|
+
const node = formatter({
|
|
25
|
+
value,
|
|
26
|
+
index,
|
|
27
|
+
isFirst: index === 0,
|
|
28
|
+
isLast: index === count - 1,
|
|
29
|
+
axis: {
|
|
30
|
+
kind: kind,
|
|
31
|
+
min: undefined,
|
|
32
|
+
max: undefined
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (typeof node === 'string' || typeof node === 'number') {
|
|
36
|
+
return String(node);
|
|
37
|
+
}
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
if ((_axis$labels2 = axis.labels) !== null && _axis$labels2 !== void 0 && _axis$labels2.format) {
|
|
41
|
+
return applyValueFormat(axis.labels.format, value);
|
|
42
|
+
}
|
|
43
|
+
if (typeof value === 'number') {
|
|
44
|
+
return timePattern ? formatTime(timePattern, value) : numberFormat(value);
|
|
45
|
+
}
|
|
46
|
+
return String(value);
|
|
47
|
+
}
|
|
48
|
+
function collectCategories(axis, series, numeric = false) {
|
|
49
|
+
var _axis$categories;
|
|
50
|
+
if ((_axis$categories = axis.categories) !== null && _axis$categories !== void 0 && _axis$categories.length) {
|
|
51
|
+
return [...axis.categories];
|
|
52
|
+
}
|
|
53
|
+
const seen = [];
|
|
54
|
+
series.forEach(({
|
|
55
|
+
points
|
|
56
|
+
}) => {
|
|
57
|
+
points.forEach(point => {
|
|
58
|
+
if (typeof point.x !== 'string' && !numeric) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const label = String(point.x);
|
|
62
|
+
if (!seen.includes(label)) {
|
|
63
|
+
seen.push(label);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
return seen;
|
|
68
|
+
}
|
|
69
|
+
const emptyExtent = () => ({
|
|
70
|
+
min: Infinity,
|
|
71
|
+
max: -Infinity
|
|
72
|
+
});
|
|
73
|
+
const grow = (extent, value) => {
|
|
74
|
+
if (value === null || !Number.isFinite(value)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
extent.min = Math.min(extent.min, value);
|
|
78
|
+
extent.max = Math.max(extent.max, value);
|
|
79
|
+
};
|
|
80
|
+
const settled = (extent, fallback) => Number.isFinite(extent.min) && Number.isFinite(extent.max) ? [extent.min, extent.max] : fallback;
|
|
81
|
+
function buildXScale(axis, kind, extent, categories, range, hint) {
|
|
82
|
+
if (kind === 'band') {
|
|
83
|
+
return createBandScale({
|
|
84
|
+
domain: categories,
|
|
85
|
+
range,
|
|
86
|
+
reversed: axis.reversed === true
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
const [min, max] = settled(extent, kind === 'time' ? [0, 1] : [0, 1]);
|
|
90
|
+
const domain = [axis.min ?? min, axis.max ?? max];
|
|
91
|
+
if (kind === 'time') {
|
|
92
|
+
return createTimeScale({
|
|
93
|
+
domain,
|
|
94
|
+
range,
|
|
95
|
+
tickHint: hint,
|
|
96
|
+
tickUnit: typeof axis.tickInterval === 'string' ? axis.tickInterval : undefined
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return createLinearScale({
|
|
100
|
+
domain,
|
|
101
|
+
range,
|
|
102
|
+
tickHint: hint,
|
|
103
|
+
allowDecimals: axis.allowDecimals
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function buildYScale(axis, extent, range, hint, linked) {
|
|
107
|
+
var _axis$categories2;
|
|
108
|
+
if ((_axis$categories2 = axis.categories) !== null && _axis$categories2 !== void 0 && _axis$categories2.length) {
|
|
109
|
+
return createBandScale({
|
|
110
|
+
domain: [...axis.categories],
|
|
111
|
+
range: (linked === null || linked === void 0 ? void 0 : linked.kind) === 'band' ? linked.range() : range,
|
|
112
|
+
paddingInner: 0,
|
|
113
|
+
paddingOuter: 0,
|
|
114
|
+
// A y axis counts up from the bottom; `reversed` puts the first row on top.
|
|
115
|
+
reversed: axis.reversed !== true
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (linked) {
|
|
119
|
+
return linked;
|
|
120
|
+
}
|
|
121
|
+
const [min, max] = settled(extent, [0, 1]);
|
|
122
|
+
return createLinearScale({
|
|
123
|
+
domain: [axis.min ?? min, axis.max ?? max],
|
|
124
|
+
range,
|
|
125
|
+
// Only the end the caller left open may be rounded outwards; an explicit
|
|
126
|
+
// `min: 0` must not cost the other end its headroom for data labels.
|
|
127
|
+
nice: {
|
|
128
|
+
min: axis.min === undefined,
|
|
129
|
+
max: axis.max === undefined
|
|
130
|
+
},
|
|
131
|
+
tickHint: hint,
|
|
132
|
+
allowDecimals: axis.allowDecimals
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function buildColorScale(options, extent, colors) {
|
|
136
|
+
var _axis$stops;
|
|
137
|
+
const axis = options.colorAxis;
|
|
138
|
+
if (!axis) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
const [min, max] = settled(extent, [0, 1]);
|
|
142
|
+
const stops = (_axis$stops = axis.stops) !== null && _axis$stops !== void 0 && _axis$stops.length ? axis.stops : [[0, DEFAULT_SURFACE_COLOR], [1, colorAt(colors, 0)]];
|
|
143
|
+
return createColorScale({
|
|
144
|
+
domain: [axis.min ?? min, axis.max ?? max],
|
|
145
|
+
stops
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const EXPLICIT_ANCHOR = {
|
|
149
|
+
left: 'start',
|
|
150
|
+
center: 'middle',
|
|
151
|
+
right: 'end'
|
|
152
|
+
};
|
|
153
|
+
function tickAnchor(label, position, axis, bounds) {
|
|
154
|
+
var _axis$labels3, _axis$labels4;
|
|
155
|
+
const explicit = ((_axis$labels3 = axis.labels) === null || _axis$labels3 === void 0 ? void 0 : _axis$labels3.align) && EXPLICIT_ANCHOR[axis.labels.align];
|
|
156
|
+
if (explicit) {
|
|
157
|
+
return explicit;
|
|
158
|
+
}
|
|
159
|
+
if (!bounds || !label) {
|
|
160
|
+
return 'middle';
|
|
161
|
+
}
|
|
162
|
+
const half = measureText(label, (_axis$labels4 = axis.labels) === null || _axis$labels4 === void 0 ? void 0 : _axis$labels4.style) / 2;
|
|
163
|
+
if (position - half < bounds[0]) {
|
|
164
|
+
return 'start';
|
|
165
|
+
}
|
|
166
|
+
if (position + half > bounds[1]) {
|
|
167
|
+
return 'end';
|
|
168
|
+
}
|
|
169
|
+
return 'middle';
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Blanks the label of any tick whose text would run into the last one kept.
|
|
173
|
+
* Highcharts hides the text and leaves the tick, which is what keeps a dense
|
|
174
|
+
* axis readable instead of turning it into a smear.
|
|
175
|
+
*/
|
|
176
|
+
function dropOverlapping(ticks, axis, along) {
|
|
177
|
+
var _axis$labels5, _axis$labels6;
|
|
178
|
+
if (((_axis$labels5 = axis.labels) === null || _axis$labels5 === void 0 ? void 0 : _axis$labels5.allowOverlap) === true || ticks.length < 2) {
|
|
179
|
+
return ticks;
|
|
180
|
+
}
|
|
181
|
+
const style = (_axis$labels6 = axis.labels) === null || _axis$labels6 === void 0 ? void 0 : _axis$labels6.style;
|
|
182
|
+
// A y axis counts upwards, so its tick positions descend. Comparing distance
|
|
183
|
+
// rather than order keeps one rule for both directions.
|
|
184
|
+
let kept = null;
|
|
185
|
+
return ticks.map(tick => {
|
|
186
|
+
if (!tick.label) {
|
|
187
|
+
return tick;
|
|
188
|
+
}
|
|
189
|
+
const half = (along === 'x' ? measureText(tick.label, style) : textHeight(style)) / 2;
|
|
190
|
+
if (kept && Math.abs(tick.position - kept.position) < half + kept.half + AXIS_LABEL_GAP) {
|
|
191
|
+
return {
|
|
192
|
+
...tick,
|
|
193
|
+
label: ''
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
kept = {
|
|
197
|
+
position: tick.position,
|
|
198
|
+
half
|
|
199
|
+
};
|
|
200
|
+
return tick;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function axisTicks(axis, scale, hint, timePattern, along, numberFormat, bounds) {
|
|
204
|
+
var _axis$tickPositions;
|
|
205
|
+
let values;
|
|
206
|
+
if ((_axis$tickPositions = axis.tickPositions) !== null && _axis$tickPositions !== void 0 && _axis$tickPositions.length) {
|
|
207
|
+
values = [...axis.tickPositions];
|
|
208
|
+
} else if (isBand(scale)) {
|
|
209
|
+
values = scale.ticks();
|
|
210
|
+
} else if (typeof axis.tickInterval === 'number' && isContinuous(scale)) {
|
|
211
|
+
const [min, max] = scale.domain();
|
|
212
|
+
values = [];
|
|
213
|
+
for (let v = Math.ceil(min / axis.tickInterval) * axis.tickInterval; v <= max && values.length < 1000; v += axis.tickInterval) {
|
|
214
|
+
values.push(v);
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
values = isContinuous(scale) ? scale.ticks(hint) : [];
|
|
218
|
+
}
|
|
219
|
+
const offset = isBand(scale) ? scale.bandwidth() / 2 : 0;
|
|
220
|
+
const built = values.map((value, index) => {
|
|
221
|
+
var _axis$labels7, _axis$labels7$hint;
|
|
222
|
+
const position = (isBand(scale) ? scale.map(String(value)) : scale.map(Number(value))) + offset;
|
|
223
|
+
const label = tickLabel(value, axis, timePattern, index, values.length, scale.kind, numberFormat);
|
|
224
|
+
return {
|
|
225
|
+
value,
|
|
226
|
+
position,
|
|
227
|
+
label,
|
|
228
|
+
anchor: tickAnchor(label, position, axis, bounds),
|
|
229
|
+
hint: (_axis$labels7 = axis.labels) === null || _axis$labels7 === void 0 || (_axis$labels7$hint = _axis$labels7.hint) === null || _axis$labels7$hint === void 0 ? void 0 : _axis$labels7$hint.call(_axis$labels7, {
|
|
230
|
+
value,
|
|
231
|
+
index,
|
|
232
|
+
isFirst: index === 0,
|
|
233
|
+
isLast: index === values.length - 1,
|
|
234
|
+
axis: {
|
|
235
|
+
kind: scale.kind,
|
|
236
|
+
min: undefined,
|
|
237
|
+
max: undefined
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
};
|
|
241
|
+
});
|
|
242
|
+
return dropOverlapping(built, axis, along);
|
|
243
|
+
}
|
|
244
|
+
function halfStepPadding(series) {
|
|
245
|
+
const xs = new Set();
|
|
246
|
+
series.forEach(({
|
|
247
|
+
renderer,
|
|
248
|
+
points
|
|
249
|
+
}) => {
|
|
250
|
+
if (renderer.scales.x !== 'band') {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
points.forEach(point => {
|
|
254
|
+
if (typeof point.x === 'number' && Number.isFinite(point.x)) {
|
|
255
|
+
xs.add(point.x);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
if (xs.size < 2) {
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
262
|
+
const sorted = [...xs].sort((left, right) => left - right);
|
|
263
|
+
let step = Infinity;
|
|
264
|
+
for (let i = 1; i < sorted.length; i += 1) {
|
|
265
|
+
step = Math.min(step, sorted[i] - sorted[i - 1]);
|
|
266
|
+
}
|
|
267
|
+
return Number.isFinite(step) ? step / 2 : 0;
|
|
268
|
+
}
|
|
269
|
+
export function buildChartModel({
|
|
270
|
+
options,
|
|
271
|
+
size,
|
|
272
|
+
renderers,
|
|
273
|
+
hidden
|
|
274
|
+
}) {
|
|
275
|
+
var _options$colors, _xAxisOptions$categor, _resolved$, _options$chart, _xAxisOptions$labels, _xAxisOptions$labels2, _xAxisOptions$labels3, _xAxisOptions$labels4, _options$legend, _options$legend2, _yAxes$, _yAxes$2, _yAxes$3, _yAxes$4, _options$legend3, _options$chart2, _options$chart3, _options$chart4, _options$chart5;
|
|
276
|
+
const colors = (_options$colors = options.colors) !== null && _options$colors !== void 0 && _options$colors.length ? options.colors : DEFAULT_COLORS;
|
|
277
|
+
const numberFormat = options.numberFormat ?? String;
|
|
278
|
+
const {
|
|
279
|
+
xAxes,
|
|
280
|
+
yAxes
|
|
281
|
+
} = resolveAxes(options);
|
|
282
|
+
const xAxisOptions = xAxes[0] ?? {};
|
|
283
|
+
const all = [];
|
|
284
|
+
resolveSeries(options).forEach((series, index) => {
|
|
285
|
+
const renderer = renderers.get(series.type);
|
|
286
|
+
if (!renderer) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
all.push({
|
|
290
|
+
options: series,
|
|
291
|
+
renderer,
|
|
292
|
+
index,
|
|
293
|
+
color: series.color ?? colorAt(colors, index),
|
|
294
|
+
points: normalizePoints(series.data, series.name ?? `series ${index}`),
|
|
295
|
+
hiddenPoints: new Set(normalizePoints(series.data, series.name ?? `series ${index}`).filter(point => hidden === null || hidden === void 0 ? void 0 : hidden.has(hiddenKey(index, point.index))).map(point => point.index)),
|
|
296
|
+
hidden: !isSeriesVisible(series) || ((hidden === null || hidden === void 0 ? void 0 : hidden.has(hiddenKey(index))) ?? false)
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
// Everything that shapes the chart — domains, stacks, column slots — comes from
|
|
300
|
+
// the series on show; a hidden one only keeps its place in the legend.
|
|
301
|
+
const shown = all.filter(series => !series.hidden);
|
|
302
|
+
// With nothing left on show the axes would collapse to their fallback range and
|
|
303
|
+
// the series being switched off would jump there before it finished fading, so
|
|
304
|
+
// the chart holds the shape it had until something is switched back on.
|
|
305
|
+
const resolved = shown.length ? shown : all;
|
|
306
|
+
const explicitCategories = Boolean((_xAxisOptions$categor = xAxisOptions.categories) === null || _xAxisOptions$categor === void 0 ? void 0 : _xAxisOptions$categor.length);
|
|
307
|
+
const axisless = resolved.length > 0 && resolved.every(({
|
|
308
|
+
renderer
|
|
309
|
+
}) => renderer.scales.x === 'none');
|
|
310
|
+
const labelled = collectCategories(xAxisOptions, resolved);
|
|
311
|
+
const rendererXKind = (_resolved$ = resolved[0]) === null || _resolved$ === void 0 ? void 0 : _resolved$.renderer.scales.x;
|
|
312
|
+
const xKind = (() => {
|
|
313
|
+
if (xAxisOptions.type === 'datetime') {
|
|
314
|
+
return 'time';
|
|
315
|
+
}
|
|
316
|
+
if (xAxisOptions.type === 'category' || labelled.length > 0) {
|
|
317
|
+
return 'band';
|
|
318
|
+
}
|
|
319
|
+
return rendererXKind === 'band' ? 'band' : 'linear';
|
|
320
|
+
})();
|
|
321
|
+
const categories = xKind === 'band' && !labelled.length ? collectCategories(xAxisOptions, resolved, true) : labelled;
|
|
322
|
+
const stacks = computeStacks(resolved);
|
|
323
|
+
if (xKind === 'band') {
|
|
324
|
+
resolved.forEach(({
|
|
325
|
+
points
|
|
326
|
+
}) => {
|
|
327
|
+
points.forEach(point => {
|
|
328
|
+
if (typeof point.x === 'string') {
|
|
329
|
+
point.band = point.x;
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (!explicitCategories) {
|
|
333
|
+
point.band = String(point.x);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
point.band = categories[Number(point.x)] ?? categories[point.index] ?? String(point.x);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
resolved.forEach(({
|
|
340
|
+
points
|
|
341
|
+
}) => {
|
|
342
|
+
points.forEach(point => {
|
|
343
|
+
if (point.band !== undefined && !categories.includes(point.band)) {
|
|
344
|
+
categories.push(point.band);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
const xExtent = emptyExtent();
|
|
350
|
+
const colorExtent = emptyExtent();
|
|
351
|
+
const yExtents = yAxes.map(() => emptyExtent());
|
|
352
|
+
resolved.forEach(({
|
|
353
|
+
points,
|
|
354
|
+
options: series,
|
|
355
|
+
index,
|
|
356
|
+
hiddenPoints
|
|
357
|
+
}) => {
|
|
358
|
+
const yIndex = Math.min(series.yAxis ?? 0, yExtents.length - 1);
|
|
359
|
+
const stacked = stacks.get(index);
|
|
360
|
+
points.forEach((point, pointIndex) => {
|
|
361
|
+
if (hiddenPoints.has(point.index)) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (typeof point.x === 'number') {
|
|
365
|
+
grow(xExtent, point.x);
|
|
366
|
+
}
|
|
367
|
+
grow(colorExtent, point.value);
|
|
368
|
+
const cumulative = stacked === null || stacked === void 0 ? void 0 : stacked[pointIndex];
|
|
369
|
+
if (cumulative) {
|
|
370
|
+
grow(yExtents[yIndex], cumulative.from);
|
|
371
|
+
grow(yExtents[yIndex], cumulative.to);
|
|
372
|
+
} else {
|
|
373
|
+
grow(yExtents[yIndex], point.y);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
const seeded = {
|
|
378
|
+
x: {
|
|
379
|
+
...xExtent
|
|
380
|
+
},
|
|
381
|
+
y: yExtents.map(extent => ({
|
|
382
|
+
...extent
|
|
383
|
+
})),
|
|
384
|
+
color: {
|
|
385
|
+
...colorExtent
|
|
386
|
+
},
|
|
387
|
+
categories
|
|
388
|
+
};
|
|
389
|
+
const acc = resolved.reduce((current, {
|
|
390
|
+
renderer,
|
|
391
|
+
options: series
|
|
392
|
+
}) => renderer.extendDomain(series, current), seeded);
|
|
393
|
+
const bandOnContinuous = xKind !== 'band' && resolved.some(({
|
|
394
|
+
renderer
|
|
395
|
+
}) => renderer.scales.x === 'band');
|
|
396
|
+
const pad = bandOnContinuous ? halfStepPadding(resolved) : 0;
|
|
397
|
+
const xDomain = pad > 0 ? {
|
|
398
|
+
min: acc.x.min - pad,
|
|
399
|
+
max: acc.x.max + pad
|
|
400
|
+
} : acc.x;
|
|
401
|
+
const yDomains = acc.y;
|
|
402
|
+
const spacing = ((_options$chart = options.chart) === null || _options$chart === void 0 ? void 0 : _options$chart.spacing) ?? DEFAULT_SPACING;
|
|
403
|
+
const [spaceTop, spaceRight, spaceBottom, spaceLeft] = spacing;
|
|
404
|
+
const labelsOnTop = xAxisOptions.opposite === true;
|
|
405
|
+
const labelStyle = (_xAxisOptions$labels = xAxisOptions.labels) === null || _xAxisOptions$labels === void 0 ? void 0 : _xAxisOptions$labels.style;
|
|
406
|
+
// A tick label is drawn a tick's length plus a fixed drop away from the axis
|
|
407
|
+
// line, and `labels.y` pushes it further still. Reserving only its text height
|
|
408
|
+
// leaves it hanging over the chart edge, where the svg clips it.
|
|
409
|
+
const xLabelPush = labelsOnTop ? Math.max(0, -(((_xAxisOptions$labels2 = xAxisOptions.labels) === null || _xAxisOptions$labels2 === void 0 ? void 0 : _xAxisOptions$labels2.y) ?? 0)) : Math.max(0, ((_xAxisOptions$labels3 = xAxisOptions.labels) === null || _xAxisOptions$labels3 === void 0 ? void 0 : _xAxisOptions$labels3.y) ?? 0);
|
|
410
|
+
const xLabelHeight = axisless || ((_xAxisOptions$labels4 = xAxisOptions.labels) === null || _xAxisOptions$labels4 === void 0 ? void 0 : _xAxisOptions$labels4.enabled) === false ? 0 : textHeight(labelStyle) + (xAxisOptions.tickLength ?? 0) + (labelsOnTop ? AXIS_LABEL_GAP : X_AXIS_LABEL_DROP) + xLabelPush;
|
|
411
|
+
const legendEnabled = ((_options$legend = options.legend) === null || _options$legend === void 0 ? void 0 : _options$legend.enabled) !== false;
|
|
412
|
+
const legend = legendEnabled ? all.flatMap(({
|
|
413
|
+
renderer,
|
|
414
|
+
options: series,
|
|
415
|
+
index,
|
|
416
|
+
hidden: off
|
|
417
|
+
}) => {
|
|
418
|
+
const items = renderer.legendItems(series, index, colors);
|
|
419
|
+
return items.map((item, ordinal) => ({
|
|
420
|
+
...item,
|
|
421
|
+
color: item.color || colorAt(colors, items.length > 1 ? ordinal : index),
|
|
422
|
+
visible: !off && !(hidden !== null && hidden !== void 0 && hidden.has(hiddenKey(index, item.pointIndex)))
|
|
423
|
+
}));
|
|
424
|
+
}) : [];
|
|
425
|
+
const legendMetrics = measureLegend(legend, options.legend);
|
|
426
|
+
const legendHeight = legendMetrics.height;
|
|
427
|
+
const legendAtTop = (((_options$legend2 = options.legend) === null || _options$legend2 === void 0 ? void 0 : _options$legend2.verticalAlign) ?? 'bottom') === 'top';
|
|
428
|
+
const titles = layoutTitles(options, size, spacing);
|
|
429
|
+
const titleHeight = titles.height;
|
|
430
|
+
const yLabelsShown = !axisless && ((_yAxes$ = yAxes[0]) === null || _yAxes$ === void 0 ? void 0 : _yAxes$.visible) !== false && ((_yAxes$2 = yAxes[0]) === null || _yAxes$2 === void 0 || (_yAxes$2 = _yAxes$2.labels) === null || _yAxes$2 === void 0 ? void 0 : _yAxes$2.enabled) !== false;
|
|
431
|
+
const markerReach = resolved.some(({
|
|
432
|
+
options: series
|
|
433
|
+
}) => {
|
|
434
|
+
var _series$marker;
|
|
435
|
+
return ((_series$marker = series.marker) === null || _series$marker === void 0 ? void 0 : _series$marker.enabled) !== false;
|
|
436
|
+
}) ? MARKER_REACH : 0;
|
|
437
|
+
// The topmost tick label is centred on the plot's top edge, so half of it hangs
|
|
438
|
+
// above the plot: without this the highest grid line and its label sit on the
|
|
439
|
+
// chart border.
|
|
440
|
+
const topOverflow = Math.max(yLabelsShown ? Math.ceil(textHeight((_yAxes$3 = yAxes[0]) === null || _yAxes$3 === void 0 || (_yAxes$3 = _yAxes$3.labels) === null || _yAxes$3 === void 0 ? void 0 : _yAxes$3.style) / 2) : 0, axisless ? 0 : markerReach);
|
|
441
|
+
// Point and stack labels are drawn above the shape they belong to, so the plot
|
|
442
|
+
// has to end below the chart edge — otherwise the topmost value sits on the
|
|
443
|
+
// border, or gets pushed inside the shape to avoid being cut off.
|
|
444
|
+
const outsideLabels = axisless ? undefined : resolved.find(({
|
|
445
|
+
options: series,
|
|
446
|
+
renderer
|
|
447
|
+
}) => {
|
|
448
|
+
var _series$dataLabels;
|
|
449
|
+
return ((_series$dataLabels = series.dataLabels) === null || _series$dataLabels === void 0 ? void 0 : _series$dataLabels.enabled) && !series.stacking && renderer.scales.y === 'linear';
|
|
450
|
+
});
|
|
451
|
+
const stackLabels = yAxes.find(axis => {
|
|
452
|
+
var _axis$stackLabels;
|
|
453
|
+
return (_axis$stackLabels = axis.stackLabels) === null || _axis$stackLabels === void 0 ? void 0 : _axis$stackLabels.enabled;
|
|
454
|
+
});
|
|
455
|
+
const labelHeadroom = (() => {
|
|
456
|
+
if (outsideLabels) {
|
|
457
|
+
var _outsideLabels$option;
|
|
458
|
+
return textHeight((_outsideLabels$option = outsideLabels.options.dataLabels) === null || _outsideLabels$option === void 0 ? void 0 : _outsideLabels$option.style) + VALUE_LABEL_HEADROOM;
|
|
459
|
+
}
|
|
460
|
+
if (stackLabels && !axisless) {
|
|
461
|
+
var _stackLabels$stackLab;
|
|
462
|
+
return textHeight((_stackLabels$stackLab = stackLabels.stackLabels) === null || _stackLabels$stackLab === void 0 ? void 0 : _stackLabels$stackLab.style) + VALUE_LABEL_HEADROOM;
|
|
463
|
+
}
|
|
464
|
+
return 0;
|
|
465
|
+
})();
|
|
466
|
+
const marginTop = spaceTop + titleHeight + (legendAtTop ? legendHeight : 0) + (labelsOnTop ? xLabelHeight : 0) + Math.max(labelHeadroom, topOverflow);
|
|
467
|
+
const marginBottom = spaceBottom + (labelsOnTop ? 0 : xLabelHeight) + (legendAtTop ? 0 : legendHeight);
|
|
468
|
+
const measureGutter = estimate => {
|
|
469
|
+
var _axis$labels8, _axis$categories3, _axis$title;
|
|
470
|
+
const axis = yAxes[0] ?? {};
|
|
471
|
+
if (((_axis$labels8 = axis.labels) === null || _axis$labels8 === void 0 ? void 0 : _axis$labels8.enabled) === false) {
|
|
472
|
+
return estimate;
|
|
473
|
+
}
|
|
474
|
+
const labels = (_axis$categories3 = axis.categories) !== null && _axis$categories3 !== void 0 && _axis$categories3.length ? [...axis.categories] : axisTicks(axis, createLinearScale({
|
|
475
|
+
domain: (() => {
|
|
476
|
+
const [min, max] = settled(yDomains[0] ?? emptyExtent(), [0, 1]);
|
|
477
|
+
return [axis.min ?? min, axis.max ?? max];
|
|
478
|
+
})(),
|
|
479
|
+
range: [size.height - marginBottom, marginTop],
|
|
480
|
+
nice: true,
|
|
481
|
+
tickHint: axis.tickAmount ?? 5,
|
|
482
|
+
allowDecimals: axis.allowDecimals
|
|
483
|
+
}), axis.tickAmount ?? 5, undefined, 'y', numberFormat).map(tick => tick.label);
|
|
484
|
+
const widest = labels.reduce((widest_, label) => {
|
|
485
|
+
var _axis$labels9;
|
|
486
|
+
return Math.max(widest_, measureText(label, (_axis$labels9 = axis.labels) === null || _axis$labels9 === void 0 ? void 0 : _axis$labels9.style));
|
|
487
|
+
}, 0);
|
|
488
|
+
const title = (_axis$title = axis.title) !== null && _axis$title !== void 0 && _axis$title.text && (axis.title.rotation ?? -90) !== 0 ? textHeight(axis.title.style) + AXIS_TITLE_GAP : 0;
|
|
489
|
+
return Math.ceil(widest) + AXIS_GUTTER_PADDING + title;
|
|
490
|
+
};
|
|
491
|
+
const gutter = axisless || ((_yAxes$4 = yAxes[0]) === null || _yAxes$4 === void 0 ? void 0 : _yAxes$4.visible) === false ? 0 : measureGutter(32);
|
|
492
|
+
const legendAlign = ((_options$legend3 = options.legend) === null || _options$legend3 === void 0 ? void 0 : _options$legend3.align) ?? 'left';
|
|
493
|
+
const legendBesideLeft = legendMetrics.beside && legendAlign === 'left';
|
|
494
|
+
// Highcharts margins replace the computed edge distance rather than adding to it.
|
|
495
|
+
const left = ((_options$chart2 = options.chart) === null || _options$chart2 === void 0 ? void 0 : _options$chart2.marginLeft) ?? spaceLeft + gutter + (legendBesideLeft ? legendMetrics.width : 0);
|
|
496
|
+
const right = ((_options$chart3 = options.chart) === null || _options$chart3 === void 0 ? void 0 : _options$chart3.marginRight) ?? spaceRight + (legendMetrics.beside && !legendBesideLeft ? legendMetrics.width : 0);
|
|
497
|
+
const top = ((_options$chart4 = options.chart) === null || _options$chart4 === void 0 ? void 0 : _options$chart4.marginTop) ?? marginTop;
|
|
498
|
+
const bottom = ((_options$chart5 = options.chart) === null || _options$chart5 === void 0 ? void 0 : _options$chart5.marginBottom) ?? marginBottom;
|
|
499
|
+
const plot = {
|
|
500
|
+
x: left,
|
|
501
|
+
y: top,
|
|
502
|
+
width: Math.max(0, size.width - left - right),
|
|
503
|
+
height: Math.max(0, size.height - top - bottom)
|
|
504
|
+
};
|
|
505
|
+
if (plot.width <= 0 || plot.height <= 0) {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
const xHint = xAxisOptions.tickPixelInterval ? Math.max(2, Math.round(plot.width / xAxisOptions.tickPixelInterval)) : Math.max(2, Math.min(8, Math.round(plot.width / 90)));
|
|
509
|
+
const xScale = buildXScale(xAxisOptions, xKind, xDomain, categories, [plot.x, plot.x + plot.width], xHint);
|
|
510
|
+
const timePattern = xKind === 'time' && isContinuous(xScale) ? defaultTimeFormat(...xScale.domain()) : undefined;
|
|
511
|
+
const slots = computeSlots({
|
|
512
|
+
series: resolved.filter(({
|
|
513
|
+
renderer
|
|
514
|
+
}) => renderer.scales.x === 'band'),
|
|
515
|
+
scale: xScale,
|
|
516
|
+
plotWidth: plot.width,
|
|
517
|
+
stacks
|
|
518
|
+
});
|
|
519
|
+
const yAxisModels = yAxes.reduce((built, axis, index) => {
|
|
520
|
+
var _axis$categories4;
|
|
521
|
+
const hint = axis.tickAmount ?? 5;
|
|
522
|
+
const target = axis.linkedTo === undefined ? undefined : built[axis.linkedTo];
|
|
523
|
+
const range = (_axis$categories4 = axis.categories) !== null && _axis$categories4 !== void 0 && _axis$categories4.length ? [plot.y, plot.y + plot.height] : [plot.y + plot.height, plot.y];
|
|
524
|
+
const scale = buildYScale(axis, yDomains[index] ?? emptyExtent(), range, hint, target === null || target === void 0 ? void 0 : target.scale);
|
|
525
|
+
built.push({
|
|
526
|
+
id: `y${index}`,
|
|
527
|
+
options: axis,
|
|
528
|
+
scale,
|
|
529
|
+
ticks: axisTicks(axis, scale, hint, undefined, 'y', numberFormat)
|
|
530
|
+
});
|
|
531
|
+
return built;
|
|
532
|
+
}, []);
|
|
533
|
+
const colorScale = buildColorScale(options, acc.color, colors);
|
|
534
|
+
return {
|
|
535
|
+
numberFormat,
|
|
536
|
+
spacing,
|
|
537
|
+
plot,
|
|
538
|
+
axisless,
|
|
539
|
+
xAxis: {
|
|
540
|
+
id: 'x0',
|
|
541
|
+
options: xAxisOptions,
|
|
542
|
+
scale: xScale,
|
|
543
|
+
ticks: axisTicks(xAxisOptions, xScale, xHint, timePattern, 'x', numberFormat, [plot.x, plot.x + plot.width])
|
|
544
|
+
},
|
|
545
|
+
yAxes: yAxisModels,
|
|
546
|
+
titles: titles.entries,
|
|
547
|
+
colorScale,
|
|
548
|
+
series: all,
|
|
549
|
+
legend,
|
|
550
|
+
colors,
|
|
551
|
+
categories,
|
|
552
|
+
stacks,
|
|
553
|
+
slots
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
export { isBand, isContinuous };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Series palette, in the order series are drawn. These are the design system's
|
|
3
|
+
* service and brand colours rather than a set invented here, so a chart matches
|
|
4
|
+
* the product it sits in. The order alternates hue family — blue, orange, green,
|
|
5
|
+
* purple — so that neighbouring series stay apart from each other.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_COLORS: readonly string[];
|
|
8
|
+
export declare const DEFAULT_GRID_COLOR: string;
|
|
9
|
+
export declare const DEFAULT_AXIS_COLOR: string;
|
|
10
|
+
export declare const DEFAULT_TEXT_COLOR: string;
|
|
11
|
+
export declare const DEFAULT_MUTED_TEXT_COLOR: string;
|
|
12
|
+
export declare const DEFAULT_SURFACE_COLOR: string;
|
|
13
|
+
export declare const DEFAULT_TOOLTIP_SURFACE_COLOR: string;
|
|
14
|
+
export declare const DEFAULT_TOOLTIP_TEXT_COLOR: string;
|
|
15
|
+
export declare const colorAt: (colors: readonly string[], index: number) => string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Color } from '@pushwoosh/kit-constants';
|
|
2
|
+
/**
|
|
3
|
+
* Series palette, in the order series are drawn. These are the design system's
|
|
4
|
+
* service and brand colours rather than a set invented here, so a chart matches
|
|
5
|
+
* the product it sits in. The order alternates hue family — blue, orange, green,
|
|
6
|
+
* purple — so that neighbouring series stay apart from each other.
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_COLORS = [Color.PRIMARY, Color.WARNING, Color.SUCCESS, Color.EXCEPTIONAL, Color.BRIGHT, Color.DANGER, Color.BRAND_GREEN, Color.SOFT, Color.BRAND_BLUE, Color.BRAND_BLACK];
|
|
9
|
+
export const DEFAULT_GRID_COLOR = Color.DIVIDER;
|
|
10
|
+
export const DEFAULT_AXIS_COLOR = Color.FORM;
|
|
11
|
+
export const DEFAULT_TEXT_COLOR = Color.MAIN;
|
|
12
|
+
export const DEFAULT_MUTED_TEXT_COLOR = Color.LOCKED;
|
|
13
|
+
export const DEFAULT_SURFACE_COLOR = Color.CLEAR;
|
|
14
|
+
// Chart tooltips follow the product hint style: dark bubble, light text.
|
|
15
|
+
export const DEFAULT_TOOLTIP_SURFACE_COLOR = Color.BRAND_BLACK;
|
|
16
|
+
export const DEFAULT_TOOLTIP_TEXT_COLOR = Color.CLEAR;
|
|
17
|
+
export const colorAt = (colors, index) => colors[index % colors.length] ?? DEFAULT_COLORS[index % DEFAULT_COLORS.length];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type AxisOptions, type ChartOptions, type SeriesOptionsBase } from '../types/options';
|
|
2
|
+
export declare const toAxisArray: (axis: AxisOptions | readonly AxisOptions[] | undefined) => AxisOptions[];
|
|
3
|
+
export declare function resolveSeries(options: ChartOptions): SeriesOptionsBase[];
|
|
4
|
+
export declare function resolveAxes(options: ChartOptions): {
|
|
5
|
+
xAxes: AxisOptions[];
|
|
6
|
+
yAxes: AxisOptions[];
|
|
7
|
+
};
|
|
8
|
+
export declare const isSeriesVisible: (series: SeriesOptionsBase) => boolean;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
function merge(...layers) {
|
|
2
|
+
const result = {};
|
|
3
|
+
layers.forEach(layer => {
|
|
4
|
+
if (!layer) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
Object.entries(layer).forEach(([key, value]) => {
|
|
8
|
+
if (value === undefined) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const existing = result[key];
|
|
12
|
+
const bothPlain = isPlainObject(existing) && isPlainObject(value);
|
|
13
|
+
result[key] = bothPlain ? merge(existing, value) : value;
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
const isPlainObject = value => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
19
|
+
export const toAxisArray = axis => {
|
|
20
|
+
if (!axis) {
|
|
21
|
+
return [{}];
|
|
22
|
+
}
|
|
23
|
+
return Array.isArray(axis) ? [...axis] : [axis];
|
|
24
|
+
};
|
|
25
|
+
export function resolveSeries(options) {
|
|
26
|
+
var _options$chart;
|
|
27
|
+
const defaultType = ((_options$chart = options.chart) === null || _options$chart === void 0 ? void 0 : _options$chart.type) ?? 'line';
|
|
28
|
+
const plotOptions = options.plotOptions ?? {};
|
|
29
|
+
return (options.series ?? []).map(series => {
|
|
30
|
+
const type = series.type ?? defaultType;
|
|
31
|
+
return merge({
|
|
32
|
+
type
|
|
33
|
+
}, plotOptions.series, plotOptions[type], series, {
|
|
34
|
+
type
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function resolveAxes(options) {
|
|
39
|
+
return {
|
|
40
|
+
xAxes: toAxisArray(options.xAxis),
|
|
41
|
+
yAxes: toAxisArray(options.yAxis)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export const isSeriesVisible = series => series.visible !== false;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type AnySeriesRenderer } from '../types/renderer';
|
|
2
|
+
export declare function registerSeries(...renderers: AnySeriesRenderer[]): void;
|
|
3
|
+
export declare function clearSeriesRegistry(): void;
|
|
4
|
+
export declare function resolveRenderers(local?: readonly AnySeriesRenderer[]): ReadonlyMap<string, AnySeriesRenderer>;
|
|
5
|
+
export declare function getRenderer(renderers: ReadonlyMap<string, AnySeriesRenderer>, type: string): AnySeriesRenderer | undefined;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const globalRegistry = new Map();
|
|
2
|
+
const isDev = process.env.NODE_ENV !== 'production';
|
|
3
|
+
export function registerSeries(...renderers) {
|
|
4
|
+
renderers.forEach(renderer => {
|
|
5
|
+
const existing = globalRegistry.get(renderer.type);
|
|
6
|
+
if (existing === renderer) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (existing && isDev) {
|
|
10
|
+
throw new Error(`[kit-charts] Two different renderers registered for series type "${renderer.type}".`);
|
|
11
|
+
}
|
|
12
|
+
globalRegistry.set(renderer.type, renderer);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export function clearSeriesRegistry() {
|
|
16
|
+
globalRegistry.clear();
|
|
17
|
+
}
|
|
18
|
+
export function resolveRenderers(local) {
|
|
19
|
+
if (!(local !== null && local !== void 0 && local.length)) {
|
|
20
|
+
return globalRegistry;
|
|
21
|
+
}
|
|
22
|
+
const resolved = new Map(globalRegistry);
|
|
23
|
+
local.forEach(renderer => resolved.set(renderer.type, renderer));
|
|
24
|
+
return resolved;
|
|
25
|
+
}
|
|
26
|
+
export function getRenderer(renderers, type) {
|
|
27
|
+
const renderer = renderers.get(type);
|
|
28
|
+
if (!renderer && isDev) {
|
|
29
|
+
console.error(`[kit-charts] No renderer for series type "${type}". ` + `Import it and call registerSeries: ` + `import { ${type} } from '@pushwoosh/kit-charts/series/${type}'.`);
|
|
30
|
+
}
|
|
31
|
+
return renderer;
|
|
32
|
+
}
|