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
|
@@ -30,10 +30,13 @@
|
|
|
30
30
|
rotate,
|
|
31
31
|
dx,
|
|
32
32
|
dy,
|
|
33
|
+
// `fontSize` is a typed prop (drives `capHeight` defaults), but on the
|
|
34
|
+
// DOM it must be rendered as the kebab-case `font-size` attribute.
|
|
35
|
+
fontSize,
|
|
33
36
|
...rest
|
|
34
37
|
}: TextProps = $props();
|
|
35
38
|
|
|
36
|
-
const c = new TextState(() => ({ rotate, dx, dy, ...rest } as TextProps));
|
|
39
|
+
const c = new TextState(() => ({ rotate, dx, dy, fontSize, ...rest } as TextProps));
|
|
37
40
|
|
|
38
41
|
let ref = $state<SVGTextElement>();
|
|
39
42
|
let svgRef = $state<SVGElement>();
|
|
@@ -78,6 +81,7 @@
|
|
|
78
81
|
transform={(rest.transform as string | undefined) ?? dataRotateTransform}
|
|
79
82
|
text-anchor={rest.textAnchor ?? 'start'}
|
|
80
83
|
dominant-baseline={rest.dominantBaseline ?? 'auto'}
|
|
84
|
+
font-size={fontSize}
|
|
81
85
|
fill={resolvedFill}
|
|
82
86
|
fill-opacity={resolvedFillOpacity}
|
|
83
87
|
stroke={resolvedStroke}
|
|
@@ -109,6 +113,7 @@
|
|
|
109
113
|
{...rest as any}
|
|
110
114
|
bind:this={ref}
|
|
111
115
|
dy={dy ?? 0}
|
|
116
|
+
font-size={fontSize}
|
|
112
117
|
fill={c.staticFill}
|
|
113
118
|
fill-opacity={c.staticFillOpacity}
|
|
114
119
|
stroke={c.staticStroke}
|
|
@@ -140,6 +145,7 @@
|
|
|
140
145
|
transform={c.transform}
|
|
141
146
|
text-anchor={rest.textAnchor ?? 'start'}
|
|
142
147
|
dominant-baseline={rest.dominantBaseline ?? 'auto'}
|
|
148
|
+
font-size={fontSize}
|
|
143
149
|
fill={c.staticFill}
|
|
144
150
|
fill-opacity={c.staticFillOpacity}
|
|
145
151
|
stroke={c.staticStroke}
|
|
@@ -122,12 +122,15 @@
|
|
|
122
122
|
return '';
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
125
|
+
// Only allocate the tween container when the user opts into a tween via
|
|
126
|
+
// `motion`; otherwise the template reads `trailPath` directly.
|
|
127
|
+
const tweenState =
|
|
128
|
+
extractTweenConfig(motion) != null
|
|
129
|
+
? createMotion(defaultPathData(), () => trailPath, {
|
|
130
|
+
type: 'tween',
|
|
131
|
+
interpolate: interpolatePath,
|
|
132
|
+
})
|
|
133
|
+
: null;
|
|
131
134
|
|
|
132
135
|
ctx.registerComponent({
|
|
133
136
|
name: 'Trail',
|
|
@@ -137,7 +140,7 @@
|
|
|
137
140
|
</script>
|
|
138
141
|
|
|
139
142
|
<Path
|
|
140
|
-
pathData={
|
|
143
|
+
pathData={tweenState ? tweenState.current : trailPath}
|
|
141
144
|
{fill}
|
|
142
145
|
{fillOpacity}
|
|
143
146
|
{opacity}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { SVGAttributes } from 'svelte/elements';
|
|
3
|
+
import type { ChartState } from '../../states/chart.svelte.js';
|
|
4
|
+
import { type Accessor } from '../../utils/common.js';
|
|
5
|
+
import { type Insets } from '../../utils/rect.svelte.js';
|
|
6
|
+
import type { CommonStyleProps, Without } from '../../utils/types.js';
|
|
7
|
+
export type WaffleRound = boolean | ((n: number) => number);
|
|
8
|
+
export type WafflePropsWithoutHTML = {
|
|
9
|
+
/** Override chart context data. */
|
|
10
|
+
data?: any[];
|
|
11
|
+
/** Override `x` from context. @default ctx.x */
|
|
12
|
+
x?: Accessor;
|
|
13
|
+
/** Override `y` from context. @default ctx.y */
|
|
14
|
+
y?: Accessor;
|
|
15
|
+
/** Override `x1` from context. @default ctx.x1 */
|
|
16
|
+
x1?: Accessor;
|
|
17
|
+
/** Override `y1` from context. @default ctx.y1 */
|
|
18
|
+
y1?: Accessor;
|
|
19
|
+
/**
|
|
20
|
+
* Axis the waffle extends along (the value axis).
|
|
21
|
+
*
|
|
22
|
+
* - `'y'` (default): vertical waffle, like Plot's `waffleY`. Cells stack
|
|
23
|
+
* upward from the value=0 baseline.
|
|
24
|
+
* - `'x'`: horizontal waffle, like Plot's `waffleX`. Cells extend rightward.
|
|
25
|
+
*
|
|
26
|
+
* Falls back to the chart's `valueAxis`.
|
|
27
|
+
*/
|
|
28
|
+
axis?: 'x' | 'y';
|
|
29
|
+
/**
|
|
30
|
+
* The quantity each cell represents. Larger units produce fewer cells.
|
|
31
|
+
* @default 1
|
|
32
|
+
*/
|
|
33
|
+
unit?: number;
|
|
34
|
+
/**
|
|
35
|
+
* The number of cells per row (along the anchor axis). When omitted,
|
|
36
|
+
* computed automatically from the bar width and unit so cells stay
|
|
37
|
+
* approximately square.
|
|
38
|
+
*/
|
|
39
|
+
multiple?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Pixel separation between adjacent cells.
|
|
42
|
+
* @default 1
|
|
43
|
+
*/
|
|
44
|
+
gap?: number;
|
|
45
|
+
/**
|
|
46
|
+
* How to handle non-integer cell counts.
|
|
47
|
+
*
|
|
48
|
+
* - `false` (default) — keep the partial cell as a fractional cut-off
|
|
49
|
+
* - `true` — `Math.round`
|
|
50
|
+
* - function — custom rounding (e.g. `Math.floor`, `Math.ceil`)
|
|
51
|
+
*/
|
|
52
|
+
round?: WaffleRound;
|
|
53
|
+
/** Cell horizontal corner radius (number of pixels, or "100%" for circles). */
|
|
54
|
+
rx?: number | string;
|
|
55
|
+
/** Cell vertical corner radius (number of pixels, or "100%" for circles). */
|
|
56
|
+
ry?: number | string;
|
|
57
|
+
/** Series key for stacked-waffle support. */
|
|
58
|
+
seriesKey?: string;
|
|
59
|
+
/** Insets to shrink each waffle's bounding band. */
|
|
60
|
+
insets?: Insets;
|
|
61
|
+
/** Fixed band-axis size in pixels. Override the band width / height. */
|
|
62
|
+
width?: number;
|
|
63
|
+
/** Fixed band-axis size in pixels. Override the band width / height. */
|
|
64
|
+
height?: number;
|
|
65
|
+
/** Default `(d, i) => i` */
|
|
66
|
+
key?: (d: any, index: number) => any;
|
|
67
|
+
/** Setup pointer events to show tooltip for the hovered datum. */
|
|
68
|
+
tooltip?: boolean;
|
|
69
|
+
/** Click handler invoked with `(event, { data })` for the hovered waffle. */
|
|
70
|
+
onWaffleClick?: (e: MouseEvent, detail: {
|
|
71
|
+
data: any;
|
|
72
|
+
}) => void;
|
|
73
|
+
/**
|
|
74
|
+
* Render a custom symbol per cell instead of the default rect. The snippet
|
|
75
|
+
* receives the cell's `width`/`height` and the resolved `color` for the
|
|
76
|
+
* cell. The CSS `color` is applied to the wrapping element, so any nested
|
|
77
|
+
* SVG using `fill="currentColor"` (or stroke) inherits it automatically.
|
|
78
|
+
*
|
|
79
|
+
* SVG layer only — canvas falls back to the default rect.
|
|
80
|
+
*/
|
|
81
|
+
symbol?: Snippet<[
|
|
82
|
+
{
|
|
83
|
+
width: number;
|
|
84
|
+
height: number;
|
|
85
|
+
datum: any;
|
|
86
|
+
color: string;
|
|
87
|
+
}
|
|
88
|
+
]>;
|
|
89
|
+
} & CommonStyleProps;
|
|
90
|
+
export type WaffleProps = WafflePropsWithoutHTML & Without<Omit<SVGAttributes<SVGElement>, 'width' | 'height' | 'x' | 'y'>, WafflePropsWithoutHTML>;
|
|
91
|
+
/** Per-datum, fully-resolved waffle layout — pixel coords ready to render. */
|
|
92
|
+
export type WaffleItem = {
|
|
93
|
+
data: any;
|
|
94
|
+
index: number;
|
|
95
|
+
/** SVG path data for the waffle outline (relative to translate). */
|
|
96
|
+
pathData: string;
|
|
97
|
+
/** Pixel translate origin — apply to the path/pattern as `translate(tx, ty)`. */
|
|
98
|
+
tx: number;
|
|
99
|
+
ty: number;
|
|
100
|
+
/** Cell box width in pixels (pattern tile width). */
|
|
101
|
+
cx: number;
|
|
102
|
+
/** Cell box height in pixels (pattern tile height). */
|
|
103
|
+
cy: number;
|
|
104
|
+
/** Cell centroid in pixel coords (translated). */
|
|
105
|
+
centroid: {
|
|
106
|
+
x: number;
|
|
107
|
+
y: number;
|
|
108
|
+
};
|
|
109
|
+
/** Resolved fill color. */
|
|
110
|
+
fill: string | null;
|
|
111
|
+
};
|
|
112
|
+
export type WaffleLayoutOptions = {
|
|
113
|
+
axis: 'x' | 'y';
|
|
114
|
+
unit: number;
|
|
115
|
+
multiple?: number;
|
|
116
|
+
round: (n: number) => number;
|
|
117
|
+
/** Anchor-axis size in pixels (the bar's other-axis extent). */
|
|
118
|
+
barSize: number;
|
|
119
|
+
/** Anchor-axis pixel position (the bar's other-axis start). */
|
|
120
|
+
barStart: number;
|
|
121
|
+
/** Pixels per data unit on the value axis (signed; negative = inverted). */
|
|
122
|
+
pixelsPerUnit: number;
|
|
123
|
+
/** Pixel position of the value=0 baseline along the value axis. */
|
|
124
|
+
valueZero: number;
|
|
125
|
+
/** Value range in data units. */
|
|
126
|
+
v1: number;
|
|
127
|
+
v2: number;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Shared reactive state for Waffle. Resolves accessors, computes per-datum
|
|
131
|
+
* dimensions and waffle layouts (pattern tile size, polygon path, centroid),
|
|
132
|
+
* and exposes them via `items`.
|
|
133
|
+
*/
|
|
134
|
+
export declare class WaffleState {
|
|
135
|
+
#private;
|
|
136
|
+
ctx: ChartState;
|
|
137
|
+
constructor(getProps: () => WaffleProps);
|
|
138
|
+
axis: "x" | "y";
|
|
139
|
+
unit: number;
|
|
140
|
+
gap: number;
|
|
141
|
+
round: (n: number) => number;
|
|
142
|
+
multipleProp: number | undefined;
|
|
143
|
+
series: import("../index.js").SeriesData<any, any> | undefined;
|
|
144
|
+
/** Opacity multiplier — fades to 0.1 when another series is highlighted. */
|
|
145
|
+
seriesOpacity: number;
|
|
146
|
+
seriesAccessor: string | number | ((d: any) => any) | Accessor<any>[] | undefined;
|
|
147
|
+
stackAccessors: {
|
|
148
|
+
y0: (d: any) => number;
|
|
149
|
+
y1: (d: any) => number;
|
|
150
|
+
value: (d: any) => [number, number] | null;
|
|
151
|
+
} | null;
|
|
152
|
+
data: any[];
|
|
153
|
+
x: Accessor;
|
|
154
|
+
y: Accessor;
|
|
155
|
+
getDimensions: (item: any) => {
|
|
156
|
+
x: any;
|
|
157
|
+
y: any;
|
|
158
|
+
width: number;
|
|
159
|
+
height: number;
|
|
160
|
+
} | undefined;
|
|
161
|
+
/** Pixel slope of the value-axis scale (signed). */
|
|
162
|
+
pixelsPerUnit: number;
|
|
163
|
+
/** Pixel position of value=0 along the value axis. */
|
|
164
|
+
valueZero: number;
|
|
165
|
+
items: WaffleItem[];
|
|
166
|
+
/** Resolved gap. */
|
|
167
|
+
resolvedGap: number;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Generate the polygon outline of a waffle covering the cell interval
|
|
171
|
+
* `[i1, i2)` on a grid of `columns` columns. The shape is approximately
|
|
172
|
+
* rectangular but may have one or two corner cuts when the start or end
|
|
173
|
+
* value is not aligned to a row boundary, plus extra cuts for fractional
|
|
174
|
+
* intervals. The last point is the centroid (popped by callers for tooltips
|
|
175
|
+
* and tip placement).
|
|
176
|
+
*
|
|
177
|
+
* Coordinate space is `(column, row)` in cell units — callers transform to
|
|
178
|
+
* pixel space (and may negate the row axis for screen y).
|
|
179
|
+
*
|
|
180
|
+
* @see https://github.com/observablehq/plot/blob/main/src/marks/waffle.js
|
|
181
|
+
*/
|
|
182
|
+
export declare function wafflePoints(i1: number, i2: number, columns: number): [number, number][];
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { accessor, chartDataArray } from '../../utils/common.js';
|
|
2
|
+
import { getChartContext } from '../../contexts/chart.js';
|
|
3
|
+
import { createDimensionGetter } from '../../utils/rect.svelte.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shared reactive state for Waffle. Resolves accessors, computes per-datum
|
|
6
|
+
* dimensions and waffle layouts (pattern tile size, polygon path, centroid),
|
|
7
|
+
* and exposes them via `items`.
|
|
8
|
+
*/
|
|
9
|
+
export class WaffleState {
|
|
10
|
+
#getProps = () => ({});
|
|
11
|
+
ctx = getChartContext();
|
|
12
|
+
constructor(getProps) {
|
|
13
|
+
this.#getProps = getProps;
|
|
14
|
+
this.ctx.registerComponent({
|
|
15
|
+
name: 'Waffle',
|
|
16
|
+
kind: 'mark',
|
|
17
|
+
markInfo: () => {
|
|
18
|
+
const p = getProps();
|
|
19
|
+
return {
|
|
20
|
+
data: p.data,
|
|
21
|
+
seriesKey: p.seriesKey,
|
|
22
|
+
color: p.fill,
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
axis = $derived(this.#getProps().axis ?? this.ctx.valueAxis);
|
|
28
|
+
unit = $derived(Math.max(0, this.#getProps().unit ?? 1));
|
|
29
|
+
gap = $derived(+(this.#getProps().gap ?? 1));
|
|
30
|
+
round = $derived(maybeRound(this.#getProps().round));
|
|
31
|
+
multipleProp = $derived(maybeMultiple(this.#getProps().multiple));
|
|
32
|
+
series = $derived.by(() => {
|
|
33
|
+
const seriesKey = this.#getProps().seriesKey;
|
|
34
|
+
return seriesKey ? this.ctx.series.series.find((s) => s.key === seriesKey) : undefined;
|
|
35
|
+
});
|
|
36
|
+
/** Opacity multiplier — fades to 0.1 when another series is highlighted. */
|
|
37
|
+
seriesOpacity = $derived.by(() => {
|
|
38
|
+
if (this.series?.key == null ||
|
|
39
|
+
this.ctx.series.visibleSeries.length <= 1 ||
|
|
40
|
+
this.ctx.series.isHighlighted(this.series.key, true)) {
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
return 0.1;
|
|
44
|
+
});
|
|
45
|
+
seriesAccessor = $derived(this.series
|
|
46
|
+
? (this.series.value ?? (this.series.data ? undefined : this.series.key))
|
|
47
|
+
: undefined);
|
|
48
|
+
stackAccessors = $derived.by(() => {
|
|
49
|
+
const seriesKey = this.#getProps().seriesKey;
|
|
50
|
+
return seriesKey && this.ctx.series.isStacked
|
|
51
|
+
? this.ctx.series.getStackAccessors(seriesKey)
|
|
52
|
+
: null;
|
|
53
|
+
});
|
|
54
|
+
data = $derived.by(() => {
|
|
55
|
+
const dataProp = this.#getProps().data;
|
|
56
|
+
if (dataProp)
|
|
57
|
+
return dataProp;
|
|
58
|
+
return this.series?.data ?? chartDataArray(this.ctx.data);
|
|
59
|
+
});
|
|
60
|
+
x = $derived.by(() => {
|
|
61
|
+
const xProp = this.#getProps().x;
|
|
62
|
+
return (xProp ??
|
|
63
|
+
(this.ctx.valueAxis === 'x'
|
|
64
|
+
? (this.stackAccessors?.value ?? this.seriesAccessor)
|
|
65
|
+
: undefined) ??
|
|
66
|
+
this.ctx.x);
|
|
67
|
+
});
|
|
68
|
+
y = $derived.by(() => {
|
|
69
|
+
const yProp = this.#getProps().y;
|
|
70
|
+
return (yProp ??
|
|
71
|
+
(this.ctx.valueAxis === 'y'
|
|
72
|
+
? (this.stackAccessors?.value ?? this.seriesAccessor)
|
|
73
|
+
: undefined) ??
|
|
74
|
+
this.ctx.y);
|
|
75
|
+
});
|
|
76
|
+
getDimensions = $derived(createDimensionGetter(this.ctx, () => ({
|
|
77
|
+
x: this.x,
|
|
78
|
+
y: this.y,
|
|
79
|
+
x1: this.#getProps().x1,
|
|
80
|
+
y1: this.#getProps().y1,
|
|
81
|
+
insets: this.#getProps().insets,
|
|
82
|
+
})));
|
|
83
|
+
/** Pixel slope of the value-axis scale (signed). */
|
|
84
|
+
pixelsPerUnit = $derived.by(() => {
|
|
85
|
+
const scale = this.axis === 'y' ? this.ctx.yScale : this.ctx.xScale;
|
|
86
|
+
if (typeof scale !== 'function')
|
|
87
|
+
return 0;
|
|
88
|
+
const a = Number(scale(0));
|
|
89
|
+
const b = Number(scale(1));
|
|
90
|
+
if (!Number.isFinite(a) || !Number.isFinite(b))
|
|
91
|
+
return 0;
|
|
92
|
+
return b - a;
|
|
93
|
+
});
|
|
94
|
+
/** Pixel position of value=0 along the value axis. */
|
|
95
|
+
valueZero = $derived.by(() => {
|
|
96
|
+
const scale = this.axis === 'y' ? this.ctx.yScale : this.ctx.xScale;
|
|
97
|
+
if (typeof scale !== 'function')
|
|
98
|
+
return 0;
|
|
99
|
+
return Number(scale(0)) || 0;
|
|
100
|
+
});
|
|
101
|
+
items = $derived.by(() => {
|
|
102
|
+
const props = this.#getProps();
|
|
103
|
+
const axis = this.axis;
|
|
104
|
+
const unit = this.unit;
|
|
105
|
+
const round = this.round;
|
|
106
|
+
const gap = this.gap;
|
|
107
|
+
const multipleProp = this.multipleProp;
|
|
108
|
+
const data = this.data;
|
|
109
|
+
const pixelsPerUnit = this.pixelsPerUnit;
|
|
110
|
+
const valueZero = this.valueZero;
|
|
111
|
+
if (!data || data.length === 0)
|
|
112
|
+
return [];
|
|
113
|
+
if (!Number.isFinite(pixelsPerUnit) || pixelsPerUnit === 0 || unit <= 0)
|
|
114
|
+
return [];
|
|
115
|
+
const result = [];
|
|
116
|
+
const cellPixels = unit * Math.abs(pixelsPerUnit); // pixels per cell along value axis
|
|
117
|
+
// Determine value range accessor — for stacked series, reads [v1, v2] arrays
|
|
118
|
+
// produced by the chart's stack series; otherwise treats value as [0, v].
|
|
119
|
+
const valueAccessorFn = accessor(axis === 'y'
|
|
120
|
+
? (this.stackAccessors?.value ?? this.seriesAccessor ?? this.#getProps().y ?? this.ctx.y)
|
|
121
|
+
: (this.stackAccessors?.value ?? this.seriesAccessor ?? this.#getProps().x ?? this.ctx.x));
|
|
122
|
+
for (let i = 0; i < data.length; i++) {
|
|
123
|
+
const d = data[i];
|
|
124
|
+
const dim = this.getDimensions(d);
|
|
125
|
+
if (!dim)
|
|
126
|
+
continue;
|
|
127
|
+
let { x, y, width, height } = dim;
|
|
128
|
+
// Width override
|
|
129
|
+
if (props.width != null && axis === 'y') {
|
|
130
|
+
x = x + (width - props.width) / 2;
|
|
131
|
+
width = props.width;
|
|
132
|
+
}
|
|
133
|
+
if (props.height != null && axis === 'x') {
|
|
134
|
+
y = y + (height - props.height) / 2;
|
|
135
|
+
height = props.height;
|
|
136
|
+
}
|
|
137
|
+
const barSize = axis === 'y' ? width : height;
|
|
138
|
+
const barStart = axis === 'y' ? x : y;
|
|
139
|
+
if (barSize <= 0)
|
|
140
|
+
continue;
|
|
141
|
+
const rawValue = valueAccessorFn(d);
|
|
142
|
+
let v1 = 0;
|
|
143
|
+
let v2;
|
|
144
|
+
if (Array.isArray(rawValue)) {
|
|
145
|
+
v1 = Number(rawValue[0]) || 0;
|
|
146
|
+
v2 = Number(rawValue[1]) || 0;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
v2 = Number(rawValue) || 0;
|
|
150
|
+
}
|
|
151
|
+
const i1 = round(v1 / unit);
|
|
152
|
+
const i2 = round(v2 / unit);
|
|
153
|
+
if (i1 === i2)
|
|
154
|
+
continue;
|
|
155
|
+
// Default `multiple` from Plot: keep cells approximately square.
|
|
156
|
+
const multiple = multipleProp ?? Math.max(1, Math.floor(Math.sqrt(barSize / cellPixels)));
|
|
157
|
+
// Outer cell tile size (along anchor and dodge axes).
|
|
158
|
+
const cx = Math.min(barSize / multiple, cellPixels * multiple);
|
|
159
|
+
const cy = cellPixels * multiple;
|
|
160
|
+
// Center the cell grid within the bar.
|
|
161
|
+
const tx0 = barStart + (barSize - multiple * cx) / 2;
|
|
162
|
+
// Cells grow away from baseline toward positive values. Sign of
|
|
163
|
+
// `pixelsPerUnit` encodes the screen direction of value growth, so the
|
|
164
|
+
// same transform works for both inverted (typical svg y) and
|
|
165
|
+
// non-inverted scales.
|
|
166
|
+
const valueDir = pixelsPerUnit < 0 ? -1 : 1;
|
|
167
|
+
const polyPoints = wafflePoints(i1, i2, multiple);
|
|
168
|
+
// Pop centroid (last point) before mapping to path string.
|
|
169
|
+
const centroid = polyPoints.pop();
|
|
170
|
+
const transformPoint = axis === 'y'
|
|
171
|
+
? ([col, row]) => [col * cx, valueDir * row * cy]
|
|
172
|
+
: ([col, row]) => [valueDir * row * cy, col * cx];
|
|
173
|
+
const pts = polyPoints.map(transformPoint);
|
|
174
|
+
const pathData = pts.length === 0 ? '' : 'M' + pts.map((p) => `${p[0]},${p[1]}`).join('L') + 'Z';
|
|
175
|
+
const cPx = transformPoint(centroid);
|
|
176
|
+
const tx = axis === 'y' ? tx0 : valueZero;
|
|
177
|
+
const ty = axis === 'y' ? valueZero : tx0;
|
|
178
|
+
// Resolve fill: explicit `fill` prop > series color > color scale > null
|
|
179
|
+
const fillProp = props.fill;
|
|
180
|
+
let fill = null;
|
|
181
|
+
if (typeof fillProp === 'string')
|
|
182
|
+
fill = fillProp;
|
|
183
|
+
else if (this.series?.color)
|
|
184
|
+
fill = this.series.color;
|
|
185
|
+
else if (this.ctx.config.c)
|
|
186
|
+
fill = String(this.ctx.cGet(d) ?? '') || null;
|
|
187
|
+
result.push({
|
|
188
|
+
data: d,
|
|
189
|
+
index: i,
|
|
190
|
+
pathData,
|
|
191
|
+
tx,
|
|
192
|
+
ty,
|
|
193
|
+
cx,
|
|
194
|
+
cy,
|
|
195
|
+
centroid: { x: tx + cPx[0], y: ty + cPx[1] },
|
|
196
|
+
fill,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
200
|
+
});
|
|
201
|
+
/** Resolved gap. */
|
|
202
|
+
resolvedGap = $derived(this.gap);
|
|
203
|
+
}
|
|
204
|
+
function maybeMultiple(multiple) {
|
|
205
|
+
return multiple === undefined ? undefined : Math.max(1, Math.floor(multiple));
|
|
206
|
+
}
|
|
207
|
+
function maybeRound(round) {
|
|
208
|
+
if (round === undefined || round === false)
|
|
209
|
+
return Number;
|
|
210
|
+
if (round === true)
|
|
211
|
+
return Math.round;
|
|
212
|
+
if (typeof round !== 'function') {
|
|
213
|
+
throw new Error(`invalid round: ${round}`);
|
|
214
|
+
}
|
|
215
|
+
return round;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Generate the polygon outline of a waffle covering the cell interval
|
|
219
|
+
* `[i1, i2)` on a grid of `columns` columns. The shape is approximately
|
|
220
|
+
* rectangular but may have one or two corner cuts when the start or end
|
|
221
|
+
* value is not aligned to a row boundary, plus extra cuts for fractional
|
|
222
|
+
* intervals. The last point is the centroid (popped by callers for tooltips
|
|
223
|
+
* and tip placement).
|
|
224
|
+
*
|
|
225
|
+
* Coordinate space is `(column, row)` in cell units — callers transform to
|
|
226
|
+
* pixel space (and may negate the row axis for screen y).
|
|
227
|
+
*
|
|
228
|
+
* @see https://github.com/observablehq/plot/blob/main/src/marks/waffle.js
|
|
229
|
+
*/
|
|
230
|
+
export function wafflePoints(i1, i2, columns) {
|
|
231
|
+
if (i2 < i1)
|
|
232
|
+
return wafflePoints(i2, i1, columns);
|
|
233
|
+
if (i1 < 0) {
|
|
234
|
+
return wafflePointsOffset(i1, i2, columns, Math.ceil(-Math.min(i1, i2) / columns));
|
|
235
|
+
}
|
|
236
|
+
const x1f = Math.floor(i1 % columns);
|
|
237
|
+
const x1c = Math.ceil(i1 % columns);
|
|
238
|
+
const x2f = Math.floor(i2 % columns);
|
|
239
|
+
const x2c = Math.ceil(i2 % columns);
|
|
240
|
+
const y1f = Math.floor(i1 / columns);
|
|
241
|
+
const y1c = Math.ceil(i1 / columns);
|
|
242
|
+
const y2f = Math.floor(i2 / columns);
|
|
243
|
+
const y2c = Math.ceil(i2 / columns);
|
|
244
|
+
const points = [];
|
|
245
|
+
if (y2c > y1c)
|
|
246
|
+
points.push([0, y1c]);
|
|
247
|
+
points.push([x1f, y1c], [x1f, y1f + (i1 % 1)], [x1c, y1f + (i1 % 1)]);
|
|
248
|
+
if (!(i1 % columns > columns - 1)) {
|
|
249
|
+
points.push([x1c, y1f]);
|
|
250
|
+
if (y2f > y1f)
|
|
251
|
+
points.push([columns, y1f]);
|
|
252
|
+
}
|
|
253
|
+
if (y2f > y1f)
|
|
254
|
+
points.push([columns, y2f]);
|
|
255
|
+
points.push([x2c, y2f], [x2c, y2f + (i2 % 1)], [x2f, y2f + (i2 % 1)]);
|
|
256
|
+
if (!(i2 % columns < 1)) {
|
|
257
|
+
points.push([x2f, y2c]);
|
|
258
|
+
if (y2c > y1c)
|
|
259
|
+
points.push([0, y2c]);
|
|
260
|
+
}
|
|
261
|
+
points.push(waffleCentroid(i1, i2, columns));
|
|
262
|
+
return points;
|
|
263
|
+
}
|
|
264
|
+
function wafflePointsOffset(i1, i2, columns, k) {
|
|
265
|
+
return wafflePoints(i1 + k * columns, i2 + k * columns, columns).map(([x, y]) => [x, y - k]);
|
|
266
|
+
}
|
|
267
|
+
function waffleCentroid(i1, i2, columns) {
|
|
268
|
+
const r = Math.floor(i2 / columns) - Math.floor(i1 / columns);
|
|
269
|
+
if (r === 0)
|
|
270
|
+
return waffleRowCentroid(i1, i2, columns);
|
|
271
|
+
if (r === 1) {
|
|
272
|
+
if (Math.floor(i2 % columns) > Math.ceil(i1 % columns)) {
|
|
273
|
+
return [(Math.floor(i2 % columns) + Math.ceil(i1 % columns)) / 2, Math.floor(i2 / columns)];
|
|
274
|
+
}
|
|
275
|
+
if (i2 % columns > columns - (i1 % columns)) {
|
|
276
|
+
return waffleRowCentroid(i2 - (i2 % columns), i2, columns);
|
|
277
|
+
}
|
|
278
|
+
return waffleRowCentroid(i1, columns * Math.ceil(i1 / columns), columns);
|
|
279
|
+
}
|
|
280
|
+
return [columns / 2, (Math.round(i1 / columns) + Math.round(i2 / columns)) / 2];
|
|
281
|
+
}
|
|
282
|
+
function waffleRowCentroid(i1, i2, columns) {
|
|
283
|
+
const c = Math.floor(i2) - Math.floor(i1);
|
|
284
|
+
if (c === 0) {
|
|
285
|
+
return [Math.floor(i1 % columns) + 0.5, Math.floor(i1 / columns) + (((i1 + i2) / 2) % 1)];
|
|
286
|
+
}
|
|
287
|
+
if (c === 1) {
|
|
288
|
+
if ((i2 % 1) - (i1 % 1) > 0.5) {
|
|
289
|
+
return [Math.ceil(i1 % columns), Math.floor(i2 / columns) + ((i1 % 1) + (i2 % 1)) / 2];
|
|
290
|
+
}
|
|
291
|
+
if (i2 % 1 > 1 - (i1 % 1)) {
|
|
292
|
+
return [Math.floor(i2 % columns) + 0.5, Math.floor(i2 / columns) + (i2 % 1) / 2];
|
|
293
|
+
}
|
|
294
|
+
return [Math.floor(i1 % columns) + 0.5, Math.floor(i1 / columns) + (1 + (i1 % 1)) / 2];
|
|
295
|
+
}
|
|
296
|
+
return [
|
|
297
|
+
Math.ceil(i1 % columns) + Math.ceil(Math.floor(i2) - Math.ceil(i1)) / 2,
|
|
298
|
+
Math.floor(i1 / columns) + (i2 >= 1 + i1 ? 0.5 : ((i1 + i2) / 2) % 1),
|
|
299
|
+
];
|
|
300
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type { WaffleProps, WafflePropsWithoutHTML } from './Waffle.shared.svelte.js';
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { cls } from '@layerstack/tailwind';
|
|
7
|
+
|
|
8
|
+
import Group from '../Group/Group.svelte';
|
|
9
|
+
import Path from '../Path/Path.svelte';
|
|
10
|
+
import Pattern from '../Pattern/Pattern.svelte';
|
|
11
|
+
|
|
12
|
+
import { WaffleState, type WaffleProps } from './Waffle.shared.svelte.js';
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
data: dataProp,
|
|
16
|
+
x: xProp,
|
|
17
|
+
y: yProp,
|
|
18
|
+
x1: x1Prop,
|
|
19
|
+
y1: y1Prop,
|
|
20
|
+
axis,
|
|
21
|
+
unit,
|
|
22
|
+
multiple,
|
|
23
|
+
gap,
|
|
24
|
+
round,
|
|
25
|
+
rx,
|
|
26
|
+
ry,
|
|
27
|
+
seriesKey,
|
|
28
|
+
insets,
|
|
29
|
+
width,
|
|
30
|
+
height,
|
|
31
|
+
key = (_, i) => i,
|
|
32
|
+
fill,
|
|
33
|
+
fillOpacity,
|
|
34
|
+
stroke,
|
|
35
|
+
strokeWidth,
|
|
36
|
+
opacity,
|
|
37
|
+
class: className,
|
|
38
|
+
tooltip,
|
|
39
|
+
onWaffleClick,
|
|
40
|
+
onpointerenter,
|
|
41
|
+
onpointermove,
|
|
42
|
+
onpointerleave,
|
|
43
|
+
onclick,
|
|
44
|
+
symbol,
|
|
45
|
+
...rest
|
|
46
|
+
}: WaffleProps = $props();
|
|
47
|
+
|
|
48
|
+
const c = new WaffleState(
|
|
49
|
+
() =>
|
|
50
|
+
({
|
|
51
|
+
data: dataProp,
|
|
52
|
+
x: xProp,
|
|
53
|
+
y: yProp,
|
|
54
|
+
x1: x1Prop,
|
|
55
|
+
y1: y1Prop,
|
|
56
|
+
axis,
|
|
57
|
+
unit,
|
|
58
|
+
multiple,
|
|
59
|
+
gap,
|
|
60
|
+
round,
|
|
61
|
+
rx,
|
|
62
|
+
ry,
|
|
63
|
+
seriesKey,
|
|
64
|
+
insets,
|
|
65
|
+
width,
|
|
66
|
+
height,
|
|
67
|
+
fill,
|
|
68
|
+
}) as WaffleProps
|
|
69
|
+
);
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
{#each c.items as item (key(item.data, item.index))}
|
|
73
|
+
{@const onItemEnter = (e: PointerEvent) => {
|
|
74
|
+
onpointerenter?.(e as any);
|
|
75
|
+
if (tooltip) c.ctx.tooltip.show(e, item.data);
|
|
76
|
+
}}
|
|
77
|
+
{@const onItemMove = (e: PointerEvent) => {
|
|
78
|
+
onpointermove?.(e as any);
|
|
79
|
+
if (tooltip) c.ctx.tooltip.show(e, item.data);
|
|
80
|
+
}}
|
|
81
|
+
{@const onItemLeave = (e: PointerEvent) => {
|
|
82
|
+
onpointerleave?.(e as any);
|
|
83
|
+
if (tooltip) c.ctx.tooltip.hide();
|
|
84
|
+
}}
|
|
85
|
+
{@const onItemClick = (e: MouseEvent) => {
|
|
86
|
+
onclick?.(e as any);
|
|
87
|
+
onWaffleClick?.(e, { data: item.data });
|
|
88
|
+
}}
|
|
89
|
+
{@const cellInset = c.gap / 2}
|
|
90
|
+
{@const innerWidth = Math.max(0, item.cx - 2 * cellInset)}
|
|
91
|
+
{@const innerHeight = Math.max(0, item.cy - 2 * cellInset)}
|
|
92
|
+
{@const color = item.fill ?? (typeof fill === 'string' ? fill : undefined) ?? 'currentColor'}
|
|
93
|
+
{#snippet symbolPatternContent()}
|
|
94
|
+
<g transform={`translate(${cellInset},${cellInset})`} {color}>
|
|
95
|
+
{@render symbol?.({
|
|
96
|
+
width: innerWidth,
|
|
97
|
+
height: innerHeight,
|
|
98
|
+
datum: item.data,
|
|
99
|
+
color,
|
|
100
|
+
})}
|
|
101
|
+
</g>
|
|
102
|
+
{/snippet}
|
|
103
|
+
<Group
|
|
104
|
+
x={item.tx}
|
|
105
|
+
y={item.ty}
|
|
106
|
+
class={cls('lc-waffle', className)}
|
|
107
|
+
opacity={(opacity ?? 1) * c.seriesOpacity}
|
|
108
|
+
>
|
|
109
|
+
<Pattern
|
|
110
|
+
width={item.cx}
|
|
111
|
+
height={item.cy}
|
|
112
|
+
rects={[
|
|
113
|
+
{
|
|
114
|
+
inset: cellInset,
|
|
115
|
+
color: item.fill ?? (typeof fill === 'string' ? fill : undefined),
|
|
116
|
+
opacity: fillOpacity,
|
|
117
|
+
rx,
|
|
118
|
+
ry,
|
|
119
|
+
},
|
|
120
|
+
]}
|
|
121
|
+
patternContent={symbol ? symbolPatternContent : undefined}
|
|
122
|
+
>
|
|
123
|
+
{#snippet children({ pattern })}
|
|
124
|
+
<Path
|
|
125
|
+
pathData={item.pathData}
|
|
126
|
+
fill={pattern}
|
|
127
|
+
stroke={stroke as string | undefined}
|
|
128
|
+
strokeWidth={strokeWidth as number | undefined}
|
|
129
|
+
class="lc-waffle-cell"
|
|
130
|
+
onpointerenter={onItemEnter}
|
|
131
|
+
onpointermove={onItemMove}
|
|
132
|
+
onpointerleave={onItemLeave}
|
|
133
|
+
onclick={onItemClick}
|
|
134
|
+
{...rest}
|
|
135
|
+
/>
|
|
136
|
+
{/snippet}
|
|
137
|
+
</Pattern>
|
|
138
|
+
</Group>
|
|
139
|
+
{/each}
|
|
140
|
+
|
|
141
|
+
<style>
|
|
142
|
+
@layer components {
|
|
143
|
+
:global(:where(.lc-waffle-cell)) {
|
|
144
|
+
--fill-color: var(--color-primary, currentColor);
|
|
145
|
+
--stroke-color: none;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
</style>
|