layerchart 2.0.0-next.62 → 2.0.0-next.63
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/dist/canvas.d.ts +4 -0
- package/dist/canvas.js +4 -0
- package/dist/components/Arc/Arc.shared.svelte.d.ts +2 -0
- package/dist/components/ArcLabel/ArcLabel.shared.svelte.d.ts +1 -0
- package/dist/components/Circle/Circle.shared.svelte.js +24 -5
- package/dist/components/Circle/Circle.svelte.test.js +70 -0
- package/dist/components/Dodge/Dodge.shared.svelte.d.ts +132 -0
- package/dist/components/Dodge/Dodge.shared.svelte.js +240 -0
- package/dist/components/Dodge/Dodge.svelte +88 -0
- package/dist/components/Dodge/Dodge.svelte.d.ts +27 -0
- package/dist/components/Dodge/Dodge.test.d.ts +1 -0
- package/dist/components/Dodge/Dodge.test.js +128 -0
- package/dist/components/Image/Image.html.svelte +0 -8
- package/dist/components/Image/Image.svg.svelte +1 -9
- package/dist/components/Pattern/Pattern.canvas.svelte +4 -1
- package/dist/components/Pattern/Pattern.shared.svelte.d.ts +31 -2
- package/dist/components/Pattern/Pattern.shared.svelte.js +20 -1
- package/dist/components/Pattern/Pattern.svg.svelte +17 -1
- package/dist/components/Raster/Raster.base.svelte +2 -8
- package/dist/components/Rect/Rect.canvas.svelte +2 -4
- package/dist/components/Rect/Rect.canvas.svelte.d.ts +1 -1
- package/dist/components/Rect/Rect.html.svelte +3 -9
- package/dist/components/Rect/Rect.html.svelte.d.ts +1 -1
- package/dist/components/Rect/Rect.shared.svelte.d.ts +5 -2
- package/dist/components/Rect/Rect.shared.svelte.js +26 -13
- package/dist/components/Rect/Rect.svelte.test.js +45 -0
- package/dist/components/Rect/Rect.svg.svelte +36 -21
- package/dist/components/Rect/Rect.svg.svelte.d.ts +1 -1
- package/dist/components/Spline/Spline.base.svelte +3 -2
- package/dist/components/Text/Text.canvas.svelte +9 -0
- package/dist/components/Text/Text.html.svelte +6 -0
- package/dist/components/Text/Text.shared.svelte.d.ts +25 -2
- package/dist/components/Text/Text.shared.svelte.js +36 -5
- package/dist/components/Text/Text.svelte.test.js +40 -0
- package/dist/components/Text/Text.svg.svelte +7 -1
- package/dist/components/Waffle/Waffle.shared.svelte.d.ts +182 -0
- package/dist/components/Waffle/Waffle.shared.svelte.js +300 -0
- package/dist/components/Waffle/Waffle.svelte +148 -0
- package/dist/components/Waffle/Waffle.svelte.d.ts +5 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +4 -0
- package/dist/html.d.ts +4 -0
- package/dist/html.js +4 -0
- package/dist/states/chart.svelte.js +8 -4
- package/dist/states/chart.svelte.test.js +53 -0
- package/dist/svg.d.ts +4 -0
- package/dist/svg.js +4 -0
- package/dist/utils/canvas.js +54 -13
- package/dist/utils/canvas.svelte.test.js +44 -0
- package/dist/utils/download.d.ts +5 -3
- package/dist/utils/download.js +36 -16
- package/dist/utils/stack.js +10 -2
- package/package.json +1 -1
package/dist/utils/canvas.js
CHANGED
|
@@ -13,6 +13,16 @@ function isTransparentFill(fill) {
|
|
|
13
13
|
// Match rgba(..., 0) - alpha channel is 0 (fully transparent)
|
|
14
14
|
return /rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*0\s*\)/.test(fill);
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Returns true if a style value cannot be assigned directly to a canvas
|
|
18
|
+
* context and must first be resolved through the hidden `<svg>` helper —
|
|
19
|
+
* specifically `var(...)` references and the `currentColor` keyword.
|
|
20
|
+
*/
|
|
21
|
+
function needsCSSResolution(value) {
|
|
22
|
+
if (typeof value !== 'string')
|
|
23
|
+
return false;
|
|
24
|
+
return value.includes('var(') || value.toLowerCase() === 'currentcolor';
|
|
25
|
+
}
|
|
16
26
|
const CANVAS_STYLES_ELEMENT_ID = '__layerchart_canvas_styles_id';
|
|
17
27
|
/**
|
|
18
28
|
* Parse an inline CSS style string into a StyleOptions object.
|
|
@@ -122,9 +132,8 @@ function render(ctx, render, styleOptions = {}, { applyText, } = {}) {
|
|
|
122
132
|
// TODO: Consider memoizing? How about reactiving to CSS variable changes (light/dark mode toggle)
|
|
123
133
|
let resolvedStyles;
|
|
124
134
|
if (typeof document === 'undefined' ||
|
|
125
|
-
(styleOptions.classes == null &&
|
|
126
|
-
|
|
127
|
-
// Skip resolving styles if running on server (no DOM), or no classes are provided and no styles are using CSS variables
|
|
135
|
+
(styleOptions.classes == null && !Object.values(mergedStyles).some(needsCSSResolution))) {
|
|
136
|
+
// Skip resolving styles if running on server (no DOM), or no classes are provided and no styles need CSS resolution (`var(...)` / `currentColor`)
|
|
128
137
|
resolvedStyles = mergedStyles;
|
|
129
138
|
// On server, provide sensible defaults for styles that would normally come from CSS
|
|
130
139
|
if (typeof document === 'undefined') {
|
|
@@ -136,12 +145,12 @@ function render(ctx, render, styleOptions = {}, { applyText, } = {}) {
|
|
|
136
145
|
else {
|
|
137
146
|
// Remove constant non-css variable properties (ex. `strokeWidth: 0.5`, `fill: #123456`) as not needed and improves memoization cache hit
|
|
138
147
|
const { constantStyles, variableStyles } = Object.entries(mergedStyles).reduce((acc, [key, value]) => {
|
|
139
|
-
if (
|
|
140
|
-
acc.constantStyles[key] = value;
|
|
141
|
-
}
|
|
142
|
-
else if (typeof value === 'string' && value.includes('var(')) {
|
|
148
|
+
if (needsCSSResolution(value)) {
|
|
143
149
|
acc.variableStyles[key] = value;
|
|
144
150
|
}
|
|
151
|
+
else if (typeof value === 'number' || typeof value === 'string') {
|
|
152
|
+
acc.constantStyles[key] = value;
|
|
153
|
+
}
|
|
145
154
|
return acc;
|
|
146
155
|
}, { constantStyles: {}, variableStyles: {} });
|
|
147
156
|
const computedStyles = getComputedStyles(ctx.canvas, {
|
|
@@ -200,7 +209,7 @@ function render(ctx, render, styleOptions = {}, { applyText, } = {}) {
|
|
|
200
209
|
styleOptions.styles?.fill instanceof CanvasGradient) ||
|
|
201
210
|
(typeof CanvasPattern !== 'undefined' &&
|
|
202
211
|
styleOptions.styles?.fill instanceof CanvasPattern) ||
|
|
203
|
-
!styleOptions.styles?.fill
|
|
212
|
+
!needsCSSResolution(styleOptions.styles?.fill))
|
|
204
213
|
? styleOptions.styles.fill
|
|
205
214
|
: resolvedStyles?.fill;
|
|
206
215
|
if (fill && !isTransparentFill(fill)) {
|
|
@@ -217,7 +226,7 @@ function render(ctx, render, styleOptions = {}, { applyText, } = {}) {
|
|
|
217
226
|
const stroke = styleOptions.styles?.stroke &&
|
|
218
227
|
((typeof CanvasGradient !== 'undefined' &&
|
|
219
228
|
styleOptions.styles?.stroke instanceof CanvasGradient) ||
|
|
220
|
-
!styleOptions.styles?.stroke
|
|
229
|
+
!needsCSSResolution(styleOptions.styles?.stroke))
|
|
221
230
|
? styleOptions.styles?.stroke
|
|
222
231
|
: resolvedStyles?.stroke;
|
|
223
232
|
if (stroke && !['none'].includes(stroke)) {
|
|
@@ -371,10 +380,18 @@ export function _createPattern(ctx, width, height, shapes, background) {
|
|
|
371
380
|
const patternCtx = patternCanvas.getContext('2d');
|
|
372
381
|
// Add pattern canvas to DOM to allow computed styles to be read (`getComputedStyles()`)
|
|
373
382
|
ctx.canvas.after(patternCanvas);
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
|
|
377
|
-
|
|
383
|
+
// Render the pattern at the device pixel ratio so the bitmap tile is
|
|
384
|
+
// sharp on high-DPI screens. Chrome samples patterns in the canvas's
|
|
385
|
+
// user (post-transform) coordinate space, so 1 source pixel = 1 user px
|
|
386
|
+
// by default — without DPR-scaling the bitmap, a 12 logical-px tile is
|
|
387
|
+
// sampled from 12 source pixels, leaving each device pixel of fill to
|
|
388
|
+
// be interpolated from a 1/dpr-th of a source pixel (blurry). Drawing
|
|
389
|
+
// at DPR resolution and scaling the pattern back down to user space at
|
|
390
|
+
// fill time keeps tiles sharp.
|
|
391
|
+
const dpr = (typeof window !== 'undefined' ? window.devicePixelRatio : 1) || 1;
|
|
392
|
+
patternCanvas.width = Math.max(1, Math.round(width * dpr));
|
|
393
|
+
patternCanvas.height = Math.max(1, Math.round(height * dpr));
|
|
394
|
+
patternCtx.scale(dpr, dpr);
|
|
378
395
|
if (background) {
|
|
379
396
|
patternCtx.fillStyle = background;
|
|
380
397
|
patternCtx.fillRect(0, 0, width, height);
|
|
@@ -389,9 +406,23 @@ export function _createPattern(ctx, width, height, shapes, background) {
|
|
|
389
406
|
styles: { stroke: shape.stroke, strokeWidth: shape.strokeWidth, opacity: shape.opacity },
|
|
390
407
|
});
|
|
391
408
|
}
|
|
409
|
+
else if (shape.type === 'rect') {
|
|
410
|
+
const rx = typeof shape.rx === 'string' ? toRectCornerPx(shape.rx, shape.width) : shape.rx;
|
|
411
|
+
const ry = typeof shape.ry === 'string' ? toRectCornerPx(shape.ry, shape.height) : (shape.ry ?? rx);
|
|
412
|
+
renderRect(patternCtx, { x: shape.x, y: shape.y, width: shape.width, height: shape.height, rx, ry }, { styles: { fill: shape.fill, opacity: shape.opacity } });
|
|
413
|
+
}
|
|
392
414
|
patternCtx.restore();
|
|
393
415
|
}
|
|
394
416
|
const pattern = ctx.createPattern(patternCanvas, 'repeat');
|
|
417
|
+
// Scale-only matrix; no translate so the pattern anchors to the path's
|
|
418
|
+
// local origin at fill time (matches SVG `patternUnits="userSpaceOnUse"`).
|
|
419
|
+
// Use the *actual* bitmap pixel dimensions for the scale so rounding
|
|
420
|
+
// `width * dpr` to an integer doesn't accumulate drift across tiles.
|
|
421
|
+
if (pattern) {
|
|
422
|
+
const sx = width / patternCanvas.width;
|
|
423
|
+
const sy = height / patternCanvas.height;
|
|
424
|
+
pattern.setTransform(new DOMMatrix([sx, 0, 0, sy, 0, 0]));
|
|
425
|
+
}
|
|
395
426
|
// Cleanup
|
|
396
427
|
ctx.canvas.parentElement?.removeChild(patternCanvas);
|
|
397
428
|
return pattern;
|
|
@@ -400,3 +431,13 @@ export function _createPattern(ctx, width, height, shapes, background) {
|
|
|
400
431
|
export const createPattern = memoize(_createPattern, {
|
|
401
432
|
cacheKey: (args) => JSON.stringify(args.slice(1)), // Ignore `ctx` argument
|
|
402
433
|
});
|
|
434
|
+
function toRectCornerPx(value, max) {
|
|
435
|
+
if (value.endsWith('%')) {
|
|
436
|
+
const pct = parseFloat(value);
|
|
437
|
+
if (!Number.isFinite(pct))
|
|
438
|
+
return 0;
|
|
439
|
+
return (max / 2) * (pct / 100);
|
|
440
|
+
}
|
|
441
|
+
const n = parseFloat(value);
|
|
442
|
+
return Number.isFinite(n) ? n : 0;
|
|
443
|
+
}
|
|
@@ -403,6 +403,33 @@ describe('renderPathData', () => {
|
|
|
403
403
|
expect(strokeSpy).toHaveBeenCalled();
|
|
404
404
|
expect(ctx.strokeStyle).toBe('#008000');
|
|
405
405
|
});
|
|
406
|
+
it('resolves currentColor stroke through the SVG helper', () => {
|
|
407
|
+
const parent = canvas.parentElement;
|
|
408
|
+
const previousColor = parent.style.color;
|
|
409
|
+
parent.style.color = 'rgb(255, 165, 0)';
|
|
410
|
+
renderPathData(ctx, 'M0,0 L100,0', {
|
|
411
|
+
styles: {
|
|
412
|
+
fill: 'none',
|
|
413
|
+
stroke: 'currentColor',
|
|
414
|
+
strokeOpacity: '1',
|
|
415
|
+
opacity: '1',
|
|
416
|
+
strokeWidth: '2',
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
// Canvas normalizes rgb(255, 165, 0) → '#ffa500'
|
|
420
|
+
expect(ctx.strokeStyle).toBe('#ffa500');
|
|
421
|
+
parent.style.color = previousColor;
|
|
422
|
+
});
|
|
423
|
+
it('resolves currentColor fill through the SVG helper', () => {
|
|
424
|
+
const parent = canvas.parentElement;
|
|
425
|
+
const previousColor = parent.style.color;
|
|
426
|
+
parent.style.color = 'rgb(128, 0, 128)';
|
|
427
|
+
renderPathData(ctx, 'M0,0 L100,0 L100,100 Z', {
|
|
428
|
+
styles: { fill: 'currentColor', fillOpacity: '1', opacity: '1', stroke: 'none' },
|
|
429
|
+
});
|
|
430
|
+
expect(ctx.fillStyle).toBe('#800080');
|
|
431
|
+
parent.style.color = previousColor;
|
|
432
|
+
});
|
|
406
433
|
});
|
|
407
434
|
// ---------------------------------------------------------------------------
|
|
408
435
|
// renderText
|
|
@@ -704,6 +731,23 @@ describe('_getComputedStyles', () => {
|
|
|
704
731
|
// 'red' resolves to 'rgb(255, 0, 0)' in the browser
|
|
705
732
|
expect(result.fill).toMatch(/rgb\(255,\s*0,\s*0\)/);
|
|
706
733
|
});
|
|
734
|
+
it('resolves currentColor for fill via inherited color', () => {
|
|
735
|
+
// Set color on the canvas's parent so the helper SVG (sibling of canvas) inherits it
|
|
736
|
+
const parent = canvas.parentElement;
|
|
737
|
+
const previousColor = parent.style.color;
|
|
738
|
+
parent.style.color = 'rgb(0, 128, 0)';
|
|
739
|
+
const result = _getComputedStyles(canvas, { styles: { fill: 'currentColor' } });
|
|
740
|
+
expect(result.fill).toMatch(/rgb\(0,\s*128,\s*0\)/);
|
|
741
|
+
parent.style.color = previousColor;
|
|
742
|
+
});
|
|
743
|
+
it('resolves currentColor for stroke via inherited color', () => {
|
|
744
|
+
const parent = canvas.parentElement;
|
|
745
|
+
const previousColor = parent.style.color;
|
|
746
|
+
parent.style.color = 'rgb(0, 0, 255)';
|
|
747
|
+
const result = _getComputedStyles(canvas, { styles: { stroke: 'currentColor' } });
|
|
748
|
+
expect(result.stroke).toMatch(/rgb\(0,\s*0,\s*255\)/);
|
|
749
|
+
parent.style.color = previousColor;
|
|
750
|
+
});
|
|
707
751
|
it('returns empty object when DOM throws (graceful error handling)', () => {
|
|
708
752
|
// Simulate error by breaking canvas.after
|
|
709
753
|
const originalAfter = canvas.after.bind(canvas);
|
package/dist/utils/download.d.ts
CHANGED
|
@@ -17,9 +17,11 @@ export type ChartImageOptions = {
|
|
|
17
17
|
*/
|
|
18
18
|
quality?: number;
|
|
19
19
|
/**
|
|
20
|
-
* Device pixel ratio to use when rasterising
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* Device pixel ratio to use when rasterising the image. Defaults to `1`
|
|
21
|
+
* so the output matches the chart's CSS dimensions (looks the same as
|
|
22
|
+
* what's on the page when viewed 1:1). Set to `window.devicePixelRatio`
|
|
23
|
+
* (or higher) to produce crisper images on retina displays at the cost
|
|
24
|
+
* of larger files.
|
|
23
25
|
*/
|
|
24
26
|
pixelRatio?: number;
|
|
25
27
|
};
|
package/dist/utils/download.js
CHANGED
|
@@ -27,6 +27,12 @@ const SVG_STYLE_PROPERTIES = [
|
|
|
27
27
|
'alignment-baseline',
|
|
28
28
|
'visibility',
|
|
29
29
|
'display',
|
|
30
|
+
// `<Text>` wraps each label in a nested `<svg class="lc-text-svg">` that
|
|
31
|
+
// relies on `overflow: visible` so labels can render outside the wrapper
|
|
32
|
+
// (axis ticks positioned at negative x). Without this inlined, the
|
|
33
|
+
// rasteriser falls back to the spec default `overflow: hidden` and clips
|
|
34
|
+
// the labels.
|
|
35
|
+
'overflow',
|
|
30
36
|
'paint-order',
|
|
31
37
|
'shape-rendering',
|
|
32
38
|
'text-rendering',
|
|
@@ -58,9 +64,16 @@ function inlineSvgStyles(svg) {
|
|
|
58
64
|
}
|
|
59
65
|
/**
|
|
60
66
|
* Draw an SVG element onto a canvas context at the given pixel dimensions.
|
|
67
|
+
* Sets `viewBox` (if not authored) so the SVG content scales to fill the
|
|
68
|
+
* destination size — without it, increasing the `width`/`height` attributes
|
|
69
|
+
* leaves content at its original pixel coordinates and the result lands in
|
|
70
|
+
* the top-left.
|
|
61
71
|
*/
|
|
62
|
-
function drawSvgToCanvas(svg, ctx, pixelWidth, pixelHeight) {
|
|
72
|
+
function drawSvgToCanvas(svg, ctx, pixelWidth, pixelHeight, cssWidth, cssHeight) {
|
|
63
73
|
const inlined = inlineSvgStyles(svg);
|
|
74
|
+
if (!inlined.getAttribute('viewBox')) {
|
|
75
|
+
inlined.setAttribute('viewBox', `0 0 ${cssWidth} ${cssHeight}`);
|
|
76
|
+
}
|
|
64
77
|
inlined.setAttribute('width', String(pixelWidth));
|
|
65
78
|
inlined.setAttribute('height', String(pixelHeight));
|
|
66
79
|
const svgStr = new XMLSerializer().serializeToString(inlined);
|
|
@@ -69,7 +82,7 @@ function drawSvgToCanvas(svg, ctx, pixelWidth, pixelHeight) {
|
|
|
69
82
|
return new Promise((resolve, reject) => {
|
|
70
83
|
const img = new Image();
|
|
71
84
|
img.onload = () => {
|
|
72
|
-
ctx.drawImage(img, 0, 0);
|
|
85
|
+
ctx.drawImage(img, 0, 0, pixelWidth, pixelHeight);
|
|
73
86
|
URL.revokeObjectURL(url);
|
|
74
87
|
resolve();
|
|
75
88
|
};
|
|
@@ -90,9 +103,22 @@ function drawSvgToCanvas(svg, ctx, pixelWidth, pixelHeight) {
|
|
|
90
103
|
*/
|
|
91
104
|
export async function getChartImageBlob(container, options = {}) {
|
|
92
105
|
const { background, format = 'png', quality = 0.92 } = options;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
106
|
+
// Default to 1 so PNGs match the chart's on-page size; pass
|
|
107
|
+
// `pixelRatio: window.devicePixelRatio` (or higher) for retina-sharp output.
|
|
108
|
+
const dpr = options.pixelRatio ?? 1;
|
|
109
|
+
// Find all SVG and Canvas layers within the container, sorted by z-index.
|
|
110
|
+
// The class-name selector implicitly excludes `.lc-hit-canvas`.
|
|
111
|
+
const layers = Array.from(container.querySelectorAll('.lc-layout-svg, .lc-layout-canvas')).sort((a, b) => {
|
|
112
|
+
const aZ = parseFloat(window.getComputedStyle(a).zIndex) || 0;
|
|
113
|
+
const bZ = parseFloat(window.getComputedStyle(b).zIndex) || 0;
|
|
114
|
+
return aZ - bZ;
|
|
115
|
+
});
|
|
116
|
+
// Size the output to the chart layers (all share the same bounds) rather
|
|
117
|
+
// than the wrapping container, so padding/margin doesn't leave blank
|
|
118
|
+
// space on the right/bottom of the image.
|
|
119
|
+
const layerRect = layers[0]?.getBoundingClientRect();
|
|
120
|
+
const cssWidth = layerRect?.width || container.clientWidth;
|
|
121
|
+
const cssHeight = layerRect?.height || container.clientHeight;
|
|
96
122
|
const pixelWidth = Math.round(cssWidth * dpr);
|
|
97
123
|
const pixelHeight = Math.round(cssHeight * dpr);
|
|
98
124
|
const offscreen = document.createElement('canvas');
|
|
@@ -105,21 +131,15 @@ export async function getChartImageBlob(container, options = {}) {
|
|
|
105
131
|
ctx.fillStyle = bg;
|
|
106
132
|
ctx.fillRect(0, 0, pixelWidth, pixelHeight);
|
|
107
133
|
}
|
|
108
|
-
// Find all SVG and Canvas layers within the container, sorted by z-index.
|
|
109
|
-
// `.lc-hit-canvas` is excluded via the class selector (it uses `.lc-layout-canvas`).
|
|
110
|
-
const layers = Array.from(container.querySelectorAll('.lc-layout-svg, .lc-layout-canvas')).sort((a, b) => {
|
|
111
|
-
const aZ = parseFloat(window.getComputedStyle(a).zIndex) || 0;
|
|
112
|
-
const bZ = parseFloat(window.getComputedStyle(b).zIndex) || 0;
|
|
113
|
-
return aZ - bZ;
|
|
114
|
-
});
|
|
115
134
|
for (const layer of layers) {
|
|
116
135
|
if (layer instanceof SVGElement) {
|
|
117
|
-
await drawSvgToCanvas(layer, ctx, pixelWidth, pixelHeight);
|
|
136
|
+
await drawSvgToCanvas(layer, ctx, pixelWidth, pixelHeight, cssWidth, cssHeight);
|
|
118
137
|
}
|
|
119
138
|
else if (layer instanceof HTMLCanvasElement) {
|
|
120
|
-
// Canvas
|
|
121
|
-
//
|
|
122
|
-
|
|
139
|
+
// Canvas bitmaps are sized to `cssSize × window.devicePixelRatio`
|
|
140
|
+
// (set by `scaleCanvas`). Map them to the requested output size so
|
|
141
|
+
// the result matches `pixelRatio`.
|
|
142
|
+
ctx.drawImage(layer, 0, 0, pixelWidth, pixelHeight);
|
|
123
143
|
}
|
|
124
144
|
}
|
|
125
145
|
return new Promise((resolve, reject) => {
|
package/dist/utils/stack.js
CHANGED
|
@@ -14,7 +14,11 @@ export function groupStackData(data, options) {
|
|
|
14
14
|
...new Set(groupData.map((d) => d[options.stackBy ?? ''])),
|
|
15
15
|
];
|
|
16
16
|
// @ts-expect-error
|
|
17
|
-
const stackData = stack()
|
|
17
|
+
const stackData = stack()
|
|
18
|
+
.keys(stackKeys)
|
|
19
|
+
.value((d, key) => d[key] ?? 0)
|
|
20
|
+
.order(options.order)
|
|
21
|
+
.offset(options.offset)(pivotData);
|
|
18
22
|
return stackData.flatMap((series) => {
|
|
19
23
|
return series.flatMap((s) => {
|
|
20
24
|
const keys = {
|
|
@@ -43,7 +47,11 @@ export function groupStackData(data, options) {
|
|
|
43
47
|
// @ts-expect-error
|
|
44
48
|
const stackKeys = [...new Set(data.map((d) => d[options.stackBy ?? '']))];
|
|
45
49
|
// @ts-expect-error
|
|
46
|
-
const stackData = stack()
|
|
50
|
+
const stackData = stack()
|
|
51
|
+
.keys(stackKeys)
|
|
52
|
+
.value((d, key) => d[key] ?? 0)
|
|
53
|
+
.order(options.order)
|
|
54
|
+
.offset(options.offset)(pivotData);
|
|
47
55
|
const result = stackData.flatMap((series) => {
|
|
48
56
|
return series.flatMap((s) => {
|
|
49
57
|
const keys = {
|
package/package.json
CHANGED