layerchart 2.0.0-next.55 → 2.0.0-next.56
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/bench/ComposableLineChart.svelte +1 -1
- package/dist/bench/GeoBench.svelte +1 -8
- package/dist/components/AnnotationRange.svelte +3 -1
- package/dist/components/Arc.svelte +1 -3
- package/dist/components/ArcLabel.svelte.test.js +7 -7
- package/dist/components/Bar.svelte +4 -2
- package/dist/components/BoxPlot.svelte +4 -12
- package/dist/components/Cell.svelte +13 -8
- package/dist/components/Chart.svelte +69 -26
- package/dist/components/ChartChildren.svelte +22 -4
- package/dist/components/Circle.svelte +34 -12
- package/dist/components/ClipPath.svelte +3 -9
- package/dist/components/Ellipse.svelte +27 -6
- package/dist/components/GeoLegend.svelte +1 -3
- package/dist/components/GeoPoint.svelte +25 -3
- package/dist/components/GeoSpline.svelte +1 -4
- package/dist/components/GeoTile.svelte +8 -4
- package/dist/components/Group.svelte +11 -5
- package/dist/components/Highlight.svelte +3 -3
- package/dist/components/Image.svelte +42 -30
- package/dist/components/Labels.svelte +2 -4
- package/dist/components/Line.svelte +7 -6
- package/dist/components/LinearGradient.svelte +8 -4
- package/dist/components/Link.svelte +0 -1
- package/dist/components/Marker.svelte +9 -1
- package/dist/components/Path.svelte +43 -23
- package/dist/components/Pattern.svelte +101 -5
- package/dist/components/Pattern.svelte.d.ts +3 -1
- package/dist/components/Pie.svelte +2 -6
- package/dist/components/RadialGradient.svelte +8 -4
- package/dist/components/Rect.svelte +29 -12
- package/dist/components/Spline.svelte +22 -4
- package/dist/components/Text.svelte +9 -5
- package/dist/components/Trail.svelte +19 -7
- package/dist/components/Vector.svelte +37 -14
- package/dist/components/Violin.svelte +1 -2
- package/dist/components/charts/ArcChart.svelte +8 -5
- package/dist/components/charts/AreaChart.svelte +6 -1
- package/dist/components/charts/BarChart.svelte +3 -1
- package/dist/components/charts/LineChart.svelte +6 -1
- package/dist/components/charts/PieChart.svelte +10 -3
- package/dist/components/tooltip/Tooltip.svelte +2 -8
- package/dist/contexts/chart.d.ts +1 -1
- package/dist/contexts/chart.js +3 -1
- package/dist/server/TestBarChart.svelte +28 -28
- package/dist/server/TestLineChart.svelte +28 -28
- package/dist/server/index.js +1 -1
- package/dist/states/brush.svelte.js +16 -13
- package/dist/states/chart.svelte.test.js +24 -19
- package/dist/states/geo.svelte.js +1 -4
- package/dist/states/series.svelte.js +1 -1
- package/dist/utils/canvas.js +7 -4
- package/dist/utils/trail.js +3 -4
- package/package.json +1 -1
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
}: Props = $props();
|
|
35
35
|
</script>
|
|
36
36
|
|
|
37
|
-
<Chart {data} {x} {y} {width} {height} {series}
|
|
37
|
+
<Chart {data} {x} {y} {width} {height} {series} {xDomain} {yDomain}>
|
|
38
38
|
<Layer type={layer}>
|
|
39
39
|
{#if axis}
|
|
40
40
|
<Axis placement="left" />
|
|
@@ -12,14 +12,7 @@
|
|
|
12
12
|
width?: number;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
let {
|
|
16
|
-
features,
|
|
17
|
-
fitGeojson,
|
|
18
|
-
projection,
|
|
19
|
-
layer = 'svg',
|
|
20
|
-
height = 400,
|
|
21
|
-
width,
|
|
22
|
-
}: Props = $props();
|
|
15
|
+
let { features, fitGeojson, projection, layer = 'svg', height = 400, width }: Props = $props();
|
|
23
16
|
</script>
|
|
24
17
|
|
|
25
18
|
<Chart geo={{ projection, fitGeojson }} {width} {height}>
|
|
@@ -78,7 +78,9 @@
|
|
|
78
78
|
const y0 = y ? ctx.yScale(y[0] ?? ctx.yDomain[0]) : ctx.yRange[0];
|
|
79
79
|
const y1 = y ? ctx.yScale(y[1] ?? ctx.yDomain[1]) : ctx.yRange[1];
|
|
80
80
|
|
|
81
|
-
const bandPadding = isScaleBand(ctx.xScale)
|
|
81
|
+
const bandPadding = isScaleBand(ctx.xScale)
|
|
82
|
+
? (ctx.xScale.padding() * ctx.xScale.step()) / 2
|
|
83
|
+
: 0;
|
|
82
84
|
const bandStep = isScaleBand(ctx.xScale) ? ctx.xScale.step() : 0;
|
|
83
85
|
|
|
84
86
|
return {
|
|
@@ -255,9 +255,7 @@
|
|
|
255
255
|
|
|
256
256
|
const endAngle = $derived(
|
|
257
257
|
endAngleProp ??
|
|
258
|
-
degreesToRadians(
|
|
259
|
-
(ctx.config.xRange ? max(ctx.config.xRange as number[]) : max(range))!
|
|
260
|
-
)
|
|
258
|
+
degreesToRadians((ctx.config.xRange ? max(ctx.config.xRange as number[]) : max(range))!)
|
|
261
259
|
);
|
|
262
260
|
|
|
263
261
|
const motionEndAngle = createMotion(initialValue, () => value, motion);
|
|
@@ -19,7 +19,7 @@ describe('ArcLabel', () => {
|
|
|
19
19
|
childComponents: [
|
|
20
20
|
{
|
|
21
21
|
component: ArcLabel,
|
|
22
|
-
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
|
|
22
|
+
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
|
|
23
23
|
centroid,
|
|
24
24
|
startAngle,
|
|
25
25
|
endAngle,
|
|
@@ -46,7 +46,7 @@ describe('ArcLabel', () => {
|
|
|
46
46
|
childComponents: [
|
|
47
47
|
{
|
|
48
48
|
component: ArcLabel,
|
|
49
|
-
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
|
|
49
|
+
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
|
|
50
50
|
centroid,
|
|
51
51
|
startAngle,
|
|
52
52
|
endAngle,
|
|
@@ -77,7 +77,7 @@ describe('ArcLabel', () => {
|
|
|
77
77
|
childComponents: [
|
|
78
78
|
{
|
|
79
79
|
component: ArcLabel,
|
|
80
|
-
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
|
|
80
|
+
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
|
|
81
81
|
centroid,
|
|
82
82
|
startAngle,
|
|
83
83
|
endAngle,
|
|
@@ -109,7 +109,7 @@ describe('ArcLabel', () => {
|
|
|
109
109
|
childComponents: [
|
|
110
110
|
{
|
|
111
111
|
component: ArcLabel,
|
|
112
|
-
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
|
|
112
|
+
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
|
|
113
113
|
centroid,
|
|
114
114
|
startAngle,
|
|
115
115
|
endAngle,
|
|
@@ -141,7 +141,7 @@ describe('ArcLabel', () => {
|
|
|
141
141
|
childComponents: [
|
|
142
142
|
{
|
|
143
143
|
component: ArcLabel,
|
|
144
|
-
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
|
|
144
|
+
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
|
|
145
145
|
centroid,
|
|
146
146
|
startAngle,
|
|
147
147
|
endAngle,
|
|
@@ -180,7 +180,7 @@ describe('ArcLabel', () => {
|
|
|
180
180
|
childComponents: [
|
|
181
181
|
{
|
|
182
182
|
component: ArcLabel,
|
|
183
|
-
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
|
|
183
|
+
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
|
|
184
184
|
centroid,
|
|
185
185
|
startAngle,
|
|
186
186
|
endAngle,
|
|
@@ -210,7 +210,7 @@ describe('ArcLabel', () => {
|
|
|
210
210
|
childComponents: [
|
|
211
211
|
{
|
|
212
212
|
component: ArcLabel,
|
|
213
|
-
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
|
|
213
|
+
props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
|
|
214
214
|
centroid,
|
|
215
215
|
startAngle,
|
|
216
216
|
endAngle,
|
|
@@ -264,13 +264,15 @@
|
|
|
264
264
|
|
|
265
265
|
// Auto-compute initial values for mount animation when motion is configured
|
|
266
266
|
const resolvedInitialY = $derived(
|
|
267
|
-
initialY ??
|
|
267
|
+
initialY ??
|
|
268
|
+
(motion && ctx.valueAxis === 'y' ? Math.max(ctx.yRange[0], ctx.yRange[1]) : undefined)
|
|
268
269
|
);
|
|
269
270
|
const resolvedInitialHeight = $derived(
|
|
270
271
|
initialHeight ?? (motion && ctx.valueAxis === 'y' ? 0 : undefined)
|
|
271
272
|
);
|
|
272
273
|
const resolvedInitialX = $derived(
|
|
273
|
-
initialX ??
|
|
274
|
+
initialX ??
|
|
275
|
+
(motion && ctx.valueAxis === 'x' ? Math.min(ctx.xRange[0], ctx.xRange[1]) : undefined)
|
|
274
276
|
);
|
|
275
277
|
const resolvedInitialWidth = $derived(
|
|
276
278
|
initialWidth ?? (motion && ctx.valueAxis === 'x' ? 0 : undefined)
|
|
@@ -134,21 +134,13 @@
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
// Resolve final statistics: explicit props take priority over computed values
|
|
137
|
-
const minVal = $derived(
|
|
138
|
-
|
|
139
|
-
);
|
|
140
|
-
const q1Val = $derived(
|
|
141
|
-
q1Prop != null ? accessor(q1Prop)(data) : computedStats?.q1
|
|
142
|
-
);
|
|
137
|
+
const minVal = $derived(minProp != null ? accessor(minProp)(data) : computedStats?.min);
|
|
138
|
+
const q1Val = $derived(q1Prop != null ? accessor(q1Prop)(data) : computedStats?.q1);
|
|
143
139
|
const medianVal = $derived(
|
|
144
140
|
medianProp != null ? accessor(medianProp)(data) : computedStats?.median
|
|
145
141
|
);
|
|
146
|
-
const q3Val = $derived(
|
|
147
|
-
|
|
148
|
-
);
|
|
149
|
-
const maxVal = $derived(
|
|
150
|
-
maxProp != null ? accessor(maxProp)(data) : computedStats?.max
|
|
151
|
-
);
|
|
142
|
+
const q3Val = $derived(q3Prop != null ? accessor(q3Prop)(data) : computedStats?.q3);
|
|
143
|
+
const maxVal = $derived(maxProp != null ? accessor(maxProp)(data) : computedStats?.max);
|
|
152
144
|
const outliersVal = $derived<number[]>(
|
|
153
145
|
outliersProp != null ? (accessor(outliersProp)(data) ?? []) : (computedStats?.outliers ?? [])
|
|
154
146
|
);
|
|
@@ -4,7 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
type BaseRectCellProps = Omit<
|
|
6
6
|
RectProps,
|
|
7
|
-
|
|
7
|
+
| 'width'
|
|
8
|
+
| 'height'
|
|
9
|
+
| 'x0'
|
|
10
|
+
| 'x1'
|
|
11
|
+
| 'y0'
|
|
12
|
+
| 'y1'
|
|
13
|
+
| 'initialX'
|
|
14
|
+
| 'initialY'
|
|
15
|
+
| 'initialWidth'
|
|
16
|
+
| 'initialHeight'
|
|
17
|
+
| 'motion'
|
|
18
|
+
| 'ref'
|
|
8
19
|
>;
|
|
9
20
|
|
|
10
21
|
export type CellProps = BaseRectCellProps & {
|
|
@@ -36,13 +47,7 @@
|
|
|
36
47
|
import Circle from './Circle.svelte';
|
|
37
48
|
import Group from './Group.svelte';
|
|
38
49
|
|
|
39
|
-
let {
|
|
40
|
-
shape = 'rect',
|
|
41
|
-
r,
|
|
42
|
-
x,
|
|
43
|
-
y,
|
|
44
|
-
...restProps
|
|
45
|
-
}: CellProps = $props();
|
|
50
|
+
let { shape = 'rect', r, x, y, ...restProps }: CellProps = $props();
|
|
46
51
|
|
|
47
52
|
const chartCtx = getChartContext();
|
|
48
53
|
const cellWidth = $derived(isScaleBand(chartCtx.xScale) ? chartCtx.xScale.bandwidth() : 0);
|
|
@@ -22,7 +22,11 @@
|
|
|
22
22
|
import { geoFitObjectTransform } from '../utils/geo.js';
|
|
23
23
|
import TransformContext from './TransformContext.svelte';
|
|
24
24
|
import BrushContext from './BrushContext.svelte';
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
type BrushDomainType,
|
|
27
|
+
type BrushState,
|
|
28
|
+
expandBandBrushDomain,
|
|
29
|
+
} from '../states/brush.svelte.js';
|
|
26
30
|
|
|
27
31
|
import { setChartContext } from '../contexts/chart.js';
|
|
28
32
|
import { ChartState } from '../states/chart.svelte.js';
|
|
@@ -741,7 +745,8 @@
|
|
|
741
745
|
|
|
742
746
|
// Resolve which projection properties the transform state applies to
|
|
743
747
|
const resolvedApply = $derived.by(() => {
|
|
744
|
-
if (transform?.mode !== 'projection')
|
|
748
|
+
if (transform?.mode !== 'projection')
|
|
749
|
+
return { rotation: false, scale: false, translate: false };
|
|
745
750
|
|
|
746
751
|
// Auto-detect globe projections from clipAngle (flat projections return 0, globes return > 0)
|
|
747
752
|
let isGlobe = false;
|
|
@@ -773,7 +778,10 @@
|
|
|
773
778
|
});
|
|
774
779
|
|
|
775
780
|
const initialTransform = $derived(
|
|
776
|
-
transform?.mode === 'projection' &&
|
|
781
|
+
transform?.mode === 'projection' &&
|
|
782
|
+
(resolvedApply.translate || resolvedApply.scale) &&
|
|
783
|
+
geo?.fitGeojson &&
|
|
784
|
+
geo?.projection
|
|
777
785
|
? geoFitObjectTransform(
|
|
778
786
|
geo.projection(),
|
|
779
787
|
[chartState.width, chartState.height],
|
|
@@ -827,7 +835,9 @@
|
|
|
827
835
|
axisScale: number,
|
|
828
836
|
dimension: number,
|
|
829
837
|
baseDomain: number[],
|
|
830
|
-
extent:
|
|
838
|
+
extent:
|
|
839
|
+
| { min?: number | Date | 'data'; max?: number | Date | 'data'; minRange?: number }
|
|
840
|
+
| undefined
|
|
831
841
|
): number => {
|
|
832
842
|
if (!extent || baseDomain.length < 2 || dimension <= 0) return axisTranslate;
|
|
833
843
|
|
|
@@ -844,13 +854,17 @@
|
|
|
844
854
|
|
|
845
855
|
// Normalize reversed domains by flipping the translate
|
|
846
856
|
const reversed = rawD0 > rawD1;
|
|
847
|
-
const normTranslate = reversed
|
|
857
|
+
const normTranslate = reversed
|
|
858
|
+
? dimension * axisScale - axisTranslate - dimension
|
|
859
|
+
: axisTranslate;
|
|
848
860
|
const numMin = Math.min(rawD0, rawD1);
|
|
849
861
|
|
|
850
862
|
const rawMinVal = resolveValue(extent.min, baseDomain[0]);
|
|
851
863
|
const rawMaxVal = resolveValue(extent.max, baseDomain[1]);
|
|
852
|
-
const minVal =
|
|
853
|
-
|
|
864
|
+
const minVal =
|
|
865
|
+
rawMinVal != null && rawMaxVal != null ? Math.min(rawMinVal, rawMaxVal) : rawMinVal;
|
|
866
|
+
const maxVal =
|
|
867
|
+
rawMinVal != null && rawMaxVal != null ? Math.max(rawMinVal, rawMaxVal) : rawMaxVal;
|
|
854
868
|
|
|
855
869
|
// Current visible domain from translate/scale
|
|
856
870
|
const f0 = -normTranslate / axisScale / dimension;
|
|
@@ -869,11 +883,15 @@
|
|
|
869
883
|
// Enforce domain min/max (pan boundaries)
|
|
870
884
|
if (minVal != null && visMin < minVal) {
|
|
871
885
|
visMin = minVal;
|
|
872
|
-
visMax =
|
|
886
|
+
visMax =
|
|
887
|
+
visMin +
|
|
888
|
+
(extent.minRange != null && visRange < extent.minRange ? extent.minRange : visRange);
|
|
873
889
|
}
|
|
874
890
|
if (maxVal != null && visMax > maxVal) {
|
|
875
891
|
visMax = maxVal;
|
|
876
|
-
visMin =
|
|
892
|
+
visMin =
|
|
893
|
+
visMax -
|
|
894
|
+
(extent.minRange != null && visRange < extent.minRange ? extent.minRange : visRange);
|
|
877
895
|
if (minVal != null && visMin < minVal) visMin = minVal;
|
|
878
896
|
}
|
|
879
897
|
|
|
@@ -930,10 +948,9 @@
|
|
|
930
948
|
|
|
931
949
|
// Whether this is a band scale domain transform (affects scaleExtent and constrain defaults)
|
|
932
950
|
const isBandDomainTransform = $derived(
|
|
933
|
-
transform?.mode === 'domain' &&
|
|
934
|
-
((transform.axis ?? 'both') !== 'y' && isScaleBand(chartState._xScaleProp)) ||
|
|
935
|
-
|
|
936
|
-
)
|
|
951
|
+
transform?.mode === 'domain' &&
|
|
952
|
+
(((transform.axis ?? 'both') !== 'y' && isScaleBand(chartState._xScaleProp)) ||
|
|
953
|
+
((transform.axis ?? 'both') !== 'x' && isScaleBand(chartState._yScaleProp)))
|
|
937
954
|
);
|
|
938
955
|
|
|
939
956
|
// For projection mode, scaleExtent is relative to the initial fitted scale (like d3-zoom).
|
|
@@ -942,10 +959,10 @@
|
|
|
942
959
|
const resolvedScaleExtent = $derived.by(() => {
|
|
943
960
|
if (transform?.mode === 'projection' && transform?.scaleExtent && initialTransform) {
|
|
944
961
|
const baseScale = initialTransform.scale;
|
|
945
|
-
return [
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
]
|
|
962
|
+
return [transform.scaleExtent[0] * baseScale, transform.scaleExtent[1] * baseScale] as [
|
|
963
|
+
number,
|
|
964
|
+
number,
|
|
965
|
+
];
|
|
949
966
|
}
|
|
950
967
|
if (!isBandDomainTransform) return transform?.scaleExtent;
|
|
951
968
|
const userExtent = transform?.scaleExtent;
|
|
@@ -972,7 +989,12 @@
|
|
|
972
989
|
// The viewport (at current zoom) must overlap with the translateExtent world bounds.
|
|
973
990
|
// As zoom increases, the allowed translate range grows proportionally.
|
|
974
991
|
const projectionTranslateConstrain = $derived.by(() => {
|
|
975
|
-
if (
|
|
992
|
+
if (
|
|
993
|
+
transform?.mode !== 'projection' ||
|
|
994
|
+
!transform?.translateExtent ||
|
|
995
|
+
!initialTransform ||
|
|
996
|
+
resolvedApply.rotation
|
|
997
|
+
) {
|
|
976
998
|
return undefined;
|
|
977
999
|
}
|
|
978
1000
|
|
|
@@ -998,10 +1020,8 @@
|
|
|
998
1020
|
// Default constrain for band scale domain transforms: prevent panning past data boundaries
|
|
999
1021
|
const bandScaleConstrain = $derived.by(() => {
|
|
1000
1022
|
if (!isBandDomainTransform) return undefined;
|
|
1001
|
-
const xIsBand =
|
|
1002
|
-
|
|
1003
|
-
const yIsBand =
|
|
1004
|
-
(transform!.axis ?? 'both') !== 'x' && isScaleBand(chartState._yScaleProp);
|
|
1023
|
+
const xIsBand = (transform!.axis ?? 'both') !== 'y' && isScaleBand(chartState._xScaleProp);
|
|
1024
|
+
const yIsBand = (transform!.axis ?? 'both') !== 'x' && isScaleBand(chartState._yScaleProp);
|
|
1005
1025
|
|
|
1006
1026
|
return (t: { scale: number; translate: { x: number; y: number } }) => {
|
|
1007
1027
|
let { scale, translate } = t;
|
|
@@ -1021,7 +1041,17 @@
|
|
|
1021
1041
|
// Compose user-provided constrain with domainExtent constrain and band scale constrain
|
|
1022
1042
|
const composedConstrain = $derived.by(() => {
|
|
1023
1043
|
const userConstrain = transform?.constrain;
|
|
1024
|
-
const constrains = [
|
|
1044
|
+
const constrains = [
|
|
1045
|
+
bandScaleConstrain,
|
|
1046
|
+
domainExtentConstrain,
|
|
1047
|
+
projectionTranslateConstrain,
|
|
1048
|
+
userConstrain,
|
|
1049
|
+
].filter(Boolean) as Array<
|
|
1050
|
+
(t: { scale: number; translate: { x: number; y: number } }) => {
|
|
1051
|
+
scale: number;
|
|
1052
|
+
translate: { x: number; y: number };
|
|
1053
|
+
}
|
|
1054
|
+
>;
|
|
1025
1055
|
if (constrains.length === 0) return undefined;
|
|
1026
1056
|
if (constrains.length === 1) return constrains[0];
|
|
1027
1057
|
return (t: { scale: number; translate: { x: number; y: number } }) => {
|
|
@@ -1089,7 +1119,14 @@
|
|
|
1089
1119
|
>
|
|
1090
1120
|
{#key chartState.isMounted}
|
|
1091
1121
|
<!-- svelte-ignore ownership_invalid_binding -->
|
|
1092
|
-
{@const {
|
|
1122
|
+
{@const {
|
|
1123
|
+
domainExtent: _de,
|
|
1124
|
+
constrain: _uc,
|
|
1125
|
+
apply: _apply,
|
|
1126
|
+
scaleExtent: _se,
|
|
1127
|
+
translateExtent: _te,
|
|
1128
|
+
...transformProps
|
|
1129
|
+
} = transform ?? {}}
|
|
1093
1130
|
<TransformContext
|
|
1094
1131
|
bind:state={chartState.transformState}
|
|
1095
1132
|
mode={transform?.mode ?? 'none'}
|
|
@@ -1100,7 +1137,9 @@
|
|
|
1100
1137
|
scaleExtent={resolvedScaleExtent}
|
|
1101
1138
|
translateExtent={resolvedTranslateExtent}
|
|
1102
1139
|
constrain={composedConstrain}
|
|
1103
|
-
disablePointer={
|
|
1140
|
+
disablePointer={brush === true ||
|
|
1141
|
+
(typeof brush === 'object' && !brush.disabled) ||
|
|
1142
|
+
transform?.disablePointer}
|
|
1104
1143
|
{ondragstart}
|
|
1105
1144
|
{onTransform}
|
|
1106
1145
|
{ondragend}
|
|
@@ -1108,7 +1147,11 @@
|
|
|
1108
1147
|
<!-- svelte-ignore ownership_invalid_binding -->
|
|
1109
1148
|
<BrushContext {...enhancedBrushProps} bind:state={chartState.brushState}>
|
|
1110
1149
|
<!-- svelte-ignore ownership_invalid_binding -->
|
|
1111
|
-
<TooltipContext
|
|
1150
|
+
<TooltipContext
|
|
1151
|
+
onclick={onTooltipClick}
|
|
1152
|
+
{...getObjectOrNull(tooltipContext)}
|
|
1153
|
+
bind:state={chartState.tooltipState}
|
|
1154
|
+
>
|
|
1112
1155
|
<ChartChildren {children} {tooltipContext} {...restProps} />
|
|
1113
1156
|
</TooltipContext>
|
|
1114
1157
|
</BrushContext>
|
|
@@ -195,7 +195,12 @@
|
|
|
195
195
|
{#if typeof grid === 'function'}
|
|
196
196
|
{@render grid(snippetProps)}
|
|
197
197
|
{:else if grid}
|
|
198
|
-
<Grid
|
|
198
|
+
<Grid
|
|
199
|
+
x={context.valueAxis === 'x' || context.radial}
|
|
200
|
+
y={context.valueAxis === 'y' || context.radial}
|
|
201
|
+
{...getObjectOrNull(grid)}
|
|
202
|
+
{...props.grid}
|
|
203
|
+
/>
|
|
199
204
|
{/if}
|
|
200
205
|
|
|
201
206
|
<ChartClipPath disabled={!context.props.brush && context.transformState?.mode !== 'domain'}>
|
|
@@ -212,7 +217,12 @@
|
|
|
212
217
|
{#if typeof rule === 'function'}
|
|
213
218
|
{@render rule(snippetProps)}
|
|
214
219
|
{:else if rule}
|
|
215
|
-
<Rule
|
|
220
|
+
<Rule
|
|
221
|
+
x={context.valueAxis === 'x' ? 0 : false}
|
|
222
|
+
y={context.valueAxis === 'y' ? 0 : false}
|
|
223
|
+
{...getObjectOrNull(rule)}
|
|
224
|
+
{...props.rule}
|
|
225
|
+
/>
|
|
216
226
|
{/if}
|
|
217
227
|
{:else if axis}
|
|
218
228
|
{#if axis !== 'x'}
|
|
@@ -236,12 +246,20 @@
|
|
|
236
246
|
{#if typeof rule === 'function'}
|
|
237
247
|
{@render rule(snippetProps)}
|
|
238
248
|
{:else if rule}
|
|
239
|
-
<Rule
|
|
249
|
+
<Rule
|
|
250
|
+
x={context.valueAxis === 'x' ? 0 : false}
|
|
251
|
+
y={context.valueAxis === 'y' ? 0 : false}
|
|
252
|
+
{...getObjectOrNull(rule)}
|
|
253
|
+
{...props.rule}
|
|
254
|
+
/>
|
|
240
255
|
{/if}
|
|
241
256
|
{/if}
|
|
242
257
|
|
|
243
258
|
<!-- Use `full` to allow labels on edge to not be cropped (bleed into padding) -->
|
|
244
|
-
<ChartClipPath
|
|
259
|
+
<ChartClipPath
|
|
260
|
+
disabled={!context.props.brush && context.transformState?.mode !== 'domain'}
|
|
261
|
+
full
|
|
262
|
+
>
|
|
245
263
|
{#if typeof points === 'function'}
|
|
246
264
|
{@render points(snippetProps)}
|
|
247
265
|
{:else if points}
|
|
@@ -227,6 +227,15 @@
|
|
|
227
227
|
const staticStrokeWidth = $derived(typeof strokeWidth === 'number' ? strokeWidth : undefined);
|
|
228
228
|
const staticOpacity = $derived(typeof opacity === 'number' ? opacity : undefined);
|
|
229
229
|
const staticClassName = $derived(typeof className === 'string' ? className : undefined);
|
|
230
|
+
// Match SVG's implicit `stroke-width: 1` default: if `stroke` is set but
|
|
231
|
+
// `strokeWidth` is not, render a 1px border so HTML matches SVG/Canvas layers.
|
|
232
|
+
const staticBorderWidth = $derived(
|
|
233
|
+
typeof strokeWidth === 'number'
|
|
234
|
+
? `${strokeWidth}px`
|
|
235
|
+
: typeof stroke === 'string'
|
|
236
|
+
? '1px'
|
|
237
|
+
: undefined
|
|
238
|
+
);
|
|
230
239
|
|
|
231
240
|
// Style options (shared between pixel and data mode)
|
|
232
241
|
function getStyleOptions(
|
|
@@ -262,12 +271,13 @@
|
|
|
262
271
|
'lc-circle',
|
|
263
272
|
itemClass ?? (typeof className === 'string' ? className : undefined)
|
|
264
273
|
),
|
|
265
|
-
style:
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
274
|
+
style:
|
|
275
|
+
[
|
|
276
|
+
restProps.style as string | undefined,
|
|
277
|
+
dashArrayAttr ? `stroke-dasharray: ${dashArrayAttr}` : undefined,
|
|
278
|
+
]
|
|
279
|
+
.filter(Boolean)
|
|
280
|
+
.join('; ') || undefined,
|
|
271
281
|
};
|
|
272
282
|
}
|
|
273
283
|
|
|
@@ -400,6 +410,12 @@
|
|
|
400
410
|
{@const resolvedStrokeWidth = resolveStyleProp(strokeWidth, item.d)}
|
|
401
411
|
{@const resolvedOpacity = resolveStyleProp(opacity, item.d)}
|
|
402
412
|
{@const resolvedClass = resolveStyleProp(className, item.d)}
|
|
413
|
+
{@const resolvedBorderWidth =
|
|
414
|
+
resolvedStrokeWidth != null
|
|
415
|
+
? `${resolvedStrokeWidth}px`
|
|
416
|
+
: resolvedStroke != null
|
|
417
|
+
? '1px'
|
|
418
|
+
: undefined}
|
|
403
419
|
<div
|
|
404
420
|
style:position="absolute"
|
|
405
421
|
style:left="{item.cx}px"
|
|
@@ -407,9 +423,10 @@
|
|
|
407
423
|
style:width="{item.r * 2}px"
|
|
408
424
|
style:height="{item.r * 2}px"
|
|
409
425
|
style:border-radius="50%"
|
|
410
|
-
style:background
|
|
426
|
+
style:background={resolvedFill}
|
|
427
|
+
style:background-origin="border-box"
|
|
411
428
|
style:opacity={resolvedOpacity}
|
|
412
|
-
style:border-width={
|
|
429
|
+
style:border-width={resolvedBorderWidth}
|
|
413
430
|
style:border-color={resolvedStroke}
|
|
414
431
|
style:border-style={dashArrayResolved ? 'dashed' : 'solid'}
|
|
415
432
|
style:transform="translate(-50%, -50%)"
|
|
@@ -425,9 +442,10 @@
|
|
|
425
442
|
style:width="{motionR.current * 2}px"
|
|
426
443
|
style:height="{motionR.current * 2}px"
|
|
427
444
|
style:border-radius="50%"
|
|
428
|
-
style:background
|
|
445
|
+
style:background={staticFill}
|
|
446
|
+
style:background-origin="border-box"
|
|
429
447
|
style:opacity={staticOpacity}
|
|
430
|
-
style:border-width={
|
|
448
|
+
style:border-width={staticBorderWidth}
|
|
431
449
|
style:border-color={staticStroke}
|
|
432
450
|
style:border-style={dashArrayResolved ? 'dashed' : 'solid'}
|
|
433
451
|
style:transform="translate(-50%, -50%)"
|
|
@@ -455,8 +473,12 @@
|
|
|
455
473
|
}
|
|
456
474
|
|
|
457
475
|
/* Html layers */
|
|
458
|
-
:global(:where(.lc-layout-html .lc-circle)
|
|
459
|
-
|
|
476
|
+
:global(:where(.lc-layout-html .lc-circle)) {
|
|
477
|
+
/* Match SVG sizing (visual extent equals `r * 2`, border on outer edge) */
|
|
478
|
+
box-sizing: border-box;
|
|
479
|
+
}
|
|
480
|
+
:global(:where(.lc-layout-html .lc-circle):not([background])) {
|
|
481
|
+
background: var(--fill-color);
|
|
460
482
|
}
|
|
461
483
|
:global(:where(.lc-layout-html .lc-circle):not([border-color])) {
|
|
462
484
|
border-color: var(--stroke-color);
|
|
@@ -86,14 +86,10 @@
|
|
|
86
86
|
|
|
87
87
|
// Outer rect covering the chart bounds — combined with the clip shape under
|
|
88
88
|
// the even-odd fill rule to invert the clip.
|
|
89
|
-
const outerRect = $derived(
|
|
90
|
-
`M0,0 H${chartCtx.width} V${chartCtx.height} H0 Z`
|
|
91
|
-
);
|
|
89
|
+
const outerRect = $derived(`M0,0 H${chartCtx.width} V${chartCtx.height} H0 Z`);
|
|
92
90
|
|
|
93
91
|
// Effective path used for canvas + html layers when inverting.
|
|
94
|
-
const effectivePath = $derived(
|
|
95
|
-
invert && path ? `${outerRect} ${path}` : path
|
|
96
|
-
);
|
|
92
|
+
const effectivePath = $derived(invert && path ? `${outerRect} ${path}` : path);
|
|
97
93
|
|
|
98
94
|
// Cache the Path2D so `ctx.clip()` gets a stable reference per `path` change.
|
|
99
95
|
const canvasPath = $derived(
|
|
@@ -144,9 +140,7 @@
|
|
|
144
140
|
class="lc-clip-path-div"
|
|
145
141
|
style:position="absolute"
|
|
146
142
|
style:inset="0"
|
|
147
|
-
style:clip-path={invert
|
|
148
|
-
? `path(evenodd, "${effectivePath}")`
|
|
149
|
-
: `path("${effectivePath}")`}
|
|
143
|
+
style:clip-path={invert ? `path(evenodd, "${effectivePath}")` : `path("${effectivePath}")`}
|
|
150
144
|
>
|
|
151
145
|
{@render children({ id, url, useId })}
|
|
152
146
|
</div>
|
|
@@ -310,6 +310,15 @@
|
|
|
310
310
|
const staticStrokeWidth = $derived(typeof strokeWidth === 'number' ? strokeWidth : undefined);
|
|
311
311
|
const staticOpacity = $derived(typeof opacity === 'number' ? opacity : undefined);
|
|
312
312
|
const staticClassName = $derived(typeof className === 'string' ? className : undefined);
|
|
313
|
+
// Match SVG's implicit `stroke-width: 1` default: if `stroke` is set but
|
|
314
|
+
// `strokeWidth` is not, render a 1px border so HTML matches SVG/Canvas layers.
|
|
315
|
+
const staticBorderWidth = $derived(
|
|
316
|
+
typeof strokeWidth === 'number'
|
|
317
|
+
? `${strokeWidth}px`
|
|
318
|
+
: typeof stroke === 'string'
|
|
319
|
+
? '1px'
|
|
320
|
+
: undefined
|
|
321
|
+
);
|
|
313
322
|
|
|
314
323
|
chartCtx.registerComponent({
|
|
315
324
|
name: 'Ellipse',
|
|
@@ -401,6 +410,12 @@
|
|
|
401
410
|
{@const resolvedStrokeWidth = resolveStyleProp(strokeWidth, item.d)}
|
|
402
411
|
{@const resolvedOpacity = resolveStyleProp(opacity, item.d)}
|
|
403
412
|
{@const resolvedClass = resolveStyleProp(className, item.d)}
|
|
413
|
+
{@const resolvedBorderWidth =
|
|
414
|
+
resolvedStrokeWidth != null
|
|
415
|
+
? `${resolvedStrokeWidth}px`
|
|
416
|
+
: resolvedStroke != null
|
|
417
|
+
? '1px'
|
|
418
|
+
: undefined}
|
|
404
419
|
<div
|
|
405
420
|
style:position="absolute"
|
|
406
421
|
style:left="{item.cx}px"
|
|
@@ -408,9 +423,10 @@
|
|
|
408
423
|
style:width="{item.rx * 2}px"
|
|
409
424
|
style:height="{item.ry * 2}px"
|
|
410
425
|
style:border-radius="50%"
|
|
411
|
-
style:background
|
|
426
|
+
style:background={resolvedFill}
|
|
427
|
+
style:background-origin="border-box"
|
|
412
428
|
style:opacity={resolvedOpacity}
|
|
413
|
-
style:border-width={
|
|
429
|
+
style:border-width={resolvedBorderWidth}
|
|
414
430
|
style:border-color={resolvedStroke}
|
|
415
431
|
style:border-style="solid"
|
|
416
432
|
style:transform="translate(-50%, -50%)"
|
|
@@ -426,9 +442,10 @@
|
|
|
426
442
|
style:width="{motionRx.current * 2}px"
|
|
427
443
|
style:height="{motionRy.current * 2}px"
|
|
428
444
|
style:border-radius="50%"
|
|
429
|
-
style:background
|
|
445
|
+
style:background={staticFill}
|
|
446
|
+
style:background-origin="border-box"
|
|
430
447
|
style:opacity={staticOpacity}
|
|
431
|
-
style:border-width={
|
|
448
|
+
style:border-width={staticBorderWidth}
|
|
432
449
|
style:border-color={staticStroke}
|
|
433
450
|
style:border-style="solid"
|
|
434
451
|
style:transform="translate(-50%, -50%)"
|
|
@@ -454,8 +471,12 @@
|
|
|
454
471
|
}
|
|
455
472
|
|
|
456
473
|
/* Html layers */
|
|
457
|
-
:global(:where(.lc-layout-html .lc-ellipse)
|
|
458
|
-
|
|
474
|
+
:global(:where(.lc-layout-html .lc-ellipse)) {
|
|
475
|
+
/* Match SVG sizing (visual extent equals `rx * 2`×`ry * 2`, border on outer edge) */
|
|
476
|
+
box-sizing: border-box;
|
|
477
|
+
}
|
|
478
|
+
:global(:where(.lc-layout-html .lc-ellipse):not([background])) {
|
|
479
|
+
background: var(--fill-color);
|
|
459
480
|
}
|
|
460
481
|
:global(:where(.lc-layout-html .lc-ellipse):not([border-color])) {
|
|
461
482
|
border-color: var(--stroke-color);
|
|
@@ -258,9 +258,7 @@
|
|
|
258
258
|
const width = $derived(Math.ceil(barWidth) + padding * 2);
|
|
259
259
|
const svgHeight = $derived(titleHeight + height + tickLabelHeight + padding * 2 + 3);
|
|
260
260
|
const barY = $derived(
|
|
261
|
-
labelPlacement === 'top'
|
|
262
|
-
? titleHeight + padding + tickLabelHeight
|
|
263
|
-
: titleHeight + padding
|
|
261
|
+
labelPlacement === 'top' ? titleHeight + padding + tickLabelHeight : titleHeight + padding
|
|
264
262
|
);
|
|
265
263
|
const tickLabelY = $derived(
|
|
266
264
|
labelPlacement === 'top'
|