layerchart 2.0.0-next.54 → 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/Axis.svelte +10 -2
- package/dist/components/Axis.svelte.d.ts +8 -2
- package/dist/components/Bar.svelte +14 -40
- 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 +51 -9
- package/dist/components/Circle.svelte.d.ts +6 -0
- package/dist/components/CircleClipPath.svelte +13 -31
- package/dist/components/CircleClipPath.svelte.d.ts +7 -1
- package/dist/components/ClipPath.svelte +58 -21
- package/dist/components/ClipPath.svelte.d.ts +21 -12
- package/dist/components/Connector.svelte +18 -0
- package/dist/components/Connector.svelte.d.ts +5 -0
- package/dist/components/Ellipse.svelte +27 -6
- package/dist/components/GeoClipPath.svelte +14 -17
- package/dist/components/GeoClipPath.svelte.d.ts +6 -0
- 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/Grid.svelte +15 -4
- package/dist/components/Grid.svelte.d.ts +14 -4
- package/dist/components/Group.svelte +11 -5
- package/dist/components/Highlight.svelte +4 -3
- package/dist/components/Image.svelte +42 -30
- package/dist/components/Labels.svelte +2 -4
- package/dist/components/Line.svelte +31 -3
- package/dist/components/Line.svelte.d.ts +7 -0
- package/dist/components/LinearGradient.svelte +8 -4
- package/dist/components/Link.svelte +8 -0
- 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 +117 -9
- package/dist/components/Rect.svelte.d.ts +13 -1
- package/dist/components/RectClipPath.svelte +11 -15
- package/dist/components/RectClipPath.svelte.d.ts +6 -0
- package/dist/components/Spline.svelte +22 -4
- package/dist/components/Text.svelte +16 -5
- package/dist/components/Trail.svelte +19 -7
- package/dist/components/Tree.svelte +7 -3
- 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/__screenshots__/canvas.svelte.test.ts/renderPathData-composes-element-opacity-with-inherited-globalAlpha--Group-opacity--1.png +0 -0
- package/dist/utils/__screenshots__/canvas.svelte.test.ts/renderPathData-composes-element-opacity-with-inherited-globalAlpha--Group-opacity--2.png +0 -0
- package/dist/utils/canvas.d.ts +2 -0
- package/dist/utils/canvas.js +20 -11
- package/dist/utils/canvas.svelte.test.js +55 -0
- package/dist/utils/connectorUtils.d.ts +13 -0
- package/dist/utils/connectorUtils.js +120 -1
- package/dist/utils/path.d.ts +19 -0
- package/dist/utils/path.js +72 -0
- package/dist/utils/rect.svelte.d.ts +18 -0
- package/dist/utils/rect.svelte.js +33 -0
- package/dist/utils/trail.js +3 -4
- package/package.json +1 -1
|
@@ -95,7 +95,9 @@
|
|
|
95
95
|
background?: string;
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
* Render as a child of the pattern
|
|
98
|
+
* Render as a child of the pattern.
|
|
99
|
+
*
|
|
100
|
+
* Note: only supported on the `<Svg>` layer.
|
|
99
101
|
*/
|
|
100
102
|
patternContent?: Snippet;
|
|
101
103
|
|
|
@@ -250,10 +252,102 @@
|
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
if (layerCtx === 'canvas') {
|
|
253
|
-
chartCtx.registerComponent({
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
chartCtx.registerComponent({
|
|
256
|
+
name: 'Pattern',
|
|
257
|
+
kind: 'group',
|
|
258
|
+
canvasRender: {
|
|
259
|
+
render,
|
|
260
|
+
deps: () => [width, height, shapes, background],
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function withOpacity(color: string, opacity: number) {
|
|
266
|
+
return opacity === 1 ? color : `color-mix(in srgb, ${color} ${opacity * 100}%, transparent)`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Build a CSS `background` value from lines/circles/background.
|
|
270
|
+
// Uses repeating-linear-gradient for lines and radial-gradient for circles
|
|
271
|
+
// so CSS variables/classes resolve natively in the HTML layer.
|
|
272
|
+
function createCSSPattern(): string {
|
|
273
|
+
const layers: string[] = [];
|
|
274
|
+
|
|
275
|
+
if (linesProp) {
|
|
276
|
+
const lineDefs = Array.isArray(linesProp)
|
|
277
|
+
? linesProp
|
|
278
|
+
: linesProp === true
|
|
279
|
+
? [{}]
|
|
280
|
+
: [linesProp];
|
|
281
|
+
for (const line of lineDefs) {
|
|
282
|
+
const color = withOpacity(
|
|
283
|
+
line.color ?? 'var(--color-surface-content, currentColor)',
|
|
284
|
+
line.opacity ?? 1
|
|
285
|
+
);
|
|
286
|
+
const sw = line.width ?? 1;
|
|
287
|
+
|
|
288
|
+
let rotate = Math.round(line.rotate ?? 0) % 360;
|
|
289
|
+
if (rotate > 180) rotate = rotate - 360;
|
|
290
|
+
else if (rotate > 90) rotate = rotate - 180;
|
|
291
|
+
else if (rotate < -180) rotate = rotate + 360;
|
|
292
|
+
else if (rotate < -90) rotate = rotate + 180;
|
|
293
|
+
|
|
294
|
+
let angle: number;
|
|
295
|
+
let period: number;
|
|
296
|
+
if (rotate === 0) {
|
|
297
|
+
angle = 0;
|
|
298
|
+
period = height;
|
|
299
|
+
} else if (rotate === 90) {
|
|
300
|
+
angle = 90;
|
|
301
|
+
period = width;
|
|
302
|
+
} else if (rotate > 0) {
|
|
303
|
+
angle = 45;
|
|
304
|
+
period = (width * height) / Math.sqrt(width * width + height * height);
|
|
305
|
+
} else {
|
|
306
|
+
angle = 135;
|
|
307
|
+
period = (width * height) / Math.sqrt(width * width + height * height);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
layers.push(
|
|
311
|
+
`repeating-linear-gradient(${angle}deg, ${color} 0 ${sw}px, transparent ${sw}px ${period}px)`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (circlesProp) {
|
|
317
|
+
const circleDefs = Array.isArray(circlesProp)
|
|
318
|
+
? circlesProp
|
|
319
|
+
: circlesProp === true
|
|
320
|
+
? [{}]
|
|
321
|
+
: [circlesProp];
|
|
322
|
+
for (const circle of circleDefs) {
|
|
323
|
+
const color = withOpacity(
|
|
324
|
+
circle.color ?? 'var(--color-surface-content, currentColor)',
|
|
325
|
+
circle.opacity ?? 1
|
|
326
|
+
);
|
|
327
|
+
const r = circle.radius ?? 1;
|
|
328
|
+
|
|
329
|
+
if (circle.stagger) {
|
|
330
|
+
layers.push(
|
|
331
|
+
`radial-gradient(circle at 25% 25%, ${color} ${r}px, transparent ${r}px) 0 0 / ${size}px ${size}px`,
|
|
332
|
+
`radial-gradient(circle at 75% 75%, ${color} ${r}px, transparent ${r}px) 0 0 / ${size}px ${size}px`
|
|
333
|
+
);
|
|
334
|
+
} else {
|
|
335
|
+
layers.push(
|
|
336
|
+
`radial-gradient(circle at center, ${color} ${r}px, transparent ${r}px) 0 0 / ${size}px ${size}px`
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// `background` may be either a plain color or an image value (e.g. `linear-gradient(...)`
|
|
343
|
+
// when used as `<Pattern background={gradient}>`). Images go in the image-layer list
|
|
344
|
+
// tiled at the pattern size (after shape layers so they paint underneath);
|
|
345
|
+
// colors go in the background-color slot.
|
|
346
|
+
const isImage = background != null && /gradient\(|url\(/i.test(background);
|
|
347
|
+
if (isImage) layers.push(`${background} 0 0 / ${width}px ${height}px`);
|
|
348
|
+
|
|
349
|
+
if (layers.length === 0) return background ?? 'transparent';
|
|
350
|
+
return !isImage && background ? `${layers.join(', ')}, ${background}` : layers.join(', ');
|
|
257
351
|
}
|
|
258
352
|
</script>
|
|
259
353
|
|
|
@@ -299,4 +393,6 @@
|
|
|
299
393
|
</defs>
|
|
300
394
|
|
|
301
395
|
{@render children?.({ id, pattern: `url(#${id})` })}
|
|
396
|
+
{:else if layerCtx === 'html'}
|
|
397
|
+
{@render children?.({ id, pattern: createCSSPattern() })}
|
|
302
398
|
{/if}
|
|
@@ -75,7 +75,9 @@ export type PatternPropsWithoutHTML = {
|
|
|
75
75
|
*/
|
|
76
76
|
background?: string;
|
|
77
77
|
/**
|
|
78
|
-
* Render as a child of the pattern
|
|
78
|
+
* Render as a child of the pattern.
|
|
79
|
+
*
|
|
80
|
+
* Note: only supported on the `<Svg>` layer.
|
|
79
81
|
*/
|
|
80
82
|
patternContent?: Snippet;
|
|
81
83
|
children?: Snippet<[{
|
|
@@ -109,9 +109,7 @@
|
|
|
109
109
|
|
|
110
110
|
const endAngle = $derived(
|
|
111
111
|
endAngleProp ??
|
|
112
|
-
degreesToRadians(
|
|
113
|
-
(ctx.config.xRange ? max(ctx.config.xRange as number[]) : max(range))!
|
|
114
|
-
)
|
|
112
|
+
degreesToRadians((ctx.config.xRange ? max(ctx.config.xRange as number[]) : max(range))!)
|
|
115
113
|
);
|
|
116
114
|
|
|
117
115
|
const motionEndAngle = createMotion(0, () => endAngle, motion);
|
|
@@ -120,9 +118,7 @@
|
|
|
120
118
|
let _pie = d3pie<any>()
|
|
121
119
|
.startAngle(
|
|
122
120
|
startAngleProp ??
|
|
123
|
-
degreesToRadians(
|
|
124
|
-
(ctx.config.xRange ? min(ctx.config.xRange as number[]) : min(range))!
|
|
125
|
-
)
|
|
121
|
+
degreesToRadians((ctx.config.xRange ? min(ctx.config.xRange as number[]) : min(range))!)
|
|
126
122
|
)
|
|
127
123
|
.endAngle(motionEndAngle.current)
|
|
128
124
|
.padAngle(padAngle)
|
|
@@ -144,10 +144,14 @@
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
if (layerCtx === 'canvas') {
|
|
147
|
-
ctx.registerComponent({
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
ctx.registerComponent({
|
|
148
|
+
name: 'Gradient',
|
|
149
|
+
kind: 'group',
|
|
150
|
+
canvasRender: {
|
|
151
|
+
render,
|
|
152
|
+
deps: () => [stops, cx, cy, fx, fy, ctx.width, ctx.height],
|
|
153
|
+
},
|
|
154
|
+
});
|
|
151
155
|
}
|
|
152
156
|
</script>
|
|
153
157
|
|
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
import { createMotion, parseMotionProp, type MotionProp } from '../utils/motion.svelte.js';
|
|
6
6
|
import { renderRect, type ComputedStylesOptions } from '../utils/canvas.js';
|
|
7
7
|
import type { DataProp, DataDrivenStyleProps } from '../utils/dataProp.js';
|
|
8
|
-
import
|
|
8
|
+
import {
|
|
9
|
+
resolveCorners,
|
|
10
|
+
cornersUniform,
|
|
11
|
+
type Corners,
|
|
12
|
+
type Insets,
|
|
13
|
+
} from '../utils/rect.svelte.js';
|
|
14
|
+
import { roundedRectPath, parseDashArray } from '../utils/path.js';
|
|
9
15
|
|
|
10
16
|
export type RectPropsWithoutHTML = {
|
|
11
17
|
/**
|
|
@@ -119,6 +125,20 @@
|
|
|
119
125
|
/** Motion configuration (pixel mode only). */
|
|
120
126
|
motion?: MotionProp<'x' | 'y' | 'width' | 'height'>;
|
|
121
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Dashed-border pattern. Accepts a number (single dash length), a
|
|
130
|
+
* `[dash, gap, ...]` array, or a string (same syntax as SVG
|
|
131
|
+
* `stroke-dasharray`). HTML layer approximates via `border-style: dashed`.
|
|
132
|
+
*/
|
|
133
|
+
dashArray?: number | number[] | string;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Per-corner radii. Accepts a number (all corners equal — same as `rx`),
|
|
137
|
+
* a `[tl, tr, br, bl]` tuple, or `{ topLeft, topRight, bottomRight, bottomLeft }`.
|
|
138
|
+
* Takes precedence over `rx`/`ry` when corners differ.
|
|
139
|
+
*/
|
|
140
|
+
corners?: Corners;
|
|
141
|
+
|
|
122
142
|
/** Children content to render. Note: Only works for Html layers */
|
|
123
143
|
children?: Snippet;
|
|
124
144
|
} & DataDrivenStyleProps;
|
|
@@ -175,6 +195,8 @@
|
|
|
175
195
|
key: keyFn = (_: any, i: number) => i,
|
|
176
196
|
ref: refProp = $bindable(),
|
|
177
197
|
motion,
|
|
198
|
+
corners,
|
|
199
|
+
dashArray,
|
|
178
200
|
class: className,
|
|
179
201
|
onclick,
|
|
180
202
|
ondblclick,
|
|
@@ -279,10 +301,24 @@
|
|
|
279
301
|
});
|
|
280
302
|
});
|
|
281
303
|
|
|
304
|
+
// Fold a uniform `corners` value into rx/ry so SVG `<rect>` renders rounded
|
|
305
|
+
// corners without needing a `<path>`. When corners are per-corner different,
|
|
306
|
+
// `pixelPathData` kicks in below and SVG emits a `<path>` instead.
|
|
307
|
+
const dashArrayResolved = $derived(parseDashArray(dashArray));
|
|
308
|
+
const dashArrayAttr = $derived(dashArrayResolved ? dashArrayResolved.join(' ') : undefined);
|
|
309
|
+
|
|
310
|
+
const cornersUniformValue = $derived.by(() => {
|
|
311
|
+
if (corners === undefined) return undefined;
|
|
312
|
+
if (typeof corners === 'number') return corners;
|
|
313
|
+
const resolved = resolveCorners(corners, Infinity, Infinity);
|
|
314
|
+
return cornersUniform(resolved) ? resolved[0] : undefined;
|
|
315
|
+
});
|
|
316
|
+
const cornersNonUniform = $derived(corners !== undefined && cornersUniformValue === undefined);
|
|
317
|
+
|
|
282
318
|
// Normalize rx/ry - if only one is provided, use it for both (SVG behavior)
|
|
283
319
|
// Coerce to number for canvas rendering (SVG allows string like "50%")
|
|
284
|
-
const rx = $derived(Number(rxProp ?? ryProp) || 0);
|
|
285
|
-
const ry = $derived(Number(ryProp ?? rxProp) || 0);
|
|
320
|
+
const rx = $derived(Number(rxProp ?? ryProp ?? cornersUniformValue) || 0);
|
|
321
|
+
const ry = $derived(Number(ryProp ?? rxProp ?? cornersUniformValue) || 0);
|
|
286
322
|
|
|
287
323
|
// --- Pixel mode ---
|
|
288
324
|
let ref = $state<SVGRectElement>();
|
|
@@ -330,11 +366,38 @@
|
|
|
330
366
|
const staticStrokeWidth = $derived(typeof strokeWidth === 'number' ? strokeWidth : undefined);
|
|
331
367
|
const staticOpacity = $derived(typeof opacity === 'number' ? opacity : undefined);
|
|
332
368
|
const staticClassName = $derived(typeof className === 'string' ? className : undefined);
|
|
369
|
+
// Match SVG's implicit `stroke-width: 1` default: if `stroke` is set but
|
|
370
|
+
// `strokeWidth` is not, render a 1px border so HTML matches SVG/Canvas layers.
|
|
333
371
|
const staticBorderWidth = $derived(
|
|
334
|
-
typeof strokeWidth === 'number'
|
|
372
|
+
typeof strokeWidth === 'number'
|
|
373
|
+
? `${strokeWidth}px`
|
|
374
|
+
: typeof stroke === 'string'
|
|
375
|
+
? '1px'
|
|
376
|
+
: undefined
|
|
335
377
|
);
|
|
336
378
|
const htmlRestProps = $derived(restProps as unknown as HTMLAttributes<HTMLDivElement>);
|
|
337
379
|
|
|
380
|
+
// Resolved per-corner radii for the static (pixel-mode) rect, clamped to bounds.
|
|
381
|
+
const resolvedCorners = $derived(
|
|
382
|
+
corners !== undefined
|
|
383
|
+
? resolveCorners(corners, motionWidth.current, motionHeight.current)
|
|
384
|
+
: undefined
|
|
385
|
+
);
|
|
386
|
+
const borderRadiusStyle = $derived(
|
|
387
|
+
resolvedCorners ? resolvedCorners.map((c) => `${c}px`).join(' ') : undefined
|
|
388
|
+
);
|
|
389
|
+
const pixelPathData = $derived(
|
|
390
|
+
resolvedCorners && cornersNonUniform
|
|
391
|
+
? roundedRectPath(
|
|
392
|
+
motionX.current,
|
|
393
|
+
motionY.current,
|
|
394
|
+
motionWidth.current,
|
|
395
|
+
motionHeight.current,
|
|
396
|
+
resolvedCorners
|
|
397
|
+
)
|
|
398
|
+
: undefined
|
|
399
|
+
);
|
|
400
|
+
|
|
338
401
|
function getStyleOptions(
|
|
339
402
|
styleOverrides: ComputedStylesOptions | undefined,
|
|
340
403
|
itemFill?: string | undefined,
|
|
@@ -371,7 +434,13 @@
|
|
|
371
434
|
'lc-rect',
|
|
372
435
|
itemClass ?? (typeof className === 'string' ? className : undefined)
|
|
373
436
|
),
|
|
374
|
-
style:
|
|
437
|
+
style:
|
|
438
|
+
[
|
|
439
|
+
restProps.style as string | undefined,
|
|
440
|
+
dashArrayAttr ? `stroke-dasharray: ${dashArrayAttr}` : undefined,
|
|
441
|
+
]
|
|
442
|
+
.filter(Boolean)
|
|
443
|
+
.join('; ') || undefined,
|
|
375
444
|
};
|
|
376
445
|
}
|
|
377
446
|
|
|
@@ -422,6 +491,7 @@
|
|
|
422
491
|
height: motionHeight.current,
|
|
423
492
|
rx,
|
|
424
493
|
ry,
|
|
494
|
+
corners: resolvedCorners,
|
|
425
495
|
},
|
|
426
496
|
styleOpts
|
|
427
497
|
);
|
|
@@ -475,6 +545,8 @@
|
|
|
475
545
|
restProps.style,
|
|
476
546
|
rx,
|
|
477
547
|
ry,
|
|
548
|
+
resolvedCorners,
|
|
549
|
+
dashArrayAttr,
|
|
478
550
|
],
|
|
479
551
|
}
|
|
480
552
|
: undefined,
|
|
@@ -504,6 +576,7 @@
|
|
|
504
576
|
opacity={resolvedOpacity}
|
|
505
577
|
{rx}
|
|
506
578
|
{ry}
|
|
579
|
+
stroke-dasharray={dashArrayAttr}
|
|
507
580
|
class={cls('lc-rect', resolvedClass)}
|
|
508
581
|
{...restProps}
|
|
509
582
|
{onclick}
|
|
@@ -515,6 +588,28 @@
|
|
|
515
588
|
{onpointerout}
|
|
516
589
|
/>
|
|
517
590
|
{/each}
|
|
591
|
+
{:else if pixelPathData}
|
|
592
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
593
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
594
|
+
<path
|
|
595
|
+
d={pixelPathData}
|
|
596
|
+
fill={staticFill}
|
|
597
|
+
fill-opacity={staticFillOpacity}
|
|
598
|
+
stroke={staticStroke}
|
|
599
|
+
stroke-opacity={staticStrokeOpacity}
|
|
600
|
+
stroke-width={staticStrokeWidth}
|
|
601
|
+
opacity={staticOpacity}
|
|
602
|
+
stroke-dasharray={dashArrayAttr}
|
|
603
|
+
class={cls('lc-rect', staticClassName)}
|
|
604
|
+
{...restProps as unknown as SVGAttributes<SVGPathElement>}
|
|
605
|
+
{onclick}
|
|
606
|
+
{ondblclick}
|
|
607
|
+
{onpointerenter}
|
|
608
|
+
{onpointermove}
|
|
609
|
+
{onpointerleave}
|
|
610
|
+
{onpointerover}
|
|
611
|
+
{onpointerout}
|
|
612
|
+
/>
|
|
518
613
|
{:else}
|
|
519
614
|
<rect
|
|
520
615
|
x={motionX.current}
|
|
@@ -529,6 +624,7 @@
|
|
|
529
624
|
opacity={staticOpacity}
|
|
530
625
|
{rx}
|
|
531
626
|
{ry}
|
|
627
|
+
stroke-dasharray={dashArrayAttr}
|
|
532
628
|
class={cls('lc-rect', staticClassName)}
|
|
533
629
|
{...restProps}
|
|
534
630
|
{onclick}
|
|
@@ -550,6 +646,12 @@
|
|
|
550
646
|
{@const resolvedStrokeWidth = resolveStyleProp(strokeWidth, item.d)}
|
|
551
647
|
{@const resolvedOpacity = resolveStyleProp(opacity, item.d)}
|
|
552
648
|
{@const resolvedClass = resolveStyleProp(className, item.d)}
|
|
649
|
+
{@const resolvedBorderWidth =
|
|
650
|
+
resolvedStrokeWidth != null
|
|
651
|
+
? `${resolvedStrokeWidth}px`
|
|
652
|
+
: resolvedStroke != null
|
|
653
|
+
? '1px'
|
|
654
|
+
: undefined}
|
|
553
655
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
554
656
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
555
657
|
<div
|
|
@@ -559,9 +661,10 @@
|
|
|
559
661
|
style:width="{item.width}px"
|
|
560
662
|
style:height="{item.height}px"
|
|
561
663
|
style:background={resolvedFill}
|
|
664
|
+
style:background-origin="border-box"
|
|
562
665
|
style:opacity={resolvedOpacity}
|
|
563
|
-
style:border-width=
|
|
564
|
-
style:border-style=
|
|
666
|
+
style:border-width={resolvedBorderWidth}
|
|
667
|
+
style:border-style={dashArrayResolved ? 'dashed' : 'solid'}
|
|
565
668
|
style:border-color={resolvedStroke}
|
|
566
669
|
style:border-radius="{rx}px"
|
|
567
670
|
class={cls('lc-rect', resolvedClass)}
|
|
@@ -585,11 +688,12 @@
|
|
|
585
688
|
style:width="{motionWidth.current}px"
|
|
586
689
|
style:height="{motionHeight.current}px"
|
|
587
690
|
style:background={staticFill}
|
|
691
|
+
style:background-origin="border-box"
|
|
588
692
|
style:opacity={staticOpacity}
|
|
589
693
|
style:border-width={staticBorderWidth}
|
|
590
|
-
style:border-style=
|
|
694
|
+
style:border-style={dashArrayResolved ? 'dashed' : 'solid'}
|
|
591
695
|
style:border-color={staticStroke}
|
|
592
|
-
style:border-radius=
|
|
696
|
+
style:border-radius={borderRadiusStyle ?? `${rx}px`}
|
|
593
697
|
class={cls('lc-rect', staticClassName)}
|
|
594
698
|
{...htmlRestProps}
|
|
595
699
|
{onclick}
|
|
@@ -621,6 +725,10 @@
|
|
|
621
725
|
}
|
|
622
726
|
|
|
623
727
|
/* Html layers */
|
|
728
|
+
:global(:where(.lc-layout-html .lc-rect)) {
|
|
729
|
+
/* Match SVG sizing/positioning (visual extent equals `width`×`height`, border on outer edge) */
|
|
730
|
+
box-sizing: border-box;
|
|
731
|
+
}
|
|
624
732
|
:global(:where(.lc-layout-html .lc-rect):not([background])) {
|
|
625
733
|
background: var(--fill-color);
|
|
626
734
|
}
|
|
@@ -3,7 +3,7 @@ import type { SVGAttributes } from 'svelte/elements';
|
|
|
3
3
|
import type { CommonEvents, Without } from '../utils/types.js';
|
|
4
4
|
import { type MotionProp } from '../utils/motion.svelte.js';
|
|
5
5
|
import type { DataProp, DataDrivenStyleProps } from '../utils/dataProp.js';
|
|
6
|
-
import type
|
|
6
|
+
import { type Corners, type Insets } from '../utils/rect.svelte.js';
|
|
7
7
|
export type RectPropsWithoutHTML = {
|
|
8
8
|
/**
|
|
9
9
|
* The x position of the rectangle.
|
|
@@ -101,6 +101,18 @@ export type RectPropsWithoutHTML = {
|
|
|
101
101
|
ref?: SVGRectElement;
|
|
102
102
|
/** Motion configuration (pixel mode only). */
|
|
103
103
|
motion?: MotionProp<'x' | 'y' | 'width' | 'height'>;
|
|
104
|
+
/**
|
|
105
|
+
* Dashed-border pattern. Accepts a number (single dash length), a
|
|
106
|
+
* `[dash, gap, ...]` array, or a string (same syntax as SVG
|
|
107
|
+
* `stroke-dasharray`). HTML layer approximates via `border-style: dashed`.
|
|
108
|
+
*/
|
|
109
|
+
dashArray?: number | number[] | string;
|
|
110
|
+
/**
|
|
111
|
+
* Per-corner radii. Accepts a number (all corners equal — same as `rx`),
|
|
112
|
+
* a `[tl, tr, br, bl]` tuple, or `{ topLeft, topRight, bottomRight, bottomLeft }`.
|
|
113
|
+
* Takes precedence over `rx`/`ry` when corners differ.
|
|
114
|
+
*/
|
|
115
|
+
corners?: Corners;
|
|
104
116
|
/** Children content to render. Note: Only works for Html layers */
|
|
105
117
|
children?: Snippet;
|
|
106
118
|
} & DataDrivenStyleProps;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
-
import
|
|
2
|
+
import { type RectPropsWithoutHTML } from './Rect.svelte';
|
|
3
3
|
import type { CommonEvents, Without } from '../utils/types.js';
|
|
4
4
|
import type { SVGAttributes } from 'svelte/elements';
|
|
5
5
|
import type { Snippet } from 'svelte';
|
|
@@ -45,6 +45,13 @@
|
|
|
45
45
|
*/
|
|
46
46
|
disabled?: boolean;
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Invert the clip — content renders *outside* the rect.
|
|
50
|
+
*
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
53
|
+
invert?: boolean;
|
|
54
|
+
|
|
48
55
|
/**
|
|
49
56
|
* The default children snippet which provides
|
|
50
57
|
* the id and url for the clipPath.
|
|
@@ -65,7 +72,6 @@
|
|
|
65
72
|
<script lang="ts">
|
|
66
73
|
import ClipPath from './ClipPath.svelte';
|
|
67
74
|
import { createId } from '../utils/createId.js';
|
|
68
|
-
import { extractLayerProps } from '../utils/attributes.js';
|
|
69
75
|
import type { MotionProp } from '../utils/motion.svelte.js';
|
|
70
76
|
|
|
71
77
|
const uid = $props.id();
|
|
@@ -77,24 +83,14 @@
|
|
|
77
83
|
width,
|
|
78
84
|
height,
|
|
79
85
|
disabled = false,
|
|
86
|
+
invert = false,
|
|
80
87
|
children: childrenProp,
|
|
81
|
-
...restProps
|
|
82
88
|
}: RectClipPathProps = $props();
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
ctx.beginPath();
|
|
86
|
-
ctx.rect(x, y, width, height);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function canvasClipDeps() {
|
|
90
|
-
return [x, y, width, height];
|
|
91
|
-
}
|
|
90
|
+
const path = $derived(`M${x},${y} h${width} v${height} h${-width} Z`);
|
|
92
91
|
</script>
|
|
93
92
|
|
|
94
|
-
<ClipPath {id} {disabled} {
|
|
95
|
-
{#snippet clip()}
|
|
96
|
-
<Rect {x} {y} {width} {height} {...extractLayerProps(restProps, 'lc-clip-path-rect')} />
|
|
97
|
-
{/snippet}
|
|
93
|
+
<ClipPath {id} {disabled} {invert} {path}>
|
|
98
94
|
{#snippet children({ url })}
|
|
99
95
|
{@render childrenProp?.({ id, url })}
|
|
100
96
|
{/snippet}
|
|
@@ -37,6 +37,12 @@ export type BaseRectClipPathPropsWithoutHTML = {
|
|
|
37
37
|
* @default false
|
|
38
38
|
*/
|
|
39
39
|
disabled?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Invert the clip — content renders *outside* the rect.
|
|
42
|
+
*
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
invert?: boolean;
|
|
40
46
|
/**
|
|
41
47
|
* The default children snippet which provides
|
|
42
48
|
* the id and url for the clipPath.
|
|
@@ -95,12 +95,30 @@
|
|
|
95
95
|
const ctx = getChartContext();
|
|
96
96
|
const geo = getGeoContext();
|
|
97
97
|
|
|
98
|
-
let {
|
|
98
|
+
let {
|
|
99
|
+
data,
|
|
100
|
+
x,
|
|
101
|
+
y,
|
|
102
|
+
seriesKey,
|
|
103
|
+
defined,
|
|
104
|
+
curve,
|
|
105
|
+
stroke,
|
|
106
|
+
fill,
|
|
107
|
+
opacity,
|
|
108
|
+
motion,
|
|
109
|
+
...restProps
|
|
110
|
+
}: SplineProps = $props();
|
|
99
111
|
|
|
100
112
|
ctx.registerComponent({
|
|
101
113
|
name: 'Spline',
|
|
102
114
|
kind: 'mark',
|
|
103
|
-
markInfo: () => ({
|
|
115
|
+
markInfo: () => ({
|
|
116
|
+
data,
|
|
117
|
+
x,
|
|
118
|
+
y,
|
|
119
|
+
seriesKey,
|
|
120
|
+
color: typeof stroke === 'string' ? stroke : undefined,
|
|
121
|
+
}),
|
|
104
122
|
});
|
|
105
123
|
|
|
106
124
|
function getScaleValue(
|
|
@@ -267,8 +285,8 @@
|
|
|
267
285
|
|
|
268
286
|
const seriesOpacity = $derived(
|
|
269
287
|
series?.key == null ||
|
|
270
|
-
|
|
271
|
-
|
|
288
|
+
ctx.series.visibleSeries.length <= 1 ||
|
|
289
|
+
ctx.series.isHighlighted(series.key, true)
|
|
272
290
|
? 1
|
|
273
291
|
: 0.1
|
|
274
292
|
);
|
|
@@ -686,7 +686,14 @@
|
|
|
686
686
|
if (segments) {
|
|
687
687
|
let xOffset = baseX;
|
|
688
688
|
for (const segment of segments) {
|
|
689
|
-
const segStyles = getTextStyles(
|
|
689
|
+
const segStyles = getTextStyles(
|
|
690
|
+
undefined,
|
|
691
|
+
undefined,
|
|
692
|
+
undefined,
|
|
693
|
+
undefined,
|
|
694
|
+
undefined,
|
|
695
|
+
segment.class
|
|
696
|
+
);
|
|
690
697
|
const text = String(segment.value);
|
|
691
698
|
// Set font before rendering and measuring so width is accurate
|
|
692
699
|
const segComputedStyles = getComputedStyles(ctx.canvas, segStyles);
|
|
@@ -838,10 +845,7 @@
|
|
|
838
845
|
>
|
|
839
846
|
{#if segments}
|
|
840
847
|
{#each segments as segment, index (index)}
|
|
841
|
-
<tspan
|
|
842
|
-
dy={index === 0 ? startDy : 0}
|
|
843
|
-
class={['lc-text-tspan', segment.class]}
|
|
844
|
-
>
|
|
848
|
+
<tspan dy={index === 0 ? startDy : 0} class={['lc-text-tspan', segment.class]}>
|
|
845
849
|
{segment.value}
|
|
846
850
|
</tspan>
|
|
847
851
|
{/each}
|
|
@@ -864,6 +868,9 @@
|
|
|
864
868
|
{#if dataMode}
|
|
865
869
|
{#each resolvedItems as item (item.key)}
|
|
866
870
|
{@const text = resolveTextValue(item.d)}
|
|
871
|
+
{@const resolvedFill = resolveColorProp(fill, item.d, chartCtx.cScale)}
|
|
872
|
+
{@const resolvedFillOpacity = resolveStyleProp(fillOpacity, item.d)}
|
|
873
|
+
{@const resolvedOpacity = resolveStyleProp(opacity, item.d)}
|
|
867
874
|
{@const resolvedClass = resolveStyleProp(className, item.d)}
|
|
868
875
|
{@const translateX = textAnchor === 'middle' ? '-50%' : textAnchor === 'end' ? '-100%' : '0%'}
|
|
869
876
|
{@const translateY =
|
|
@@ -881,6 +888,8 @@
|
|
|
881
888
|
{textAnchor === 'middle' ? 'center' : textAnchor === 'end' ? 'right' : 'left'}"
|
|
882
889
|
style:white-space="pre-wrap"
|
|
883
890
|
style:line-height={lineHeight}
|
|
891
|
+
style:color={resolvedFill}
|
|
892
|
+
style:opacity={resolvedOpacity ?? resolvedFillOpacity}
|
|
884
893
|
class={['lc-text', resolvedClass]}
|
|
885
894
|
>
|
|
886
895
|
{text}
|
|
@@ -903,6 +912,8 @@
|
|
|
903
912
|
{textAnchor === 'middle' ? 'center' : textAnchor === 'end' ? 'right' : 'left'}"
|
|
904
913
|
style:white-space="pre-wrap"
|
|
905
914
|
style:line-height={lineHeight}
|
|
915
|
+
style:color={staticFill}
|
|
916
|
+
style:opacity={staticOpacity ?? staticFillOpacity}
|
|
906
917
|
class={['lc-text', staticClassName]}
|
|
907
918
|
>
|
|
908
919
|
{#if segments}
|
|
@@ -180,9 +180,15 @@
|
|
|
180
180
|
.map((d: any) => ({
|
|
181
181
|
x: getScaleValue(d, ctx.xScale, xAccessor) + xOffset,
|
|
182
182
|
y: getScaleValue(d, ctx.yScale, yAccessor) + yOffset,
|
|
183
|
-
r:
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
r:
|
|
184
|
+
resolvedR != null
|
|
185
|
+
? resolveDataProp(
|
|
186
|
+
resolvedR,
|
|
187
|
+
d,
|
|
188
|
+
ctx.rScale,
|
|
189
|
+
typeof resolvedR === 'number' ? resolvedR : 4
|
|
190
|
+
)
|
|
191
|
+
: 4,
|
|
186
192
|
}));
|
|
187
193
|
|
|
188
194
|
return computeTrailPath(points, { curve, cap, tension, resolution });
|
|
@@ -206,9 +212,15 @@
|
|
|
206
212
|
.map((d: any) => ({
|
|
207
213
|
x: getScaleValue(d, ctx.xScale, xAccessor) + xOffset,
|
|
208
214
|
y: baseline,
|
|
209
|
-
r:
|
|
210
|
-
|
|
211
|
-
|
|
215
|
+
r:
|
|
216
|
+
resolvedR != null
|
|
217
|
+
? resolveDataProp(
|
|
218
|
+
resolvedR,
|
|
219
|
+
d,
|
|
220
|
+
ctx.rScale,
|
|
221
|
+
typeof resolvedR === 'number' ? resolvedR : 4
|
|
222
|
+
)
|
|
223
|
+
: 4,
|
|
212
224
|
}));
|
|
213
225
|
|
|
214
226
|
return computeTrailPath(points, { curve, cap, tension, resolution });
|
|
@@ -236,7 +248,7 @@
|
|
|
236
248
|
<Path
|
|
237
249
|
pathData={isTweened ? tweenState.current : trailPath}
|
|
238
250
|
{fill}
|
|
239
|
-
|
|
251
|
+
{fillOpacity}
|
|
240
252
|
{opacity}
|
|
241
253
|
stroke="none"
|
|
242
254
|
class={cls('lc-trail', className)}
|
|
@@ -49,9 +49,13 @@
|
|
|
49
49
|
const ctx = getChartContext();
|
|
50
50
|
|
|
51
51
|
const treeData = $derived.by(() => {
|
|
52
|
-
const _tree =
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
const _tree = ctx.radial
|
|
53
|
+
? d3Tree<T>()
|
|
54
|
+
.size([2 * Math.PI, Math.min(ctx.width, ctx.height) / 2])
|
|
55
|
+
.separation((a, b) => (a.parent === b.parent ? 1 : 2) / a.depth)
|
|
56
|
+
: d3Tree<T>().size(
|
|
57
|
+
orientation === 'horizontal' ? [ctx.height, ctx.width] : [ctx.width, ctx.height]
|
|
58
|
+
);
|
|
55
59
|
|
|
56
60
|
if (nodeSize) {
|
|
57
61
|
_tree.nodeSize(nodeSize);
|