layerchart 2.0.0-next.61 → 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 +6 -2
- package/dist/canvas.js +6 -2
- package/dist/components/Arc/Arc.base.svelte +49 -11
- package/dist/components/Arc/Arc.shared.svelte.d.ts +2 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-full-circle--360-degree-range--1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-full-circle--360-degree-range--2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-innerRadius-of-0--pie-slice--1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-innerRadius-of-0--pie-slice--2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-negative-domain-values-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-negative-domain-values-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-partial-arc--e-g---180-degrees--1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-partial-arc--e-g---180-degrees--2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-at-max-domain-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-at-max-domain-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-below-domain-min-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-below-domain-min-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-exceeding-domain-max-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-exceeding-domain-max-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-of-0-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-of-0-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-enter-events-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-enter-events-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-move-events-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-move-events-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-touch-move-events-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-touch-move-events-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-custom-class-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-custom-class-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fill-color-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fill-color-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fillOpacity-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fillOpacity-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-offset-to-arc-position-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-offset-to-arc-position-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-opacity-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-opacity-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-stroke-color-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-stroke-color-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-strokeWidth-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-strokeWidth-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-zero-offset-by-default-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-zero-offset-by-default-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-hide-on-pointer-leave-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-hide-on-pointer-leave-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-show-on-pointer-enter-with-data-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-show-on-pointer-enter-with-data-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-handle-custom-start-angle-in-range-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-handle-custom-start-angle-in-range-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-have-stroke--none--by-default-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-have-stroke--none--by-default-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-an-arc-path-with-value-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-an-arc-path-with-value-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-when-track-prop-is-provided-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-when-track-prop-is-provided-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-custom-class-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-custom-class-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackCornerRadius-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackCornerRadius-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackEndAngle-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackEndAngle-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-and-trackOuterRadius-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-and-trackOuterRadius-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackOuterRadius-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackOuterRadius-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackPadAngle-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackPadAngle-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-and-trackEndAngle-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-and-trackEndAngle-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-both-startAngle-and-endAngle-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-both-startAngle-and-endAngle-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-cornerRadius-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-cornerRadius-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-and-range-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-and-range-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-range-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-range-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-endAngle-in-radians-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-endAngle-in-radians-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-and-outerRadius-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-and-outerRadius-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-outerRadius-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-outerRadius-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-padAngle-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-padAngle-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-startAngle-in-radians-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-startAngle-in-radians-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-Arc-element-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-Arc-element-2.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-track-1.png +0 -0
- package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-track-2.png +0 -0
- package/dist/components/ArcLabel/ArcLabel.shared.svelte.d.ts +1 -0
- package/dist/components/{ArcLabel.svelte.test.js → ArcLabel/ArcLabel.svelte.test.js} +3 -3
- package/dist/components/ArcLabel/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-renders-a-text-element-with-the-supplied-value-at-the-centroid-1.png +0 -0
- package/dist/components/ArcLabel/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-renders-a-text-element-with-the-supplied-value-at-the-centroid-2.png +0 -0
- package/dist/components/Blur/Blur.canvas.svelte +25 -0
- package/dist/components/Blur/Blur.canvas.svelte.d.ts +4 -0
- package/dist/components/Blur/Blur.html.svelte +11 -0
- package/dist/components/Blur/Blur.html.svelte.d.ts +4 -0
- package/dist/components/{Blur.svelte.d.ts → Blur/Blur.shared.svelte.d.ts} +3 -5
- package/dist/components/Blur/Blur.svelte +23 -0
- package/dist/components/Blur/Blur.svelte.d.ts +4 -0
- package/dist/components/Blur/Blur.svg.svelte +24 -0
- package/dist/components/Blur/Blur.svg.svelte.d.ts +4 -0
- package/dist/components/Chart/Chart.base.svelte +13 -7
- package/dist/components/Chart/ChartCore.svelte.test.d.ts +1 -0
- package/dist/components/{ChartCore.svelte.test.js → Chart/ChartCore.svelte.test.js} +1 -1
- 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/Link/Link.base.svelte +15 -9
- package/dist/components/Path/Path.canvas.svelte +5 -2
- package/dist/components/Path/Path.shared.svelte.d.ts +17 -4
- package/dist/components/Path/Path.shared.svelte.js +26 -8
- package/dist/components/Path/Path.svg.svelte +75 -60
- 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/RectClipPath/RectClipPath.base.svelte +25 -1
- package/dist/components/RectClipPath/RectClipPath.shared.svelte.d.ts +8 -0
- 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/Trail/Trail.base.svelte +10 -7
- 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/charts/__screenshots__/ArcChart.svelte.test.ts/ArcChart-uses-the-chart-value-accessor-for-explicit-per-series-tooltip-values-1.png +0 -0
- package/dist/components/charts/__screenshots__/ArcChart.svelte.test.ts/ArcChart-uses-the-chart-value-accessor-for-explicit-per-series-tooltip-values-2.png +0 -0
- package/dist/components/charts/__screenshots__/BarChart.svelte.test.ts/BarChart-legend-series-toggle-adjusts-group-scale-should-adjust-grouped-bar-widths-when-series-are-toggled-via-legend-1.png +0 -0
- package/dist/components/charts/__screenshots__/PieChart.svelte.test.ts/PieChart-uses-hovered-slice-identity-for-implicit-tooltip-series-1.png +0 -0
- package/dist/components/charts/__screenshots__/PieChart.svelte.test.ts/PieChart-uses-hovered-slice-identity-for-implicit-tooltip-series-2.png +0 -0
- package/dist/components/index.d.ts +6 -2
- package/dist/components/index.js +6 -2
- package/dist/html.d.ts +6 -2
- package/dist/html.js +6 -2
- package/dist/states/chart.svelte.d.ts +4 -2
- package/dist/states/chart.svelte.js +53 -22
- package/dist/states/chart.svelte.test.js +54 -1
- package/dist/states/series.svelte.js +9 -13
- package/dist/states/series.svelte.test.js +5 -1
- package/dist/svg.d.ts +6 -2
- package/dist/svg.js +6 -2
- 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/components/Blur.svelte +0 -49
- /package/dist/components/{ArcLabel.svelte.test.d.ts → ArcLabel/ArcLabel.svelte.test.d.ts} +0 -0
- /package/dist/components/{ChartCore.svelte.test.d.ts → Blur/Blur.shared.svelte.js} +0 -0
|
@@ -11,18 +11,36 @@ const defaultKey = (_, i) => i;
|
|
|
11
11
|
* either the geo projection or the chart's x/y/r scales.
|
|
12
12
|
*/
|
|
13
13
|
export function resolveCircle(d, props, chartCtx, geo) {
|
|
14
|
+
// When cx/cy/r are omitted, fall back to the chart's accessors
|
|
15
|
+
// (xGet/yGet/rGet) — same pattern as `Points`. Hardcoded defaults
|
|
16
|
+
// (0/0/1) only apply when neither prop nor chart-level config is set.
|
|
17
|
+
const cxDefault = typeof props.cx === 'number'
|
|
18
|
+
? props.cx
|
|
19
|
+
: props.cx == null && chartCtx.config.x != null
|
|
20
|
+
? Number(chartCtx.xGet(d)) || 0
|
|
21
|
+
: 0;
|
|
22
|
+
const cyDefault = typeof props.cy === 'number'
|
|
23
|
+
? props.cy
|
|
24
|
+
: props.cy == null && chartCtx.config.y != null
|
|
25
|
+
? Number(chartCtx.yGet(d)) || 0
|
|
26
|
+
: 0;
|
|
27
|
+
const rDefault = typeof props.r === 'number'
|
|
28
|
+
? props.r
|
|
29
|
+
: props.r == null && chartCtx.config.r != null
|
|
30
|
+
? Number(chartCtx.rGet(d)) || 1
|
|
31
|
+
: 1;
|
|
14
32
|
if (geo.projection) {
|
|
15
33
|
const [projX, projY] = resolveGeoDataPair(props.cx, props.cy, d, geo.projection);
|
|
16
34
|
return {
|
|
17
35
|
cx: projX,
|
|
18
36
|
cy: projY,
|
|
19
|
-
r: resolveDataProp(props.r, d, chartCtx.rScale,
|
|
37
|
+
r: resolveDataProp(props.r, d, chartCtx.rScale, rDefault),
|
|
20
38
|
};
|
|
21
39
|
}
|
|
22
40
|
return {
|
|
23
|
-
cx: resolveDataProp(props.cx, d, chartCtx.xScale,
|
|
24
|
-
cy: resolveDataProp(props.cy, d, chartCtx.yScale,
|
|
25
|
-
r: resolveDataProp(props.r, d, chartCtx.rScale,
|
|
41
|
+
cx: resolveDataProp(props.cx, d, chartCtx.xScale, cxDefault),
|
|
42
|
+
cy: resolveDataProp(props.cy, d, chartCtx.yScale, cyDefault),
|
|
43
|
+
r: resolveDataProp(props.r, d, chartCtx.rScale, rDefault),
|
|
26
44
|
};
|
|
27
45
|
}
|
|
28
46
|
/**
|
|
@@ -46,7 +64,8 @@ export class CircleState {
|
|
|
46
64
|
// Reactive derivations
|
|
47
65
|
dashArrayResolved = $derived(parseDashArray(this.#getProps().dashArray));
|
|
48
66
|
dashArrayAttr = $derived(this.dashArrayResolved ? this.dashArrayResolved.join(' ') : undefined);
|
|
49
|
-
dataMode = $derived(
|
|
67
|
+
dataMode = $derived(this.#getProps().data != null ||
|
|
68
|
+
hasAnyDataProp(this.#getProps().cx, this.#getProps().cy, this.#getProps().r));
|
|
50
69
|
#resolvedData = $derived(this.dataMode ? (this.#getProps().data ?? chartDataArray(this.chartCtx.data)) : []);
|
|
51
70
|
// Per-key motion tracking (only created when motion is configured)
|
|
52
71
|
#dataMotionMap = null;
|
|
@@ -137,5 +137,75 @@ describe('Circle', () => {
|
|
|
137
137
|
const circles = page.getByTestId(componentTestId).elements();
|
|
138
138
|
await expect.poll(() => circles.length).toBe(1);
|
|
139
139
|
});
|
|
140
|
+
it('should enter data mode when only `data` prop is set, using chart accessors', async () => {
|
|
141
|
+
render(TestHarness, {
|
|
142
|
+
component: Circle,
|
|
143
|
+
chartProps: {
|
|
144
|
+
data,
|
|
145
|
+
x: 'date',
|
|
146
|
+
y: 'value',
|
|
147
|
+
yDomain: [0, 100],
|
|
148
|
+
r: 'value',
|
|
149
|
+
rRange: [2, 10],
|
|
150
|
+
},
|
|
151
|
+
componentProps: {
|
|
152
|
+
data,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
const circles = page.getByTestId(componentTestId).elements();
|
|
156
|
+
await expect.poll(() => circles.length).toBe(3);
|
|
157
|
+
const radii = circles.map((c) => Number(c.getAttribute('r')));
|
|
158
|
+
for (const r of radii) {
|
|
159
|
+
expect(r).toBeGreaterThanOrEqual(2);
|
|
160
|
+
expect(r).toBeLessThanOrEqual(10);
|
|
161
|
+
}
|
|
162
|
+
expect(radii[2]).toBeGreaterThan(radii[0]);
|
|
163
|
+
});
|
|
164
|
+
it('should mix explicit and chart-inherited position props', async () => {
|
|
165
|
+
render(TestHarness, {
|
|
166
|
+
component: Circle,
|
|
167
|
+
chartProps: {
|
|
168
|
+
data,
|
|
169
|
+
x: 'date',
|
|
170
|
+
y: 'value',
|
|
171
|
+
yDomain: [0, 100],
|
|
172
|
+
},
|
|
173
|
+
componentProps: {
|
|
174
|
+
data,
|
|
175
|
+
cx: 'date',
|
|
176
|
+
r: 4,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
const circles = page.getByTestId(componentTestId).elements();
|
|
180
|
+
await expect.poll(() => circles.length).toBe(3);
|
|
181
|
+
const cys = circles.map((c) => Number(c.getAttribute('cy')));
|
|
182
|
+
expect(cys[0]).toBeGreaterThan(cys[1]);
|
|
183
|
+
expect(cys[1]).toBeGreaterThan(cys[2]);
|
|
184
|
+
for (const c of circles) {
|
|
185
|
+
expect(c.getAttribute('r')).toBe('4');
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
it('explicit r should win over chart rGet', async () => {
|
|
189
|
+
render(TestHarness, {
|
|
190
|
+
component: Circle,
|
|
191
|
+
chartProps: {
|
|
192
|
+
data,
|
|
193
|
+
x: 'date',
|
|
194
|
+
y: 'value',
|
|
195
|
+
yDomain: [0, 100],
|
|
196
|
+
r: 'value',
|
|
197
|
+
rRange: [2, 10],
|
|
198
|
+
},
|
|
199
|
+
componentProps: {
|
|
200
|
+
data,
|
|
201
|
+
r: 7,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
const circles = page.getByTestId(componentTestId).elements();
|
|
205
|
+
await expect.poll(() => circles.length).toBe(3);
|
|
206
|
+
for (const c of circles) {
|
|
207
|
+
expect(c.getAttribute('r')).toBe('7');
|
|
208
|
+
}
|
|
209
|
+
});
|
|
140
210
|
});
|
|
141
211
|
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type DodgeAnchor = 'top' | 'middle' | 'bottom' | 'left' | 'right';
|
|
3
|
+
export type DodgeItem<T> = {
|
|
4
|
+
data: T;
|
|
5
|
+
/** Pixel position along the x-axis. */
|
|
6
|
+
x: number;
|
|
7
|
+
/** Pixel position along the y-axis. */
|
|
8
|
+
y: number;
|
|
9
|
+
/** Resolved circular radius (circular mode) or anchor-axis half-extent (rectangular mode). */
|
|
10
|
+
r: number;
|
|
11
|
+
/** Resolved x-axis half-extent. Equal to `r` in circular mode. */
|
|
12
|
+
rx: number;
|
|
13
|
+
/** Resolved y-axis half-extent. Equal to `r` in circular mode. */
|
|
14
|
+
ry: number;
|
|
15
|
+
/** Original index of the datum in the input `data` array. */
|
|
16
|
+
index: number;
|
|
17
|
+
};
|
|
18
|
+
export type DodgePropsWithoutHTML<T = any> = {
|
|
19
|
+
/** Data to dodge. Falls back to chart context data when omitted. */
|
|
20
|
+
data?: T[];
|
|
21
|
+
/**
|
|
22
|
+
* Axis to dodge along (the axis whose value is computed; the other axis is the anchor).
|
|
23
|
+
* @default 'y'
|
|
24
|
+
*/
|
|
25
|
+
axis?: 'x' | 'y';
|
|
26
|
+
/**
|
|
27
|
+
* Anchor edge along the dodge axis.
|
|
28
|
+
* - `axis='y'`: `'top'` (stack down), `'middle'` (stack from center), `'bottom'` (stack up). Default `'bottom'`.
|
|
29
|
+
* - `axis='x'`: `'left'` (stack right), `'middle'`, `'right'`. Default `'left'`.
|
|
30
|
+
*/
|
|
31
|
+
anchor?: DodgeAnchor;
|
|
32
|
+
/**
|
|
33
|
+
* Minimum padding between items in pixels.
|
|
34
|
+
* @default 1
|
|
35
|
+
*/
|
|
36
|
+
padding?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Circular collision radius (or accessor). Used unless both `rx` and `ry`
|
|
39
|
+
* are provided.
|
|
40
|
+
*
|
|
41
|
+
* Resolution priority:
|
|
42
|
+
* 1. This prop, if set.
|
|
43
|
+
* 2. The chart's `r` accessor (via `rScale`/`rRange`), if configured.
|
|
44
|
+
* 3. Default of `5`.
|
|
45
|
+
*/
|
|
46
|
+
r?: number | ((d: T) => number);
|
|
47
|
+
/**
|
|
48
|
+
* X-axis half-extent (or accessor). When set together with `ry`, switches
|
|
49
|
+
* to axis-aligned **rectangular** packing instead of circular collision.
|
|
50
|
+
*
|
|
51
|
+
* For `axis='y'` (vertical dodge), `rx` controls the horizontal collision
|
|
52
|
+
* extent (typically the per-item value, e.g. half the label width). For
|
|
53
|
+
* `axis='x'` (horizontal dodge), `rx` becomes the dodge-axis (column)
|
|
54
|
+
* half-extent and is typically a constant.
|
|
55
|
+
*/
|
|
56
|
+
rx?: number | ((d: T) => number);
|
|
57
|
+
/**
|
|
58
|
+
* Y-axis half-extent (or accessor). When set together with `rx`, switches
|
|
59
|
+
* to axis-aligned **rectangular** packing instead of circular collision.
|
|
60
|
+
*
|
|
61
|
+
* For `axis='y'` (vertical dodge), `ry` becomes the dodge-axis (row)
|
|
62
|
+
* half-extent and is typically a constant. For `axis='x'`, `ry` controls
|
|
63
|
+
* the vertical collision extent.
|
|
64
|
+
*/
|
|
65
|
+
ry?: number | ((d: T) => number);
|
|
66
|
+
/**
|
|
67
|
+
* Override the anchor-axis pixel accessor.
|
|
68
|
+
* For `axis='y'`, this is x; for `axis='x'`, this is y.
|
|
69
|
+
* Defaults to the chart context's `xGet`/`yGet` (which applies the chart's
|
|
70
|
+
* scale to the chart's `x`/`y` accessor).
|
|
71
|
+
*/
|
|
72
|
+
position?: (d: T) => number;
|
|
73
|
+
/**
|
|
74
|
+
* Pixel coordinate (along the dodge axis) of the line items grow away from:
|
|
75
|
+
* the centerline for `anchor='middle'`, the edge for the others.
|
|
76
|
+
*
|
|
77
|
+
* Default depends on `axis` + `anchor`:
|
|
78
|
+
* - `axis='y'`: `0` (top), `ctx.height / 2` (middle), `ctx.height` (bottom)
|
|
79
|
+
* - `axis='x'`: `0` (left), `ctx.width / 2` (middle), `ctx.width` (right)
|
|
80
|
+
*
|
|
81
|
+
* Pass a custom value to dodge within a sub-region — e.g. a band scale's
|
|
82
|
+
* `bandLeft + bandwidth/2` for per-band beeswarms, or a horizontal
|
|
83
|
+
* baseline pixel for split top/bottom timeline labels. Output positions
|
|
84
|
+
* are in chart coordinates (no snippet translation needed).
|
|
85
|
+
*/
|
|
86
|
+
baseline?: number;
|
|
87
|
+
/** Snippet receives computed positions in original data order. */
|
|
88
|
+
children?: Snippet<[{
|
|
89
|
+
items: DodgeItem<T>[];
|
|
90
|
+
}]>;
|
|
91
|
+
};
|
|
92
|
+
export type DodgeProps<T = any> = DodgePropsWithoutHTML<T>;
|
|
93
|
+
type DodgeInput<T> = {
|
|
94
|
+
/** Anchor-axis pixel position (always — the algorithm packs the other axis). */
|
|
95
|
+
x: number;
|
|
96
|
+
/** X-axis half-extent. */
|
|
97
|
+
rx: number;
|
|
98
|
+
/** Y-axis half-extent. */
|
|
99
|
+
ry: number;
|
|
100
|
+
data: T;
|
|
101
|
+
index: number;
|
|
102
|
+
};
|
|
103
|
+
type DodgeOpts = {
|
|
104
|
+
axis: 'x' | 'y';
|
|
105
|
+
anchor: DodgeAnchor;
|
|
106
|
+
padding: number;
|
|
107
|
+
/** Pixel coordinate (along the dodge axis) of the line items grow away from. */
|
|
108
|
+
baseline: number;
|
|
109
|
+
/**
|
|
110
|
+
* When `true`, switch from circular to axis-aligned rectangular packing.
|
|
111
|
+
* Inputs' `rx` / `ry` are then treated as independent half-extents per axis
|
|
112
|
+
* (the dodge-axis half-extent should be uniform for sensible row alignment).
|
|
113
|
+
*/
|
|
114
|
+
rectangular?: boolean;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Pack items along one axis so they don't overlap, given their positions on
|
|
118
|
+
* the other axis. Modeled after Observable Plot's `dodge` transform — uses an
|
|
119
|
+
* interval tree to find candidate positions in `O(log n + k)` per item.
|
|
120
|
+
*
|
|
121
|
+
* `input.x` is always the anchor-axis position regardless of `axis`. The
|
|
122
|
+
* algorithm packs along the dodge axis and the wrapper swaps `x`/`y` in the
|
|
123
|
+
* result for `axis='x'`.
|
|
124
|
+
*
|
|
125
|
+
* Set `opts.rectangular` to true to switch from circular to axis-aligned
|
|
126
|
+
* rectangular packing — useful for text labels where the bounding box is much
|
|
127
|
+
* wider than tall (so a circular `r` would produce excessive vertical gaps).
|
|
128
|
+
*
|
|
129
|
+
* @see https://observablehq.com/plot/transforms/dodge
|
|
130
|
+
*/
|
|
131
|
+
export declare function dodge<T>(input: DodgeInput<T>[], opts: DodgeOpts): DodgeItem<T>[];
|
|
132
|
+
export {};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
function createIntervalTree() {
|
|
2
|
+
let root = null;
|
|
3
|
+
function insertInto(node, interval) {
|
|
4
|
+
if (!node) {
|
|
5
|
+
return { interval, maxHi: interval[1], left: null, right: null };
|
|
6
|
+
}
|
|
7
|
+
if (interval[0] < node.interval[0]) {
|
|
8
|
+
node.left = insertInto(node.left, interval);
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
node.right = insertInto(node.right, interval);
|
|
12
|
+
}
|
|
13
|
+
if (interval[1] > node.maxHi)
|
|
14
|
+
node.maxHi = interval[1];
|
|
15
|
+
return node;
|
|
16
|
+
}
|
|
17
|
+
function queryNode(node, lo, hi, visit) {
|
|
18
|
+
if (!node || node.maxHi < lo)
|
|
19
|
+
return;
|
|
20
|
+
queryNode(node.left, lo, hi, visit);
|
|
21
|
+
const it = node.interval;
|
|
22
|
+
if (it[0] <= hi && it[1] >= lo)
|
|
23
|
+
visit(it);
|
|
24
|
+
// Once `it[0] > hi` the right subtree (all `lo >= it[0]`) cannot overlap.
|
|
25
|
+
if (it[0] <= hi)
|
|
26
|
+
queryNode(node.right, lo, hi, visit);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
insert(interval) {
|
|
30
|
+
root = insertInto(root, interval);
|
|
31
|
+
},
|
|
32
|
+
queryInterval(lo, hi, visit) {
|
|
33
|
+
queryNode(root, lo, hi, visit);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Direction multiplier for an anchor along its dodge axis.
|
|
39
|
+
*
|
|
40
|
+
* - `'top'` / `'left'`: `+1` — items grow at increasing chart coord
|
|
41
|
+
* - `'bottom'` / `'right'`: `-1` — items grow at decreasing chart coord
|
|
42
|
+
* - `'middle'`: `0` — items spread symmetrically (algorithm
|
|
43
|
+
* treats this specially, not multiplied)
|
|
44
|
+
*
|
|
45
|
+
* Combined with `baseline` (the anchor's pixel coordinate), the algorithm
|
|
46
|
+
* maps a packed local position `p` to chart coords via `baseline + dir * p`
|
|
47
|
+
* (or `baseline ± p` for middle).
|
|
48
|
+
*/
|
|
49
|
+
function anchorDirection(axis, anchor) {
|
|
50
|
+
if (anchor === 'middle')
|
|
51
|
+
return 0;
|
|
52
|
+
if (axis === 'y')
|
|
53
|
+
return anchor === 'top' ? 1 : -1; // bottom
|
|
54
|
+
return anchor === 'right' ? -1 : 1; // left
|
|
55
|
+
}
|
|
56
|
+
function compareSymmetric(a, b) {
|
|
57
|
+
return Math.abs(a) - Math.abs(b);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Pack items along one axis so they don't overlap, given their positions on
|
|
61
|
+
* the other axis. Modeled after Observable Plot's `dodge` transform — uses an
|
|
62
|
+
* interval tree to find candidate positions in `O(log n + k)` per item.
|
|
63
|
+
*
|
|
64
|
+
* `input.x` is always the anchor-axis position regardless of `axis`. The
|
|
65
|
+
* algorithm packs along the dodge axis and the wrapper swaps `x`/`y` in the
|
|
66
|
+
* result for `axis='x'`.
|
|
67
|
+
*
|
|
68
|
+
* Set `opts.rectangular` to true to switch from circular to axis-aligned
|
|
69
|
+
* rectangular packing — useful for text labels where the bounding box is much
|
|
70
|
+
* wider than tall (so a circular `r` would produce excessive vertical gaps).
|
|
71
|
+
*
|
|
72
|
+
* @see https://observablehq.com/plot/transforms/dodge
|
|
73
|
+
*/
|
|
74
|
+
export function dodge(input, opts) {
|
|
75
|
+
if (opts.rectangular) {
|
|
76
|
+
return dodgeRows(input, opts);
|
|
77
|
+
}
|
|
78
|
+
return dodgeCircular(input, opts);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Map per-item normalized dodge-axis positions (`packed`) back to chart space
|
|
82
|
+
* and package as `DodgeItem`s. Handles the axis swap for `axis='x'` (where
|
|
83
|
+
* `input.x` is the chart-y anchor).
|
|
84
|
+
*/
|
|
85
|
+
function buildResult(input, packed, axis, dir, baseline) {
|
|
86
|
+
const factor = dir === 0 ? 1 : dir;
|
|
87
|
+
const result = new Array(input.length);
|
|
88
|
+
for (let i = 0; i < input.length; i++) {
|
|
89
|
+
const item = input[i];
|
|
90
|
+
const dodgePos = packed[i] * factor + baseline;
|
|
91
|
+
// `r` carries the anchor-axis half-extent for back-compat with circular
|
|
92
|
+
// callers (`<Circle r={r}>`). In rectangular mode that's `rx` for axis='y'
|
|
93
|
+
// and `ry` for axis='x' — i.e. whichever axis the layout collides on.
|
|
94
|
+
const rAnchor = axis === 'y' ? item.rx : item.ry;
|
|
95
|
+
result[i] =
|
|
96
|
+
axis === 'y'
|
|
97
|
+
? {
|
|
98
|
+
data: item.data,
|
|
99
|
+
x: item.x,
|
|
100
|
+
y: dodgePos,
|
|
101
|
+
r: rAnchor,
|
|
102
|
+
rx: item.rx,
|
|
103
|
+
ry: item.ry,
|
|
104
|
+
index: item.index,
|
|
105
|
+
}
|
|
106
|
+
: {
|
|
107
|
+
data: item.data,
|
|
108
|
+
x: dodgePos,
|
|
109
|
+
y: item.x,
|
|
110
|
+
r: rAnchor,
|
|
111
|
+
rx: item.rx,
|
|
112
|
+
ry: item.ry,
|
|
113
|
+
index: item.index,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
function dodgeCircular(input, opts) {
|
|
119
|
+
const { axis, anchor, padding, baseline } = opts;
|
|
120
|
+
const dir = anchorDirection(axis, anchor);
|
|
121
|
+
const isMiddle = dir === 0;
|
|
122
|
+
// `intervals[0..k]` is a flat array of [lo0, hi0, lo1, hi1, ...] forbidden
|
|
123
|
+
// zones along the dodge axis for the current item. Slot 0/1 is reserved
|
|
124
|
+
// for the natural anchor zone ([0, 0], a no-op zone keeping y=0 in the
|
|
125
|
+
// candidate set). Tangent positions from each colliding placed item add
|
|
126
|
+
// two more candidate y values each. `candidates` is a parallel buffer for
|
|
127
|
+
// the sortable copy — pre-allocated once to avoid per-iteration allocation.
|
|
128
|
+
const cap = 2 * input.length + 2;
|
|
129
|
+
const intervals = new Float64Array(cap);
|
|
130
|
+
const candidates = new Float64Array(cap);
|
|
131
|
+
const packed = new Float64Array(input.length);
|
|
132
|
+
const tree = createIntervalTree();
|
|
133
|
+
for (let i = 0; i < input.length; i++) {
|
|
134
|
+
const item = input[i];
|
|
135
|
+
// Circular mode: `rx === ry` (Dodge.svelte ensures this), so either one
|
|
136
|
+
// is the circular radius. Using `rx` keeps the field reference uniform.
|
|
137
|
+
const ri = item.rx;
|
|
138
|
+
// y0 shifts the natural anchor by ri+padding so the item sits flush
|
|
139
|
+
// against the baseline. middle anchor (dir=0) needs no shift.
|
|
140
|
+
const y0 = isMiddle ? 0 : ri + padding;
|
|
141
|
+
const l = item.x - ri;
|
|
142
|
+
const h = item.x + ri;
|
|
143
|
+
let k = 2;
|
|
144
|
+
tree.queryInterval(l - padding, h + padding, (interval) => {
|
|
145
|
+
const j = interval[2];
|
|
146
|
+
const yj = packed[j] - y0;
|
|
147
|
+
const dx = item.x - input[j].x;
|
|
148
|
+
const dr = ri + input[j].rx + padding;
|
|
149
|
+
const sq = dr * dr - dx * dx;
|
|
150
|
+
if (sq >= 0) {
|
|
151
|
+
const dy = Math.sqrt(sq);
|
|
152
|
+
intervals[k++] = yj - dy;
|
|
153
|
+
intervals[k++] = yj + dy;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
// Sort the candidate y-values in-place into our reusable buffer. Native
|
|
157
|
+
// typed-array sort is materially faster than Array.sort with a JS
|
|
158
|
+
// comparator. For non-middle anchors we want ascending order (default);
|
|
159
|
+
// for middle we want symmetric (closest to 0 first), which still goes
|
|
160
|
+
// through the typed-array sort and avoids the prior allocation.
|
|
161
|
+
const view = candidates.subarray(0, k);
|
|
162
|
+
view.set(intervals.subarray(0, k));
|
|
163
|
+
if (isMiddle) {
|
|
164
|
+
view.sort(compareSymmetric);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
view.sort();
|
|
168
|
+
}
|
|
169
|
+
let chosen = y0; // fallback: natural anchor when nothing fits
|
|
170
|
+
outer: for (let c = 0; c < k; c++) {
|
|
171
|
+
const y = view[c];
|
|
172
|
+
// For non-middle anchors, items grow only in the +y direction relative
|
|
173
|
+
// to y0 — negative candidates are below the baseline and inadmissible.
|
|
174
|
+
if (!isMiddle && y < 0)
|
|
175
|
+
continue;
|
|
176
|
+
for (let j = 0; j < k; j += 2) {
|
|
177
|
+
if (intervals[j] + 1e-6 < y && y < intervals[j + 1] - 1e-6) {
|
|
178
|
+
continue outer;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
chosen = y + y0;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
packed[i] = chosen;
|
|
185
|
+
tree.insert([l, h, i]);
|
|
186
|
+
}
|
|
187
|
+
return buildResult(input, packed, axis, dir, baseline);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Axis-aligned rectangular packing — same interval-tree query as circular
|
|
191
|
+
* dodge, but we only care which rows/columns are already occupied in the
|
|
192
|
+
* overlap range. Items snap to the lowest free row at fixed increments along
|
|
193
|
+
* the dodge axis.
|
|
194
|
+
*
|
|
195
|
+
* Half-extent semantics:
|
|
196
|
+
* - For `axis='y'`: `rx` is the anchor-axis (horizontal) collision extent;
|
|
197
|
+
* `ry` is the dodge-axis (row) half-extent. Row spacing = `2 * ry`.
|
|
198
|
+
* - For `axis='x'`: `ry` is the anchor-axis (vertical) collision extent;
|
|
199
|
+
* `rx` is the dodge-axis (column) half-extent. Column spacing = `2 * rx`.
|
|
200
|
+
*
|
|
201
|
+
* The anchor-axis half-extent typically varies per item (e.g. half a label's
|
|
202
|
+
* width); the dodge-axis half-extent should be uniform for sensible
|
|
203
|
+
* row alignment.
|
|
204
|
+
*/
|
|
205
|
+
function dodgeRows(input, opts) {
|
|
206
|
+
const { axis, anchor, padding, baseline } = opts;
|
|
207
|
+
const dir = anchorDirection(axis, anchor);
|
|
208
|
+
const isVertical = axis === 'y';
|
|
209
|
+
const rows = new Int32Array(input.length);
|
|
210
|
+
const packed = new Float64Array(input.length);
|
|
211
|
+
const tree = createIntervalTree();
|
|
212
|
+
for (let i = 0; i < input.length; i++) {
|
|
213
|
+
const item = input[i];
|
|
214
|
+
const rAnchor = isVertical ? item.rx : item.ry;
|
|
215
|
+
const rDodge = isVertical ? item.ry : item.rx;
|
|
216
|
+
const l = item.x - rAnchor;
|
|
217
|
+
const h = item.x + rAnchor;
|
|
218
|
+
const used = new Set();
|
|
219
|
+
tree.queryInterval(l - padding, h + padding, (interval) => {
|
|
220
|
+
used.add(rows[interval[2]]);
|
|
221
|
+
});
|
|
222
|
+
let row = 0;
|
|
223
|
+
while (used.has(row))
|
|
224
|
+
row++;
|
|
225
|
+
rows[i] = row;
|
|
226
|
+
// Row spacing is `2 * rDodge`; centers fall at half-row offsets.
|
|
227
|
+
if (dir === 0) {
|
|
228
|
+
// middle: alternate above/below (even rows above, odd rows below)
|
|
229
|
+
const sign = row % 2 === 0 ? -1 : 1;
|
|
230
|
+
const step = Math.floor(row / 2) + (row === 0 ? 0 : 1);
|
|
231
|
+
packed[i] = sign * step * 2 * rDodge;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
// start/end: stack outward from the anchor edge
|
|
235
|
+
packed[i] = row * 2 * rDodge + rDodge;
|
|
236
|
+
}
|
|
237
|
+
tree.insert([l, h, i]);
|
|
238
|
+
}
|
|
239
|
+
return buildResult(input, packed, axis, dir, baseline);
|
|
240
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type {
|
|
3
|
+
DodgeAnchor,
|
|
4
|
+
DodgeItem,
|
|
5
|
+
DodgeProps,
|
|
6
|
+
DodgePropsWithoutHTML,
|
|
7
|
+
} from './Dodge.shared.svelte.js';
|
|
8
|
+
export { dodge } from './Dodge.shared.svelte.js';
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<script lang="ts" generics="T = any">
|
|
12
|
+
import { getChartContext } from '../../contexts/chart.js';
|
|
13
|
+
import { dodge, type DodgeProps } from './Dodge.shared.svelte.js';
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
data: dataProp,
|
|
17
|
+
axis = 'y',
|
|
18
|
+
anchor,
|
|
19
|
+
padding = 1,
|
|
20
|
+
r,
|
|
21
|
+
rx,
|
|
22
|
+
ry,
|
|
23
|
+
position,
|
|
24
|
+
baseline: baselineProp,
|
|
25
|
+
children,
|
|
26
|
+
}: DodgeProps<T> = $props();
|
|
27
|
+
|
|
28
|
+
const ctx = getChartContext<T>();
|
|
29
|
+
|
|
30
|
+
ctx.registerComponent({ name: 'Dodge', kind: 'composite-mark' });
|
|
31
|
+
|
|
32
|
+
const resolvedAnchor = $derived(anchor ?? (axis === 'y' ? 'bottom' : 'left'));
|
|
33
|
+
|
|
34
|
+
const data = $derived((dataProp ?? (ctx.data as T[] | undefined) ?? []) as T[]);
|
|
35
|
+
|
|
36
|
+
const positionFn = $derived(
|
|
37
|
+
position ?? ((axis === 'y' ? ctx.xGet : ctx.yGet) as (d: T) => number)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Rectangular mode is opt-in by providing both `rx` and `ry`.
|
|
41
|
+
const rectangular = $derived(rx != null && ry != null);
|
|
42
|
+
|
|
43
|
+
// Resolve `r` (circular fallback) — also serves as the default per-axis
|
|
44
|
+
// half-extent in circular mode (rx === ry === r).
|
|
45
|
+
const rFn = $derived.by(() => {
|
|
46
|
+
if (typeof r === 'function') return r as (d: T) => number;
|
|
47
|
+
if (r != null) return () => r as number;
|
|
48
|
+
if (ctx.config.r) return (d: T) => Number(ctx.rGet(d)) || 0;
|
|
49
|
+
return () => 5;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
function asFn(v: number | ((d: T) => number) | undefined, fallback: (d: T) => number) {
|
|
53
|
+
if (typeof v === 'function') return v as (d: T) => number;
|
|
54
|
+
if (v != null) return () => v as number;
|
|
55
|
+
return fallback;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const rxFn = $derived(asFn(rx, rFn));
|
|
59
|
+
const ryFn = $derived(asFn(ry, rFn));
|
|
60
|
+
|
|
61
|
+
// Default baseline: the chart-coord position of the anchor edge / centerline.
|
|
62
|
+
const baseline = $derived.by(() => {
|
|
63
|
+
if (baselineProp != null) return baselineProp;
|
|
64
|
+
const dim = axis === 'y' ? ctx.height : ctx.width;
|
|
65
|
+
if (resolvedAnchor === 'middle') return dim / 2;
|
|
66
|
+
if (axis === 'y') return resolvedAnchor === 'top' ? 0 : dim; // bottom
|
|
67
|
+
return resolvedAnchor === 'right' ? dim : 0; // left
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const items = $derived.by(() => {
|
|
71
|
+
const input = data.map((d, index) => ({
|
|
72
|
+
x: Number(positionFn(d)) || 0,
|
|
73
|
+
rx: Number(rxFn(d)) || 0,
|
|
74
|
+
ry: Number(ryFn(d)) || 0,
|
|
75
|
+
data: d,
|
|
76
|
+
index,
|
|
77
|
+
}));
|
|
78
|
+
return dodge(input, {
|
|
79
|
+
axis,
|
|
80
|
+
anchor: resolvedAnchor,
|
|
81
|
+
padding,
|
|
82
|
+
baseline,
|
|
83
|
+
rectangular,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
{@render children?.({ items })}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type { DodgeAnchor, DodgeItem, DodgeProps, DodgePropsWithoutHTML, } from './Dodge.shared.svelte.js';
|
|
2
|
+
export { dodge } from './Dodge.shared.svelte.js';
|
|
3
|
+
import { type DodgeProps } from './Dodge.shared.svelte.js';
|
|
4
|
+
declare function $$render<T = any>(): {
|
|
5
|
+
props: DodgeProps<T>;
|
|
6
|
+
exports: {};
|
|
7
|
+
bindings: "";
|
|
8
|
+
slots: {};
|
|
9
|
+
events: {};
|
|
10
|
+
};
|
|
11
|
+
declare class __sveltets_Render<T = any> {
|
|
12
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
13
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
14
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
15
|
+
bindings(): "";
|
|
16
|
+
exports(): {};
|
|
17
|
+
}
|
|
18
|
+
interface $$IsomorphicComponent {
|
|
19
|
+
new <T = any>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
20
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
21
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
22
|
+
<T = any>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
23
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
24
|
+
}
|
|
25
|
+
declare const Dodge: $$IsomorphicComponent;
|
|
26
|
+
type Dodge<T = any> = InstanceType<typeof Dodge<T>>;
|
|
27
|
+
export default Dodge;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|