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
package/dist/components/index.js
CHANGED
|
@@ -42,6 +42,8 @@ export { default as Connector } from './Connector.svelte';
|
|
|
42
42
|
export * from './Connector.svelte';
|
|
43
43
|
export { default as Dagre } from './Dagre.svelte';
|
|
44
44
|
export * from './Dagre.svelte';
|
|
45
|
+
export { default as Ellipse } from './Ellipse.svelte';
|
|
46
|
+
export * from './Ellipse.svelte';
|
|
45
47
|
export { default as Frame } from './Frame.svelte';
|
|
46
48
|
export * from './Frame.svelte';
|
|
47
49
|
export { default as ForceSimulation } from './ForceSimulation.svelte';
|
|
@@ -102,6 +104,8 @@ export { default as Point } from './Point.svelte';
|
|
|
102
104
|
export * from './Point.svelte';
|
|
103
105
|
export { default as Points } from './Points.svelte';
|
|
104
106
|
export * from './Points.svelte';
|
|
107
|
+
export { default as Polygon } from './Polygon.svelte';
|
|
108
|
+
export * from './Polygon.svelte';
|
|
105
109
|
export { default as RadialGradient } from './RadialGradient.svelte';
|
|
106
110
|
export * from './RadialGradient.svelte';
|
|
107
111
|
export { default as Rect } from './Rect.svelte';
|
|
@@ -124,7 +128,7 @@ export * as Tooltip from './tooltip/index.js';
|
|
|
124
128
|
export * from './tooltip/TooltipContext.svelte';
|
|
125
129
|
export { default as TransformContext } from './TransformContext.svelte';
|
|
126
130
|
export * from './TransformContext.svelte';
|
|
127
|
-
// export { default as TransformControls } from './TransformControls.svelte'; // TODO: Restore once no longer using `
|
|
131
|
+
// export { default as TransformControls } from './TransformControls.svelte'; // TODO: Restore once no longer using `svelte-ux` or `~icons` (as they are dev dependencies)
|
|
128
132
|
export { default as Tree } from './Tree.svelte';
|
|
129
133
|
export * from './Tree.svelte';
|
|
130
134
|
export { default as Treemap } from './Treemap.svelte';
|
|
@@ -64,6 +64,13 @@
|
|
|
64
64
|
*/
|
|
65
65
|
ignoreTransform?: boolean;
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Disable the hit canvas (useful when animations are playing)
|
|
69
|
+
*
|
|
70
|
+
* @default false
|
|
71
|
+
*/
|
|
72
|
+
disableHitCanvas?: boolean;
|
|
73
|
+
|
|
67
74
|
/**
|
|
68
75
|
* Show the hit canvas for debugging purposes.
|
|
69
76
|
*
|
|
@@ -144,13 +151,13 @@
|
|
|
144
151
|
import { onMount, untrack, type Snippet } from 'svelte';
|
|
145
152
|
import { cls } from '@layerstack/tailwind';
|
|
146
153
|
import { Logger, localPoint } from '@layerstack/utils';
|
|
147
|
-
import {
|
|
154
|
+
import { MediaQueryPresets } from '@layerstack/svelte-state';
|
|
148
155
|
|
|
149
156
|
import { setRenderContext } from '../Chart.svelte';
|
|
150
157
|
import { getTransformContext } from '../TransformContext.svelte';
|
|
151
158
|
import { getPixelColor, scaleCanvas, type ComputedStylesOptions } from '../../utils/canvas.js';
|
|
152
159
|
import { getColorStr, rgbColorGenerator } from '../../utils/color.js';
|
|
153
|
-
import { Context } from 'runed';
|
|
160
|
+
import { Context, useMutationObserver, watch } from 'runed';
|
|
154
161
|
import type {
|
|
155
162
|
HTMLCanvasAttributes,
|
|
156
163
|
MouseEventHandler,
|
|
@@ -171,6 +178,7 @@
|
|
|
171
178
|
fallback,
|
|
172
179
|
center = false,
|
|
173
180
|
ignoreTransform = false,
|
|
181
|
+
disableHitCanvas = false,
|
|
174
182
|
class: className,
|
|
175
183
|
children,
|
|
176
184
|
onclick,
|
|
@@ -256,6 +264,23 @@
|
|
|
256
264
|
* end HitCanvas
|
|
257
265
|
*/
|
|
258
266
|
|
|
267
|
+
// Invalidate/redraw if color scheme changes, either via browser `prefers-color-scheme` (including emulation) or by changing `<html class="dark">` or `<html data-theme="...">`
|
|
268
|
+
const { dark } = new MediaQueryPresets();
|
|
269
|
+
watch(
|
|
270
|
+
() => dark.current,
|
|
271
|
+
() => {
|
|
272
|
+
canvasContext.invalidate();
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
useMutationObserver(
|
|
276
|
+
() => document.documentElement,
|
|
277
|
+
() => canvasContext.invalidate(),
|
|
278
|
+
{
|
|
279
|
+
attributes: true,
|
|
280
|
+
attributeFilter: ['class', 'data-theme'],
|
|
281
|
+
}
|
|
282
|
+
);
|
|
283
|
+
|
|
259
284
|
onMount(() => {
|
|
260
285
|
context = ref?.getContext('2d', { willReadFrequently }) as CanvasRenderingContext2D;
|
|
261
286
|
|
|
@@ -263,22 +288,7 @@
|
|
|
263
288
|
willReadFrequently: false, // Explicitly set to `false` to resolve pixel artifacts between fill and stroke with the same color (issue #372)
|
|
264
289
|
}) as CanvasRenderingContext2D;
|
|
265
290
|
|
|
266
|
-
// Invalidate/redraw if color scheme changes, either via browser `prefers-color-scheme` (including emulation) or by changing `<html class="dark">` or `<html data-theme="...">`
|
|
267
|
-
darkColorScheme.subscribe(() => {
|
|
268
|
-
canvasContext.invalidate();
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const observer = new MutationObserver(() => {
|
|
272
|
-
canvasContext.invalidate();
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
observer.observe(document.documentElement, {
|
|
276
|
-
attributes: true,
|
|
277
|
-
attributeFilter: ['class', 'data-theme'],
|
|
278
|
-
});
|
|
279
|
-
|
|
280
291
|
return () => {
|
|
281
|
-
observer.disconnect();
|
|
282
292
|
if (frameId) {
|
|
283
293
|
cancelAnimationFrame(frameId);
|
|
284
294
|
}
|
|
@@ -336,45 +346,53 @@
|
|
|
336
346
|
context.restore();
|
|
337
347
|
}
|
|
338
348
|
|
|
339
|
-
|
|
349
|
+
/*
|
|
350
|
+
* Sync hit canvas with main canvas
|
|
351
|
+
*/
|
|
340
352
|
if (hitCanvasContext) {
|
|
341
|
-
// scale hit canvas to match main canvas
|
|
342
|
-
scaleCanvas(hitCanvasContext, ctx.containerWidth, ctx.containerHeight);
|
|
343
|
-
hitCanvasContext.clearRect(0, 0, ctx.containerWidth, ctx.containerHeight);
|
|
344
|
-
|
|
345
|
-
// reset and sync transform to the state after retainState components
|
|
346
|
-
hitCanvasContext.resetTransform();
|
|
347
|
-
hitCanvasContext.setTransform(mainTransformAfterRetain);
|
|
348
|
-
|
|
349
|
-
// reset color generator
|
|
350
|
-
colorGenerator = rgbColorGenerator();
|
|
351
|
-
|
|
352
353
|
const inactiveMoving = !activeCanvas && transformCtx.moving;
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
354
|
+
if (disableHitCanvas || transformCtx.dragging || inactiveMoving) {
|
|
355
|
+
// Skip rendering hit canvas
|
|
356
|
+
hitCanvasContext.clearRect(0, 0, ctx.containerWidth, ctx.containerHeight);
|
|
357
|
+
} else {
|
|
358
|
+
// scale hit canvas to match main canvas
|
|
359
|
+
scaleCanvas(hitCanvasContext, ctx.containerWidth, ctx.containerHeight);
|
|
360
|
+
hitCanvasContext.clearRect(0, 0, ctx.containerWidth, ctx.containerHeight);
|
|
361
|
+
|
|
362
|
+
// reset and sync transform to the state after retainState components
|
|
363
|
+
hitCanvasContext.resetTransform();
|
|
364
|
+
hitCanvasContext.setTransform(mainTransformAfterRetain);
|
|
365
|
+
|
|
366
|
+
// reset color generator
|
|
367
|
+
colorGenerator = rgbColorGenerator();
|
|
368
|
+
|
|
369
|
+
// render retainState components on hit canvas (e.g., Group)
|
|
370
|
+
for (const c of retainStateComponents) {
|
|
371
|
+
const componentHasEvents =
|
|
372
|
+
c.events && Object.values(c.events).filter((d) => d).length > 0;
|
|
373
|
+
|
|
374
|
+
if (componentHasEvents) {
|
|
375
|
+
// since the transform was already applied via setTransform, skip rendering
|
|
376
|
+
// the retainState component's transform again; proceed to its children
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
362
379
|
}
|
|
363
|
-
}
|
|
364
380
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
381
|
+
// render non-retainState components on hit canvas
|
|
382
|
+
for (const c of nonRetainStateComponents) {
|
|
383
|
+
const componentHasEvents =
|
|
384
|
+
c.events && Object.values(c.events).filter((d) => d).length > 0;
|
|
368
385
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
386
|
+
if (componentHasEvents) {
|
|
387
|
+
const color = getColorStr(colorGenerator.next().value);
|
|
388
|
+
const styleOverrides = { styles: { fill: color, stroke: color, _fillOpacity: 0.1 } };
|
|
372
389
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
390
|
+
hitCanvasContext.save();
|
|
391
|
+
c.render(hitCanvasContext, styleOverrides);
|
|
392
|
+
hitCanvasContext.restore();
|
|
376
393
|
|
|
377
|
-
|
|
394
|
+
componentByColor.set(color, c);
|
|
395
|
+
}
|
|
378
396
|
}
|
|
379
397
|
}
|
|
380
398
|
}
|
|
@@ -55,6 +55,12 @@ export type CanvasPropsWithoutHTML = {
|
|
|
55
55
|
* @default false
|
|
56
56
|
*/
|
|
57
57
|
ignoreTransform?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Disable the hit canvas (useful when animations are playing)
|
|
60
|
+
*
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
disableHitCanvas?: boolean;
|
|
58
64
|
/**
|
|
59
65
|
* Show the hit canvas for debugging purposes.
|
|
60
66
|
*
|
|
@@ -3,17 +3,19 @@
|
|
|
3
3
|
|
|
4
4
|
export type CanvasLayerProps = {
|
|
5
5
|
type: 'canvas';
|
|
6
|
-
} & Omit<ComponentProps<typeof Canvas>, 'type'>;
|
|
6
|
+
} & Omit<ComponentProps<typeof Canvas>, 'type' | 'onpointermove'>;
|
|
7
7
|
|
|
8
8
|
export type HtmlLayerProps = {
|
|
9
9
|
type: 'html';
|
|
10
|
-
} & Omit<ComponentProps<typeof Html>, 'type'>;
|
|
10
|
+
} & Omit<ComponentProps<typeof Html>, 'type' | 'onpointermove'>;
|
|
11
11
|
|
|
12
12
|
export type SvgLayerProps = {
|
|
13
13
|
type: 'svg';
|
|
14
|
-
} & Omit<ComponentProps<typeof Svg>, 'type'>;
|
|
14
|
+
} & Omit<ComponentProps<typeof Svg>, 'type' | 'onpointermove'>;
|
|
15
15
|
|
|
16
|
-
export type LayerProps = CanvasLayerProps | HtmlLayerProps | SvgLayerProps
|
|
16
|
+
export type LayerProps = (CanvasLayerProps | HtmlLayerProps | SvgLayerProps) & {
|
|
17
|
+
onpointermove?: (e: PointerEvent) => void;
|
|
18
|
+
};
|
|
17
19
|
</script>
|
|
18
20
|
|
|
19
21
|
<script lang="ts">
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { ComponentProps } from 'svelte';
|
|
2
2
|
export type CanvasLayerProps = {
|
|
3
3
|
type: 'canvas';
|
|
4
|
-
} & Omit<ComponentProps<typeof Canvas>, 'type'>;
|
|
4
|
+
} & Omit<ComponentProps<typeof Canvas>, 'type' | 'onpointermove'>;
|
|
5
5
|
export type HtmlLayerProps = {
|
|
6
6
|
type: 'html';
|
|
7
|
-
} & Omit<ComponentProps<typeof Html>, 'type'>;
|
|
7
|
+
} & Omit<ComponentProps<typeof Html>, 'type' | 'onpointermove'>;
|
|
8
8
|
export type SvgLayerProps = {
|
|
9
9
|
type: 'svg';
|
|
10
|
-
} & Omit<ComponentProps<typeof Svg>, 'type'>;
|
|
11
|
-
export type LayerProps = CanvasLayerProps | HtmlLayerProps | SvgLayerProps
|
|
10
|
+
} & Omit<ComponentProps<typeof Svg>, 'type' | 'onpointermove'>;
|
|
11
|
+
export type LayerProps = (CanvasLayerProps | HtmlLayerProps | SvgLayerProps) & {
|
|
12
|
+
onpointermove?: (e: PointerEvent) => void;
|
|
13
|
+
};
|
|
12
14
|
import Canvas from './Canvas.svelte';
|
|
13
15
|
import Html from './Html.svelte';
|
|
14
16
|
import Svg from './Svg.svelte';
|
|
@@ -359,13 +359,8 @@
|
|
|
359
359
|
{#if tooltipCtx.data}
|
|
360
360
|
<div
|
|
361
361
|
{...props.root}
|
|
362
|
-
class={cls(
|
|
363
|
-
|
|
364
|
-
'absolute z-50 select-none',
|
|
365
|
-
!pointerEvents && 'pointer-events-none',
|
|
366
|
-
classes.root,
|
|
367
|
-
props.root?.class
|
|
368
|
-
)}
|
|
362
|
+
class={cls('root', layerClass('tooltip-root'), classes.root, props.root?.class)}
|
|
363
|
+
class:pointer-events-none={!pointerEvents}
|
|
369
364
|
style:top="{motionY.current}px"
|
|
370
365
|
style:left="{motionX.current}px"
|
|
371
366
|
transition:fade={{ duration: 100 }}
|
|
@@ -408,3 +403,15 @@
|
|
|
408
403
|
</div>
|
|
409
404
|
</div>
|
|
410
405
|
{/if}
|
|
406
|
+
|
|
407
|
+
<style>
|
|
408
|
+
.root {
|
|
409
|
+
position: absolute;
|
|
410
|
+
z-index: 50;
|
|
411
|
+
user-select: none;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.pointer-events-none {
|
|
415
|
+
pointer-events: none;
|
|
416
|
+
}
|
|
417
|
+
</style>
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
const _TooltipContext = new Context<TooltipContextValue>('TooltipContext');
|
|
7
7
|
|
|
8
8
|
type TooltipMode =
|
|
9
|
-
| 'bisect-x'
|
|
10
|
-
| 'bisect-y'
|
|
9
|
+
| 'bisect-x' // requires values to be sorted
|
|
10
|
+
| 'bisect-y' // requires values to be sorted
|
|
11
11
|
| 'band'
|
|
12
|
-
| 'bisect-band'
|
|
12
|
+
| 'bisect-band' // requires values to be sorted
|
|
13
13
|
| 'bounds'
|
|
14
14
|
| 'voronoi'
|
|
15
15
|
| 'quadtree'
|
|
16
|
+
| 'quadtree-x' // ignores y values (constant 0)
|
|
17
|
+
| 'quadtree-y' // ignores x values (constant 0)
|
|
16
18
|
| 'manual';
|
|
17
19
|
|
|
18
20
|
export type TooltipContextValue<T = any> = {
|
|
@@ -20,7 +22,11 @@
|
|
|
20
22
|
y: number;
|
|
21
23
|
data: T | null;
|
|
22
24
|
payload: TooltipPayload[];
|
|
23
|
-
show(
|
|
25
|
+
show(
|
|
26
|
+
e: PointerEvent | MouseEvent | TouchEvent,
|
|
27
|
+
tooltipData?: any,
|
|
28
|
+
payload?: TooltipPayload
|
|
29
|
+
): void;
|
|
24
30
|
hide(e?: PointerEvent): void;
|
|
25
31
|
mode: TooltipMode;
|
|
26
32
|
isHoveringTooltipArea: boolean;
|
|
@@ -72,7 +78,7 @@
|
|
|
72
78
|
locked?: boolean;
|
|
73
79
|
|
|
74
80
|
/**
|
|
75
|
-
* quadtree search radius
|
|
81
|
+
* quadtree search or voronoi clip radius
|
|
76
82
|
* @default Infinity
|
|
77
83
|
*/
|
|
78
84
|
radius?: number;
|
|
@@ -116,11 +122,14 @@
|
|
|
116
122
|
</script>
|
|
117
123
|
|
|
118
124
|
<script lang="ts" generics="TData = any">
|
|
125
|
+
import type { Snippet } from 'svelte';
|
|
119
126
|
import { bisector, max, min } from 'd3-array';
|
|
120
127
|
import { quadtree as d3Quadtree, type Quadtree } from 'd3-quadtree';
|
|
121
128
|
import { sortFunc, localPoint } from '@layerstack/utils';
|
|
122
129
|
import { cls } from '@layerstack/tailwind';
|
|
123
130
|
|
|
131
|
+
import { getChartContext } from '../Chart.svelte';
|
|
132
|
+
import { getGeoContext } from '../GeoContext.svelte';
|
|
124
133
|
import Svg from './../layout/Svg.svelte';
|
|
125
134
|
import Arc from '../Arc.svelte';
|
|
126
135
|
import ChartClipPath from './../ChartClipPath.svelte';
|
|
@@ -130,8 +139,6 @@
|
|
|
130
139
|
import { cartesianToPolar } from '../../utils/math.js';
|
|
131
140
|
import { quadtreeRects } from '../../utils/quadtree.js';
|
|
132
141
|
import { raise } from '../../utils/chart.js';
|
|
133
|
-
import { getChartContext } from '../Chart.svelte';
|
|
134
|
-
import type { Snippet } from 'svelte';
|
|
135
142
|
import {
|
|
136
143
|
getTooltipMetaContext,
|
|
137
144
|
getTooltipPayload,
|
|
@@ -140,6 +147,7 @@
|
|
|
140
147
|
import { layerClass } from '../../utils/attributes.js';
|
|
141
148
|
|
|
142
149
|
const ctx = getChartContext<any>();
|
|
150
|
+
const geoCtx = getGeoContext();
|
|
143
151
|
|
|
144
152
|
let {
|
|
145
153
|
ref: refProp = $bindable(),
|
|
@@ -268,7 +276,7 @@
|
|
|
268
276
|
}
|
|
269
277
|
}
|
|
270
278
|
|
|
271
|
-
function showTooltip(e: PointerEvent, tooltipData?: any) {
|
|
279
|
+
function showTooltip(e: PointerEvent | MouseEvent | TouchEvent, tooltipData?: any) {
|
|
272
280
|
// Cancel hiding tooltip if from previous event loop
|
|
273
281
|
if (hideTimeoutId) {
|
|
274
282
|
clearTimeout(hideTimeoutId);
|
|
@@ -296,7 +304,6 @@
|
|
|
296
304
|
}
|
|
297
305
|
|
|
298
306
|
// If tooltipData not provided already (voronoi, etc), attempt to find it
|
|
299
|
-
// TODO: When using bisect-x/y/band, values should be sorted. Typically they are for `x`, but not `y` (and band depends on if x or y scale)
|
|
300
307
|
if (tooltipData == null) {
|
|
301
308
|
switch (mode) {
|
|
302
309
|
case 'bisect-x': {
|
|
@@ -309,6 +316,7 @@
|
|
|
309
316
|
xValueAtPoint = scaleInvert(ctx.xScale, point.x - ctx.padding.left);
|
|
310
317
|
}
|
|
311
318
|
|
|
319
|
+
// Requires values to be sorted
|
|
312
320
|
const index = bisectX(ctx.flatData, xValueAtPoint, 1);
|
|
313
321
|
const previousValue = ctx.flatData[index - 1];
|
|
314
322
|
const currentValue = ctx.flatData[index];
|
|
@@ -320,6 +328,7 @@
|
|
|
320
328
|
// `y` value at pointer coordinate
|
|
321
329
|
const yValueAtPoint = scaleInvert(ctx.yScale, point.y - ctx.padding.top);
|
|
322
330
|
|
|
331
|
+
// Requires values to be sorted
|
|
323
332
|
const index = bisectY(ctx.flatData, yValueAtPoint, 1);
|
|
324
333
|
const previousValue = ctx.flatData[index - 1];
|
|
325
334
|
const currentValue = ctx.flatData[index];
|
|
@@ -337,6 +346,7 @@
|
|
|
337
346
|
const bandData = ctx.flatData
|
|
338
347
|
.filter((d) => ctx.x(d) === xValueAtPoint)
|
|
339
348
|
.sort(sortFunc(ctx.y as () => any)); // sort for bisect
|
|
349
|
+
// Requires values to be sorted
|
|
340
350
|
const index = bisectY(bandData, yValueAtPoint, 1);
|
|
341
351
|
const previousValue = bandData[index - 1];
|
|
342
352
|
const currentValue = bandData[index];
|
|
@@ -346,6 +356,7 @@
|
|
|
346
356
|
const bandData = ctx.flatData
|
|
347
357
|
.filter((d) => ctx.y(d) === yValueAtPoint)
|
|
348
358
|
.sort(sortFunc(ctx.x as () => any)); // sort for bisect
|
|
359
|
+
// Requires values to be sorted
|
|
349
360
|
const index = bisectX(bandData, xValueAtPoint, 1);
|
|
350
361
|
const previousValue = bandData[index - 1];
|
|
351
362
|
const currentValue = bandData[index];
|
|
@@ -356,8 +367,14 @@
|
|
|
356
367
|
break;
|
|
357
368
|
}
|
|
358
369
|
|
|
370
|
+
case 'quadtree-x':
|
|
371
|
+
case 'quadtree-y':
|
|
359
372
|
case 'quadtree': {
|
|
360
|
-
tooltipData = quadtree?.find(
|
|
373
|
+
tooltipData = quadtree?.find(
|
|
374
|
+
point.x - ctx.padding.left,
|
|
375
|
+
point.y - ctx.padding.top,
|
|
376
|
+
radius
|
|
377
|
+
);
|
|
361
378
|
break;
|
|
362
379
|
}
|
|
363
380
|
}
|
|
@@ -400,13 +417,20 @@
|
|
|
400
417
|
}
|
|
401
418
|
|
|
402
419
|
const quadtree: Quadtree<[number, number]> | undefined = $derived.by(() => {
|
|
403
|
-
if (
|
|
420
|
+
if (['quadtree', 'quadtree-x', 'quadtree-y'].includes(mode)) {
|
|
404
421
|
return d3Quadtree()
|
|
405
|
-
.extent([
|
|
406
|
-
[0, 0],
|
|
407
|
-
[ctx.width, ctx.height],
|
|
408
|
-
])
|
|
409
422
|
.x((d) => {
|
|
423
|
+
if (mode === 'quadtree-y') {
|
|
424
|
+
return 0;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (geoCtx.projection) {
|
|
428
|
+
const lat = ctx.x(d);
|
|
429
|
+
const long = ctx.y(d);
|
|
430
|
+
const geoValue = geoCtx.projection([lat, long]) ?? [0, 0];
|
|
431
|
+
return geoValue[0];
|
|
432
|
+
}
|
|
433
|
+
|
|
410
434
|
const value = ctx.xGet(d);
|
|
411
435
|
|
|
412
436
|
if (Array.isArray(value)) {
|
|
@@ -420,6 +444,17 @@
|
|
|
420
444
|
}
|
|
421
445
|
})
|
|
422
446
|
.y((d) => {
|
|
447
|
+
if (mode === 'quadtree-x') {
|
|
448
|
+
return 0;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (geoCtx.projection) {
|
|
452
|
+
const lat = ctx.x(d);
|
|
453
|
+
const long = ctx.y(d);
|
|
454
|
+
const geoValue = geoCtx.projection([lat, long]) ?? [0, 0];
|
|
455
|
+
return geoValue[1];
|
|
456
|
+
}
|
|
457
|
+
|
|
423
458
|
const value = ctx.yGet(d);
|
|
424
459
|
|
|
425
460
|
if (Array.isArray(value)) {
|
|
@@ -493,8 +528,26 @@
|
|
|
493
528
|
});
|
|
494
529
|
|
|
495
530
|
const triggerPointerEvents = $derived(
|
|
496
|
-
['bisect-x', 'bisect-y', 'bisect-band', 'quadtree'].includes(mode)
|
|
531
|
+
['bisect-x', 'bisect-y', 'bisect-band', 'quadtree', 'quadtree-x', 'quadtree-y'].includes(mode)
|
|
497
532
|
);
|
|
533
|
+
|
|
534
|
+
function onPointerEnter(e: PointerEvent | MouseEvent | TouchEvent) {
|
|
535
|
+
isHoveringTooltipArea = true;
|
|
536
|
+
if (triggerPointerEvents) {
|
|
537
|
+
showTooltip(e);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function onPointerMove(e: PointerEvent | MouseEvent | TouchEvent) {
|
|
542
|
+
if (triggerPointerEvents) {
|
|
543
|
+
showTooltip(e);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function onPointerLeave(e: PointerEvent | MouseEvent | TouchEvent) {
|
|
548
|
+
isHoveringTooltipArea = false;
|
|
549
|
+
hideTooltip();
|
|
550
|
+
}
|
|
498
551
|
</script>
|
|
499
552
|
|
|
500
553
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
@@ -505,25 +558,15 @@
|
|
|
505
558
|
style:height="{ctx.height}px"
|
|
506
559
|
class={cls(
|
|
507
560
|
layerClass('tooltip-context'),
|
|
508
|
-
'absolute
|
|
561
|
+
'absolute',
|
|
509
562
|
debug && triggerPointerEvents && 'bg-danger/10 outline outline-danger'
|
|
510
563
|
)}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
onpointermove={(e) => {
|
|
518
|
-
if (triggerPointerEvents) {
|
|
519
|
-
showTooltip(e);
|
|
520
|
-
}
|
|
521
|
-
}}
|
|
522
|
-
onpointerleave={(e) => {
|
|
523
|
-
isHoveringTooltipArea = false;
|
|
524
|
-
|
|
525
|
-
hideTooltip();
|
|
526
|
-
}}
|
|
564
|
+
onmouseenter={onPointerEnter}
|
|
565
|
+
ontouchstart={onPointerEnter}
|
|
566
|
+
onmousemove={onPointerMove}
|
|
567
|
+
ontouchmove={onPointerMove}
|
|
568
|
+
onmouseleave={onPointerLeave}
|
|
569
|
+
ontouchend={onPointerLeave}
|
|
527
570
|
onclick={(e) => {
|
|
528
571
|
// Ignore clicks without data (triggered from Legend clicks, for example)
|
|
529
572
|
if (triggerPointerEvents && tooltipContext.data != null) {
|
|
@@ -546,6 +589,7 @@
|
|
|
546
589
|
{#if mode === 'voronoi'}
|
|
547
590
|
<Svg>
|
|
548
591
|
<Voronoi
|
|
592
|
+
r={radius}
|
|
549
593
|
onpointerenter={(e, { data }) => {
|
|
550
594
|
showTooltip(e, data);
|
|
551
595
|
}}
|
|
@@ -621,7 +665,7 @@
|
|
|
621
665
|
{/each}
|
|
622
666
|
</g>
|
|
623
667
|
</Svg>
|
|
624
|
-
{:else if
|
|
668
|
+
{:else if ['quadtree', 'quadtree-x', 'quadtree-y'].includes(mode) && debug}
|
|
625
669
|
<Svg pointerEvents={false}>
|
|
626
670
|
<ChartClipPath>
|
|
627
671
|
<g class={layerClass('tooltip-quadtree-g')}>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
2
|
import type { Without } from '../../utils/types.js';
|
|
3
|
-
type TooltipMode = 'bisect-x' | 'bisect-y' | 'band' | 'bisect-band' | 'bounds' | 'voronoi' | 'quadtree' | 'manual';
|
|
3
|
+
type TooltipMode = 'bisect-x' | 'bisect-y' | 'band' | 'bisect-band' | 'bounds' | 'voronoi' | 'quadtree' | 'quadtree-x' | 'quadtree-y' | 'manual';
|
|
4
4
|
export type TooltipContextValue<T = any> = {
|
|
5
5
|
x: number;
|
|
6
6
|
y: number;
|
|
7
7
|
data: T | null;
|
|
8
8
|
payload: TooltipPayload[];
|
|
9
|
-
show(e: PointerEvent, tooltipData?: any, payload?: TooltipPayload): void;
|
|
9
|
+
show(e: PointerEvent | MouseEvent | TouchEvent, tooltipData?: any, payload?: TooltipPayload): void;
|
|
10
10
|
hide(e?: PointerEvent): void;
|
|
11
11
|
mode: TooltipMode;
|
|
12
12
|
isHoveringTooltipArea: boolean;
|
|
@@ -36,7 +36,7 @@ type TooltipContextPropsWithoutHTML<T = any> = {
|
|
|
36
36
|
*/
|
|
37
37
|
locked?: boolean;
|
|
38
38
|
/**
|
|
39
|
-
* quadtree search radius
|
|
39
|
+
* quadtree search or voronoi clip radius
|
|
40
40
|
* @default Infinity
|
|
41
41
|
*/
|
|
42
42
|
radius?: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
|
-
import type
|
|
4
|
+
import { asAny, type Without } from '../../utils/types.js';
|
|
5
5
|
|
|
6
6
|
export type TooltipHeaderPropsWithoutHTML = {
|
|
7
7
|
/**
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
/**
|
|
14
14
|
* The format to use when displaying the value.
|
|
15
15
|
*/
|
|
16
|
-
format?: FormatType;
|
|
16
|
+
format?: FormatType | FormatConfig;
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* The color to use for the color dot.
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
</script>
|
|
55
55
|
|
|
56
56
|
<script lang="ts">
|
|
57
|
-
import { format as formatUtil, type FormatType } from '@layerstack/utils';
|
|
57
|
+
import { format as formatUtil, type FormatType, type FormatConfig } from '@layerstack/utils';
|
|
58
58
|
import { cls } from '@layerstack/tailwind';
|
|
59
59
|
import { layerClass } from '../../utils/attributes.js';
|
|
60
60
|
|
|
@@ -114,6 +114,7 @@
|
|
|
114
114
|
{#if children}
|
|
115
115
|
{@render children?.()}
|
|
116
116
|
{:else}
|
|
117
|
-
|
|
117
|
+
<!-- @ts-expect-error - improve types -->
|
|
118
|
+
{format ? formatUtil(value, asAny(format)) : value}
|
|
118
119
|
{/if}
|
|
119
120
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
-
import type
|
|
3
|
+
import { type Without } from '../../utils/types.js';
|
|
4
4
|
export type TooltipHeaderPropsWithoutHTML = {
|
|
5
5
|
/**
|
|
6
6
|
* The value to display in the tooltip header when the `children`
|
|
@@ -10,7 +10,7 @@ export type TooltipHeaderPropsWithoutHTML = {
|
|
|
10
10
|
/**
|
|
11
11
|
* The format to use when displaying the value.
|
|
12
12
|
*/
|
|
13
|
-
format?: FormatType;
|
|
13
|
+
format?: FormatType | FormatConfig;
|
|
14
14
|
/**
|
|
15
15
|
* The color to use for the color dot.
|
|
16
16
|
*/
|
|
@@ -40,7 +40,7 @@ export type TooltipHeaderPropsWithoutHTML = {
|
|
|
40
40
|
children?: Snippet;
|
|
41
41
|
};
|
|
42
42
|
export type TooltipHeaderProps = TooltipHeaderPropsWithoutHTML & Without<HTMLAttributes<HTMLElement>, TooltipHeaderPropsWithoutHTML>;
|
|
43
|
-
import { type FormatType } from '@layerstack/utils';
|
|
43
|
+
import { type FormatType, type FormatConfig } from '@layerstack/utils';
|
|
44
44
|
declare const TooltipHeader: import("svelte").Component<TooltipHeaderProps, {}, "ref" | "colorRef">;
|
|
45
45
|
type TooltipHeader = ReturnType<typeof TooltipHeader>;
|
|
46
46
|
export default TooltipHeader;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
-
import type
|
|
3
|
+
import { asAny, type Without } from '../../utils/types.js';
|
|
4
4
|
|
|
5
5
|
export type TooltipItemPropsWithoutHTML = {
|
|
6
6
|
/**
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
/**
|
|
17
17
|
* Format to use when displaying the value.
|
|
18
18
|
*/
|
|
19
|
-
format?: FormatType;
|
|
19
|
+
format?: FormatType | FormatConfig;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Alignment of the value.
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
</script>
|
|
76
76
|
|
|
77
77
|
<script lang="ts">
|
|
78
|
-
import { format as formatUtil, type FormatType } from '@layerstack/utils';
|
|
78
|
+
import { format as formatUtil, type FormatType, type FormatConfig } from '@layerstack/utils';
|
|
79
79
|
import { cls } from '@layerstack/tailwind';
|
|
80
80
|
import type { Snippet } from 'svelte';
|
|
81
81
|
import { layerClass } from '../../utils/attributes.js';
|
|
@@ -189,7 +189,8 @@
|
|
|
189
189
|
{#if children}
|
|
190
190
|
{@render children()}
|
|
191
191
|
{:else}
|
|
192
|
-
|
|
192
|
+
<!-- @ts-expect-error - improve types -->
|
|
193
|
+
{format ? formatUtil(value, asAny(format)) : value}
|
|
193
194
|
{/if}
|
|
194
195
|
</div>
|
|
195
196
|
</div>
|