layerchart 2.0.0-next.3 → 2.0.0-next.30
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/components/AnnotationPoint.svelte +16 -9
- package/dist/components/AnnotationRange.svelte +3 -3
- package/dist/components/Arc.svelte +2 -2
- package/dist/components/Axis.svelte +63 -14
- package/dist/components/Axis.svelte.d.ts +12 -2
- package/dist/components/Blur.svelte +5 -3
- package/dist/components/Blur.svelte.d.ts +2 -5
- package/dist/components/BrushContext.svelte +1 -1
- package/dist/components/Calendar.svelte +10 -6
- package/dist/components/Calendar.svelte.d.ts +2 -1
- package/dist/components/Chart.svelte +2 -2
- package/dist/components/Connector.svelte +2 -2
- package/dist/components/Connector.svelte.d.ts +1 -1
- package/dist/components/Ellipse.svelte +187 -0
- package/dist/components/Ellipse.svelte.d.ts +64 -0
- package/dist/components/ForceSimulation.svelte +168 -50
- package/dist/components/ForceSimulation.svelte.d.ts +80 -21
- package/dist/components/GeoPath.svelte +12 -5
- package/dist/components/GeoPoint.svelte +1 -2
- package/dist/components/GeoSpline.svelte +4 -4
- package/dist/components/GeoSpline.svelte.d.ts +1 -1
- package/dist/components/Group.svelte +2 -2
- package/dist/components/Highlight.svelte +2 -2
- package/dist/components/Hull.svelte +1 -1
- package/dist/components/Labels.svelte +3 -2
- package/dist/components/Labels.svelte.d.ts +2 -2
- package/dist/components/Legend.svelte +19 -12
- package/dist/components/Legend.svelte.d.ts +5 -5
- package/dist/components/MonthPath.svelte +14 -11
- package/dist/components/MonthPath.svelte.d.ts +4 -3
- package/dist/components/Polygon.svelte +285 -0
- package/dist/components/Polygon.svelte.d.ts +115 -0
- package/dist/components/RadialGradient.svelte +1 -3
- package/dist/components/Spline.svelte +30 -18
- package/dist/components/Spline.svelte.d.ts +12 -4
- package/dist/components/Text.svelte +62 -60
- package/dist/components/Text.svelte.d.ts +6 -0
- package/dist/components/TransformControls.svelte +16 -20
- package/dist/components/Treemap.svelte +63 -26
- package/dist/components/Treemap.svelte.d.ts +11 -11
- package/dist/components/Voronoi.svelte +51 -33
- package/dist/components/Voronoi.svelte.d.ts +3 -1
- package/dist/components/charts/ArcChart.svelte +5 -3
- package/dist/components/charts/AreaChart.svelte +11 -11
- package/dist/components/charts/BarChart.svelte +64 -53
- package/dist/components/charts/DefaultTooltip.svelte +1 -1
- package/dist/components/charts/LineChart.svelte +10 -6
- package/dist/components/charts/PieChart.svelte +5 -3
- package/dist/components/charts/ScatterChart.svelte +2 -3
- package/dist/components/charts/utils.svelte.d.ts +2 -2
- package/dist/components/charts/utils.svelte.js +5 -1
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +5 -1
- package/dist/components/layout/Canvas.svelte +67 -49
- package/dist/components/layout/Canvas.svelte.d.ts +6 -0
- package/dist/components/layout/Layer.svelte +6 -4
- package/dist/components/layout/Layer.svelte.d.ts +6 -4
- package/dist/components/tooltip/Tooltip.svelte +14 -7
- package/dist/components/tooltip/TooltipContext.svelte +78 -34
- package/dist/components/tooltip/TooltipContext.svelte.d.ts +3 -3
- package/dist/components/tooltip/TooltipHeader.svelte +5 -4
- package/dist/components/tooltip/TooltipHeader.svelte.d.ts +3 -3
- package/dist/components/tooltip/TooltipItem.svelte +5 -4
- package/dist/components/tooltip/TooltipItem.svelte.d.ts +3 -3
- package/dist/components/tooltip/TooltipList.svelte +1 -1
- package/dist/components/tooltip/tooltipMetaContext.d.ts +2 -2
- package/dist/docs/Blockquote.svelte +6 -4
- package/dist/docs/Blockquote.svelte.d.ts +4 -19
- package/dist/docs/Code.svelte +20 -12
- package/dist/docs/Code.svelte.d.ts +9 -23
- package/dist/docs/Header1.svelte +4 -2
- package/dist/docs/Header1.svelte.d.ts +4 -28
- package/dist/docs/Json.svelte +11 -3
- package/dist/docs/Json.svelte.d.ts +9 -21
- package/dist/docs/Layout.svelte +10 -7
- package/dist/docs/Layout.svelte.d.ts +4 -19
- package/dist/docs/Link.svelte +7 -3
- package/dist/docs/Link.svelte.d.ts +4 -38
- package/dist/docs/Preview.svelte +6 -3
- package/dist/docs/TilesetField.svelte +20 -19
- package/dist/docs/TilesetField.svelte.d.ts +5 -22
- package/dist/docs/ViewSourceButton.svelte +9 -6
- package/dist/docs/ViewSourceButton.svelte.d.ts +7 -21
- package/dist/utils/arcText.svelte.js +4 -4
- package/dist/utils/array.d.ts +11 -0
- package/dist/utils/array.js +23 -0
- package/dist/utils/array.test.d.ts +1 -0
- package/dist/utils/array.test.js +200 -0
- package/dist/utils/canvas.d.ts +77 -0
- package/dist/utils/canvas.js +105 -41
- package/dist/utils/genData.d.ts +14 -0
- package/dist/utils/genData.js +24 -6
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/path.d.ts +10 -0
- package/dist/utils/path.js +30 -0
- package/dist/utils/scales.svelte.d.ts +3 -2
- package/dist/utils/scales.svelte.js +7 -3
- package/dist/utils/shape.d.ts +43 -0
- package/dist/utils/shape.js +59 -0
- package/dist/utils/string.d.ts +49 -0
- package/dist/utils/string.js +4 -2
- package/dist/utils/ticks.d.ts +15 -4
- package/dist/utils/ticks.js +140 -159
- package/dist/utils/ticks.test.js +6 -16
- package/dist/utils/treemap.d.ts +1 -1
- package/package.json +27 -25
- package/dist/utils/object.js +0 -2
|
@@ -102,6 +102,7 @@
|
|
|
102
102
|
fx = cx,
|
|
103
103
|
fy = cy,
|
|
104
104
|
r = '50%',
|
|
105
|
+
// fr = '0%'; // TODO: Svelte / Typescript does not know `<radialRadiant fr="...">`
|
|
105
106
|
spreadMethod = 'pad',
|
|
106
107
|
transform = undefined,
|
|
107
108
|
units = 'objectBoundingBox',
|
|
@@ -111,9 +112,6 @@
|
|
|
111
112
|
...restProps
|
|
112
113
|
}: RadialGradientProps = $props();
|
|
113
114
|
|
|
114
|
-
// TODO: Svelte / Typescript does not know `<radialRadiant fr="...">`
|
|
115
|
-
// export let fr = '0%';
|
|
116
|
-
|
|
117
115
|
const ctx = getChartContext();
|
|
118
116
|
|
|
119
117
|
const renderCtx = getRenderContext();
|
|
@@ -87,23 +87,23 @@
|
|
|
87
87
|
/**
|
|
88
88
|
* Add additional content at the start of the line.
|
|
89
89
|
*
|
|
90
|
-
* Receives `{ point: DOMPoint }` as a snippet prop.
|
|
90
|
+
* Receives `{ point: DOMPoint; value: { x: number; y: number } }` as a snippet prop.
|
|
91
91
|
*/
|
|
92
|
-
startContent?: Snippet<[{ point: DOMPoint }]>;
|
|
92
|
+
startContent?: Snippet<[{ point: DOMPoint; value: { x: number; y: number } }]>;
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
95
|
* Add additional content at the end of the line.
|
|
96
96
|
*
|
|
97
|
-
* Receives `{ point: DOMPoint }` as a snippet prop.
|
|
97
|
+
* Receives `{ point: DOMPoint; value: { x: number; y: number } }` as a snippet prop.
|
|
98
98
|
*/
|
|
99
|
-
endContent?: Snippet<[{ point: DOMPoint }]>;
|
|
99
|
+
endContent?: Snippet<[{ point: DOMPoint; value: { x: number; y: number } }]>;
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
102
|
* A reference to the `<path>` element.
|
|
103
103
|
*
|
|
104
104
|
* @bindable
|
|
105
105
|
*/
|
|
106
|
-
|
|
106
|
+
pathRef?: SVGPathElement;
|
|
107
107
|
|
|
108
108
|
motion?: MotionProp;
|
|
109
109
|
} & CommonStyleProps;
|
|
@@ -159,14 +159,14 @@
|
|
|
159
159
|
startContent,
|
|
160
160
|
endContent,
|
|
161
161
|
opacity,
|
|
162
|
-
|
|
162
|
+
pathRef: pathRefProp = $bindable(),
|
|
163
163
|
...restProps
|
|
164
164
|
}: SplineProps = $props();
|
|
165
165
|
|
|
166
|
-
let
|
|
166
|
+
let pathRef = $state<SVGPathElement>();
|
|
167
167
|
|
|
168
168
|
$effect.pre(() => {
|
|
169
|
-
|
|
169
|
+
pathRefProp = pathRef;
|
|
170
170
|
});
|
|
171
171
|
|
|
172
172
|
const markerStart = $derived(markerStartProp ?? marker);
|
|
@@ -331,8 +331,8 @@
|
|
|
331
331
|
easing: typeof draw === 'object' && draw.easing ? draw.easing : cubicInOut,
|
|
332
332
|
interpolate() {
|
|
333
333
|
return (t: number) => {
|
|
334
|
-
const totalLength =
|
|
335
|
-
const point =
|
|
334
|
+
const totalLength = pathRef?.getTotalLength() ?? 0;
|
|
335
|
+
const point = pathRef?.getPointAtLength(totalLength * t);
|
|
336
336
|
return point;
|
|
337
337
|
};
|
|
338
338
|
},
|
|
@@ -343,16 +343,16 @@
|
|
|
343
343
|
$effect(() => {
|
|
344
344
|
if (!startContent && !endContent) return;
|
|
345
345
|
d;
|
|
346
|
-
if (!
|
|
347
|
-
startPoint =
|
|
348
|
-
const totalLength =
|
|
349
|
-
endPoint.target =
|
|
346
|
+
if (!pathRef || !pathRef.getTotalLength()) return;
|
|
347
|
+
startPoint = pathRef.getPointAtLength(0);
|
|
348
|
+
const totalLength = pathRef.getTotalLength();
|
|
349
|
+
endPoint.target = pathRef.getPointAtLength(totalLength);
|
|
350
350
|
});
|
|
351
351
|
|
|
352
352
|
$effect(() => {
|
|
353
353
|
if (!draw) return;
|
|
354
|
-
[tweenedState.current];
|
|
355
354
|
// Anytime the path data changes, redraw
|
|
355
|
+
[pathData, data, ctx.data];
|
|
356
356
|
key = Symbol();
|
|
357
357
|
});
|
|
358
358
|
</script>
|
|
@@ -377,7 +377,7 @@
|
|
|
377
377
|
marker-mid={markerMidId ? `url(#${markerMidId})` : undefined}
|
|
378
378
|
marker-end={markerEndId ? `url(#${markerEndId})` : undefined}
|
|
379
379
|
in:drawTransition|global={typeof draw === 'object' ? draw : undefined}
|
|
380
|
-
bind:this={
|
|
380
|
+
bind:this={pathRef}
|
|
381
381
|
/>
|
|
382
382
|
<MarkerWrapper id={markerStartId} marker={markerStart} />
|
|
383
383
|
<MarkerWrapper id={markerMidId} marker={markerMid} />
|
|
@@ -385,13 +385,25 @@
|
|
|
385
385
|
|
|
386
386
|
{#if startContent && startPoint}
|
|
387
387
|
<Group x={startPoint.x} y={startPoint.y} class={layerClass('spline-g-start')}>
|
|
388
|
-
{@render startContent({
|
|
388
|
+
{@render startContent({
|
|
389
|
+
point: startPoint,
|
|
390
|
+
value: {
|
|
391
|
+
x: ctx.xScale?.invert?.(startPoint.x),
|
|
392
|
+
y: ctx.yScale?.invert?.(startPoint.y),
|
|
393
|
+
},
|
|
394
|
+
})}
|
|
389
395
|
</Group>
|
|
390
396
|
{/if}
|
|
391
397
|
|
|
392
398
|
{#if endContent && endPoint.current}
|
|
393
399
|
<Group x={endPoint.current.x} y={endPoint.current.y} class={layerClass('spline-g-end')}>
|
|
394
|
-
{@render endContent({
|
|
400
|
+
{@render endContent({
|
|
401
|
+
point: endPoint.current,
|
|
402
|
+
value: {
|
|
403
|
+
x: ctx.xScale?.invert?.(endPoint.current.x),
|
|
404
|
+
y: ctx.yScale?.invert?.(endPoint.current.y),
|
|
405
|
+
},
|
|
406
|
+
})}
|
|
395
407
|
</Group>
|
|
396
408
|
{/if}
|
|
397
409
|
{/key}
|
|
@@ -67,29 +67,37 @@ export type SplinePropsWithoutHTML = {
|
|
|
67
67
|
/**
|
|
68
68
|
* Add additional content at the start of the line.
|
|
69
69
|
*
|
|
70
|
-
* Receives `{ point: DOMPoint }` as a snippet prop.
|
|
70
|
+
* Receives `{ point: DOMPoint; value: { x: number; y: number } }` as a snippet prop.
|
|
71
71
|
*/
|
|
72
72
|
startContent?: Snippet<[{
|
|
73
73
|
point: DOMPoint;
|
|
74
|
+
value: {
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
};
|
|
74
78
|
}]>;
|
|
75
79
|
/**
|
|
76
80
|
* Add additional content at the end of the line.
|
|
77
81
|
*
|
|
78
|
-
* Receives `{ point: DOMPoint }` as a snippet prop.
|
|
82
|
+
* Receives `{ point: DOMPoint; value: { x: number; y: number } }` as a snippet prop.
|
|
79
83
|
*/
|
|
80
84
|
endContent?: Snippet<[{
|
|
81
85
|
point: DOMPoint;
|
|
86
|
+
value: {
|
|
87
|
+
x: number;
|
|
88
|
+
y: number;
|
|
89
|
+
};
|
|
82
90
|
}]>;
|
|
83
91
|
/**
|
|
84
92
|
* A reference to the `<path>` element.
|
|
85
93
|
*
|
|
86
94
|
* @bindable
|
|
87
95
|
*/
|
|
88
|
-
|
|
96
|
+
pathRef?: SVGPathElement;
|
|
89
97
|
motion?: MotionProp;
|
|
90
98
|
} & CommonStyleProps;
|
|
91
99
|
export type SplineProps = SplinePropsWithoutHTML & Without<SVGAttributes<SVGPathElement>, SplinePropsWithoutHTML>;
|
|
92
100
|
import { draw as _drawTransition } from 'svelte/transition';
|
|
93
|
-
declare const Spline: import("svelte").Component<SplineProps, {}, "
|
|
101
|
+
declare const Spline: import("svelte").Component<SplineProps, {}, "pathRef">;
|
|
94
102
|
type Spline = ReturnType<typeof Spline>;
|
|
95
103
|
export default Spline;
|
|
@@ -97,6 +97,20 @@
|
|
|
97
97
|
*/
|
|
98
98
|
verticalAnchor?: 'start' | 'middle' | 'end' | 'inherit';
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* The dominant baseline of the text. Useful for aligning text to the baseline of the axis.
|
|
102
|
+
*
|
|
103
|
+
* @default 'auto'
|
|
104
|
+
*/
|
|
105
|
+
dominantBaseline?:
|
|
106
|
+
| 'auto'
|
|
107
|
+
| 'text-before-edge'
|
|
108
|
+
| 'text-after-edge'
|
|
109
|
+
| 'middle'
|
|
110
|
+
| 'hanging'
|
|
111
|
+
| 'ideographic'
|
|
112
|
+
| 'mathematical';
|
|
113
|
+
|
|
100
114
|
/**
|
|
101
115
|
* Rotational angle of the text
|
|
102
116
|
*/
|
|
@@ -178,20 +192,6 @@
|
|
|
178
192
|
import { degreesToRadians } from '../utils/math.js';
|
|
179
193
|
import { createId } from '../utils/createId.js';
|
|
180
194
|
|
|
181
|
-
/*
|
|
182
|
-
TODO:
|
|
183
|
-
- [ ] Handle styled text (use <slot /> to measure?)
|
|
184
|
-
- [ ] Simplify by using `alignment-baseline` / `dominant-baseline`, rework multiline or drop support, etc
|
|
185
|
-
- https://svelte.dev/repl/f12d3003313a43ba8a0be53e5786f1c7?version=3.44.3
|
|
186
|
-
- https://observablehq.com/@neocartocnrs/cheat-sheet-on-texts-in-svg
|
|
187
|
-
|
|
188
|
-
Reference:
|
|
189
|
-
- https://bl.ocks.org/mbostock/7555321
|
|
190
|
-
- https://github.com/airbnb/visx/blob/master/packages/visx-text/src/Text.tsx
|
|
191
|
-
- https://airbnb.io/visx/text
|
|
192
|
-
- https://github.com/airbnb/visx/blob/master/packages/visx-demo/src/pages/text.tsx
|
|
193
|
-
*/
|
|
194
|
-
|
|
195
195
|
const uid = $props.id();
|
|
196
196
|
|
|
197
197
|
let {
|
|
@@ -208,6 +208,7 @@
|
|
|
208
208
|
scaleToFit = false,
|
|
209
209
|
textAnchor = 'start',
|
|
210
210
|
verticalAnchor = 'end',
|
|
211
|
+
dominantBaseline = 'auto',
|
|
211
212
|
rotate,
|
|
212
213
|
opacity = 1,
|
|
213
214
|
strokeWidth = 0,
|
|
@@ -227,6 +228,8 @@
|
|
|
227
228
|
...restProps
|
|
228
229
|
}: TextProps = $props();
|
|
229
230
|
|
|
231
|
+
const renderCtx = getRenderContext();
|
|
232
|
+
|
|
230
233
|
let ref = $state<SVGTextElement>();
|
|
231
234
|
let svgRef = $state<SVGElement>();
|
|
232
235
|
let pathRef = $state<SVGPathElement>();
|
|
@@ -260,49 +263,56 @@
|
|
|
260
263
|
};
|
|
261
264
|
});
|
|
262
265
|
|
|
263
|
-
|
|
266
|
+
// Handle null and convert `\n` strings back to newline characters
|
|
267
|
+
const rawText = $derived(value != null ? value.toString().replace(/\\n/g, '\n') : '');
|
|
264
268
|
|
|
265
269
|
const textValue = $derived.by(() => {
|
|
266
270
|
if (!truncateConfig) return rawText;
|
|
267
271
|
return truncateText(rawText, truncateConfig);
|
|
268
272
|
});
|
|
269
273
|
|
|
270
|
-
const
|
|
274
|
+
const spaceWidth = $derived(getStringWidth('\u00A0', style) || 0);
|
|
271
275
|
|
|
272
|
-
const
|
|
276
|
+
const wordsByLines = $derived.by(() => {
|
|
277
|
+
// Split by newlines to preserve explicit line breaks
|
|
278
|
+
const lines = textValue.split('\n');
|
|
273
279
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
width: getStringWidth(word, style) || 0,
|
|
278
|
-
}))
|
|
279
|
-
);
|
|
280
|
+
return lines.flatMap((line) => {
|
|
281
|
+
// Split each line into words
|
|
282
|
+
const words = line.split(/(?:(?!\u00A0+)\s+)/);
|
|
280
283
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
wordsWithWidth.reduce((result: { words: string[]; width?: number }[], item) => {
|
|
285
|
-
const currentLine = result[result.length - 1];
|
|
286
|
-
|
|
287
|
-
if (
|
|
288
|
-
currentLine &&
|
|
289
|
-
(width == null || scaleToFit || (currentLine.width || 0) + item.width + spaceWidth < width)
|
|
290
|
-
) {
|
|
291
|
-
// Word can be added to an existing line
|
|
292
|
-
currentLine.words.push(item.word);
|
|
293
|
-
currentLine.width = currentLine.width || 0;
|
|
294
|
-
currentLine.width += item.width + spaceWidth;
|
|
284
|
+
if (width == null) {
|
|
285
|
+
// No width specified, only use explicit line breaks (if used)
|
|
286
|
+
return [{ words }];
|
|
295
287
|
} else {
|
|
296
|
-
//
|
|
297
|
-
|
|
298
|
-
|
|
288
|
+
// Handle word wrapping within each line
|
|
289
|
+
return words.reduce((result: { words: string[]; width?: number }[], item) => {
|
|
290
|
+
const currentLine = result[result.length - 1];
|
|
291
|
+
const itemWidth = getStringWidth(item, style) || 0;
|
|
292
|
+
|
|
293
|
+
if (
|
|
294
|
+
currentLine &&
|
|
295
|
+
(width == null ||
|
|
296
|
+
scaleToFit ||
|
|
297
|
+
(currentLine.width || 0) + itemWidth + spaceWidth < width)
|
|
298
|
+
) {
|
|
299
|
+
// Word can be added to an existing line
|
|
300
|
+
currentLine.words.push(item);
|
|
301
|
+
currentLine.width = currentLine.width || 0;
|
|
302
|
+
currentLine.width += itemWidth + spaceWidth;
|
|
303
|
+
} else {
|
|
304
|
+
// Add first word to line or word is too long to scaleToFit on existing line
|
|
305
|
+
const newLine = { words: [item], width: itemWidth };
|
|
306
|
+
result.push(newLine);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return result;
|
|
310
|
+
}, []);
|
|
299
311
|
}
|
|
312
|
+
});
|
|
313
|
+
});
|
|
300
314
|
|
|
301
|
-
|
|
302
|
-
}, [])
|
|
303
|
-
);
|
|
304
|
-
|
|
305
|
-
const lines = $derived(wordsByLines.length);
|
|
315
|
+
const lineCount = $derived(wordsByLines.length);
|
|
306
316
|
|
|
307
317
|
/**
|
|
308
318
|
* Convert css value to pixel value (ex. 0.71em => 11.36)
|
|
@@ -327,28 +337,18 @@
|
|
|
327
337
|
|
|
328
338
|
const startDy = $derived.by(() => {
|
|
329
339
|
if (verticalAnchor === 'start') {
|
|
330
|
-
return getPixelValue(
|
|
331
|
-
} else if (verticalAnchor === 'middle') {
|
|
332
|
-
return ((lines - 1) / 2) * -getPixelValue(lineHeight) + getPixelValue(capHeight) / 2;
|
|
333
|
-
} else {
|
|
334
|
-
return (lines - 1) * -getPixelValue(lineHeight);
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
const pathStartDy = $derived.by(() => {
|
|
339
|
-
if (verticalAnchor === 'start') {
|
|
340
|
-
return getPixelValue(capHeight);
|
|
340
|
+
return getPixelValue(lineHeight);
|
|
341
341
|
} else if (verticalAnchor === 'middle') {
|
|
342
|
-
return (
|
|
342
|
+
return ((lineCount - 1) / 2) * -getPixelValue(lineHeight) + getPixelValue(capHeight) / 2;
|
|
343
343
|
} else {
|
|
344
|
-
return
|
|
344
|
+
return (lineCount - 1) * -getPixelValue(lineHeight) - getPixelValue(capHeight) / 2;
|
|
345
345
|
}
|
|
346
346
|
});
|
|
347
347
|
|
|
348
348
|
const scaleTransform = $derived.by(() => {
|
|
349
349
|
if (
|
|
350
350
|
scaleToFit &&
|
|
351
|
-
|
|
351
|
+
lineCount > 0 &&
|
|
352
352
|
typeof x == 'number' &&
|
|
353
353
|
typeof y == 'number' &&
|
|
354
354
|
typeof width == 'number'
|
|
@@ -500,6 +500,7 @@
|
|
|
500
500
|
>
|
|
501
501
|
<textPath
|
|
502
502
|
style="text-anchor: {textAnchor};"
|
|
503
|
+
dominant-baseline={dominantBaseline}
|
|
503
504
|
href="#{pathId}"
|
|
504
505
|
{startOffset}
|
|
505
506
|
class={cls(layerClass('text-path'))}
|
|
@@ -514,6 +515,7 @@
|
|
|
514
515
|
y={motionY.current}
|
|
515
516
|
{transform}
|
|
516
517
|
text-anchor={textAnchor}
|
|
518
|
+
dominant-baseline={dominantBaseline}
|
|
517
519
|
{...restProps}
|
|
518
520
|
{fill}
|
|
519
521
|
fill-opacity={fillOpacity}
|
|
@@ -525,7 +527,7 @@
|
|
|
525
527
|
{#each wordsByLines as line, index}
|
|
526
528
|
<tspan
|
|
527
529
|
x={motionX.current}
|
|
528
|
-
dy={index === 0 ? startDy : lineHeight}
|
|
530
|
+
dy={index === 0 ? startDy : getPixelValue(lineHeight)}
|
|
529
531
|
class={layerClass('text-tspan')}
|
|
530
532
|
>
|
|
531
533
|
{line.words.join(' ')}
|
|
@@ -80,6 +80,12 @@ export type TextPropsWithoutHTML = {
|
|
|
80
80
|
* @default 'end'
|
|
81
81
|
*/
|
|
82
82
|
verticalAnchor?: 'start' | 'middle' | 'end' | 'inherit';
|
|
83
|
+
/**
|
|
84
|
+
* The dominant baseline of the text. Useful for aligning text to the baseline of the axis.
|
|
85
|
+
*
|
|
86
|
+
* @default 'auto'
|
|
87
|
+
*/
|
|
88
|
+
dominantBaseline?: 'auto' | 'text-before-edge' | 'text-after-edge' | 'middle' | 'hanging' | 'ideographic' | 'mathematical';
|
|
83
89
|
/**
|
|
84
90
|
* Rotational angle of the text
|
|
85
91
|
*/
|
|
@@ -34,18 +34,14 @@
|
|
|
34
34
|
import { Button, Icon, MenuButton, Tooltip } from 'svelte-ux';
|
|
35
35
|
import { cls } from '@layerstack/tailwind';
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
import
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
mdiResize,
|
|
46
|
-
mdiArrowExpandAll,
|
|
47
|
-
mdiCancel,
|
|
48
|
-
} from '@mdi/js';
|
|
37
|
+
import LucideFocus from '~icons/lucide/focus';
|
|
38
|
+
import LucideChevronDown from '~icons/lucide/chevron-down';
|
|
39
|
+
import LucideCircleOff from '~icons/lucide/circle-off';
|
|
40
|
+
import LucideImageUpscale from '~icons/lucide/image-upscale';
|
|
41
|
+
import LucideMove from '~icons/lucide/move';
|
|
42
|
+
import LucideUndo2 from '~icons/lucide/undo-2';
|
|
43
|
+
import LucideZoomIn from '~icons/lucide/zoom-in';
|
|
44
|
+
import LucideZoomOut from '~icons/lucide/zoom-out';
|
|
49
45
|
|
|
50
46
|
import { getTransformContext } from './TransformContext.svelte';
|
|
51
47
|
import type { Without } from '../utils/types.js';
|
|
@@ -113,7 +109,7 @@
|
|
|
113
109
|
{#if show.includes('zoomIn')}
|
|
114
110
|
<Tooltip title="Zoom in">
|
|
115
111
|
<Button
|
|
116
|
-
icon={
|
|
112
|
+
icon={LucideZoomIn}
|
|
117
113
|
on:click={() => transform.zoomIn()}
|
|
118
114
|
{size}
|
|
119
115
|
class="text-surface-content p-2"
|
|
@@ -124,7 +120,7 @@
|
|
|
124
120
|
{#if show.includes('zoomOut')}
|
|
125
121
|
<Tooltip title="Zoom out">
|
|
126
122
|
<Button
|
|
127
|
-
icon={
|
|
123
|
+
icon={LucideZoomOut}
|
|
128
124
|
on:click={() => transform.zoomOut()}
|
|
129
125
|
{size}
|
|
130
126
|
class="text-surface-content p-2"
|
|
@@ -135,7 +131,7 @@
|
|
|
135
131
|
{#if show.includes('center')}
|
|
136
132
|
<Tooltip title="Center">
|
|
137
133
|
<Button
|
|
138
|
-
icon={
|
|
134
|
+
icon={LucideFocus}
|
|
139
135
|
on:click={() => transform.translateCenter()}
|
|
140
136
|
{size}
|
|
141
137
|
class="text-surface-content p-2"
|
|
@@ -146,7 +142,7 @@
|
|
|
146
142
|
{#if show.includes('reset')}
|
|
147
143
|
<Tooltip title="Reset">
|
|
148
144
|
<Button
|
|
149
|
-
icon={
|
|
145
|
+
icon={LucideUndo2}
|
|
150
146
|
on:click={() => transform.reset()}
|
|
151
147
|
{size}
|
|
152
148
|
class="text-surface-content p-2"
|
|
@@ -159,9 +155,9 @@
|
|
|
159
155
|
<MenuButton
|
|
160
156
|
iconOnly
|
|
161
157
|
options={[
|
|
162
|
-
{ label: 'None', value: 'none', icon:
|
|
163
|
-
{ label: 'Zoom', value: 'scale', icon:
|
|
164
|
-
{ label: 'Move', value: 'translate', icon:
|
|
158
|
+
{ label: 'None', value: 'none', icon: LucideCircleOff },
|
|
159
|
+
{ label: 'Zoom', value: 'scale', icon: LucideImageUpscale },
|
|
160
|
+
{ label: 'Move', value: 'translate', icon: LucideMove },
|
|
165
161
|
]}
|
|
166
162
|
menuProps={{ placement: menuPlacementByOrientationAndPlacement[orientation][placement] }}
|
|
167
163
|
menuIcon={null}
|
|
@@ -171,7 +167,7 @@
|
|
|
171
167
|
class="text-surface-content"
|
|
172
168
|
>
|
|
173
169
|
<svelte:fragment slot="selection" let:value>
|
|
174
|
-
<Icon data={value?.icon ??
|
|
170
|
+
<Icon data={value?.icon ?? LucideChevronDown} />
|
|
175
171
|
</svelte:fragment>
|
|
176
172
|
</MenuButton>
|
|
177
173
|
</Tooltip>
|
|
@@ -18,53 +18,53 @@
|
|
|
18
18
|
*
|
|
19
19
|
* @default 0
|
|
20
20
|
*/
|
|
21
|
-
padding?: number;
|
|
21
|
+
padding?: number | ((node: HierarchyRectangularNode<T>) => number);
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* The inner padding between nodes.
|
|
25
25
|
*
|
|
26
26
|
* @default 0
|
|
27
27
|
*/
|
|
28
|
-
paddingInner?: number;
|
|
28
|
+
paddingInner?: number | ((node: HierarchyRectangularNode<T>) => number);
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* The outer padding between nodes.
|
|
32
32
|
*
|
|
33
33
|
* @default 0
|
|
34
34
|
*/
|
|
35
|
-
paddingOuter?: number;
|
|
35
|
+
paddingOuter?: number | ((node: HierarchyRectangularNode<T>) => number);
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* The top padding between nodes.
|
|
39
39
|
*
|
|
40
40
|
* @default 0
|
|
41
41
|
*/
|
|
42
|
-
paddingTop?: number;
|
|
42
|
+
paddingTop?: number | ((node: HierarchyRectangularNode<T>) => number);
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* The bottom padding between nodes.
|
|
46
46
|
*
|
|
47
47
|
* @default 0
|
|
48
48
|
*/
|
|
49
|
-
paddingBottom?: number;
|
|
49
|
+
paddingBottom?: number | ((node: HierarchyRectangularNode<T>) => number);
|
|
50
50
|
/**
|
|
51
51
|
* The left padding between nodes.
|
|
52
52
|
*
|
|
53
53
|
*/
|
|
54
|
-
paddingLeft?: number;
|
|
54
|
+
paddingLeft?: number | ((node: HierarchyRectangularNode<T>) => number);
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* The right padding between nodes.
|
|
58
58
|
*
|
|
59
59
|
*/
|
|
60
|
-
paddingRight?: number;
|
|
60
|
+
paddingRight?: number | ((node: HierarchyRectangularNode<T>) => number);
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* Modify tiling function for approapriate aspect ratio when treemap is zoomed in
|
|
64
64
|
*
|
|
65
|
-
* @default
|
|
65
|
+
* @default false
|
|
66
66
|
*/
|
|
67
|
-
|
|
67
|
+
maintainAspectRatio?: boolean;
|
|
68
68
|
|
|
69
69
|
hierarchy?: HierarchyNode<T>;
|
|
70
70
|
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
paddingBottom = 0,
|
|
100
100
|
paddingLeft,
|
|
101
101
|
paddingRight,
|
|
102
|
-
|
|
102
|
+
maintainAspectRatio = false,
|
|
103
103
|
children,
|
|
104
104
|
}: TreemapProps<T> = $props();
|
|
105
105
|
|
|
@@ -121,45 +121,82 @@
|
|
|
121
121
|
: tile
|
|
122
122
|
);
|
|
123
123
|
|
|
124
|
-
const
|
|
124
|
+
const treemapData = $derived.by(() => {
|
|
125
125
|
const _treemap = d3treemap<T>()
|
|
126
126
|
.size([ctx.width, ctx.height])
|
|
127
|
-
.tile(aspectTile(tileFunc, ctx.width, ctx.height));
|
|
127
|
+
.tile(maintainAspectRatio ? aspectTile(tileFunc, ctx.width, ctx.height) : tileFunc);
|
|
128
128
|
|
|
129
129
|
if (padding) {
|
|
130
|
-
|
|
130
|
+
// Make Typescript happy to pick the correct overload
|
|
131
|
+
// TODO: Better way to do this?
|
|
132
|
+
if (typeof padding === 'number') {
|
|
133
|
+
_treemap.padding(padding);
|
|
134
|
+
} else {
|
|
135
|
+
_treemap.padding(padding);
|
|
136
|
+
}
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
if (paddingInner) {
|
|
134
|
-
|
|
140
|
+
if (typeof paddingInner === 'number') {
|
|
141
|
+
_treemap.paddingInner(typeof paddingInner === 'number' ? paddingInner : paddingInner);
|
|
142
|
+
} else {
|
|
143
|
+
_treemap.paddingInner(paddingInner);
|
|
144
|
+
}
|
|
135
145
|
}
|
|
136
146
|
|
|
137
147
|
if (paddingOuter) {
|
|
138
|
-
|
|
148
|
+
if (typeof paddingOuter === 'number') {
|
|
149
|
+
_treemap.paddingOuter(paddingOuter);
|
|
150
|
+
} else {
|
|
151
|
+
_treemap.paddingOuter(paddingOuter);
|
|
152
|
+
}
|
|
139
153
|
}
|
|
140
154
|
|
|
141
155
|
if (paddingTop) {
|
|
142
|
-
|
|
156
|
+
if (typeof paddingTop === 'number') {
|
|
157
|
+
_treemap.paddingTop(paddingTop);
|
|
158
|
+
} else {
|
|
159
|
+
_treemap.paddingTop(paddingTop);
|
|
160
|
+
}
|
|
143
161
|
}
|
|
144
162
|
|
|
145
163
|
if (paddingBottom) {
|
|
146
|
-
|
|
164
|
+
if (typeof paddingBottom === 'number') {
|
|
165
|
+
_treemap.paddingBottom(paddingBottom);
|
|
166
|
+
} else {
|
|
167
|
+
_treemap.paddingBottom(paddingBottom);
|
|
168
|
+
}
|
|
147
169
|
}
|
|
148
170
|
|
|
149
171
|
if (paddingLeft) {
|
|
150
|
-
|
|
172
|
+
if (typeof paddingLeft === 'number') {
|
|
173
|
+
_treemap.paddingLeft(paddingLeft);
|
|
174
|
+
} else {
|
|
175
|
+
_treemap.paddingLeft(paddingLeft);
|
|
176
|
+
}
|
|
151
177
|
}
|
|
152
178
|
if (paddingRight) {
|
|
153
|
-
|
|
179
|
+
if (typeof paddingRight === 'number') {
|
|
180
|
+
_treemap.paddingRight(paddingRight);
|
|
181
|
+
} else {
|
|
182
|
+
_treemap.paddingRight(paddingRight);
|
|
183
|
+
}
|
|
154
184
|
}
|
|
155
|
-
return _treemap;
|
|
156
|
-
});
|
|
157
185
|
|
|
158
|
-
|
|
186
|
+
if (hierarchy) {
|
|
187
|
+
const h = hierarchy.copy();
|
|
188
|
+
const treemapData = _treemap(h);
|
|
189
|
+
return {
|
|
190
|
+
links: treemapData.links(),
|
|
191
|
+
nodes: treemapData.descendants(),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
159
194
|
|
|
160
|
-
|
|
161
|
-
|
|
195
|
+
return {
|
|
196
|
+
links: [],
|
|
197
|
+
nodes: [],
|
|
198
|
+
};
|
|
162
199
|
});
|
|
163
200
|
</script>
|
|
164
201
|
|
|
165
|
-
{@render children?.({ nodes: treemapData
|
|
202
|
+
{@render children?.({ nodes: treemapData.nodes })}
|