layerchart 2.0.0-next.2 → 2.0.0-next.4

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.
Files changed (32) hide show
  1. package/dist/components/AnnotationLine.svelte +17 -29
  2. package/dist/components/AnnotationLine.svelte.d.ts +4 -2
  3. package/dist/components/AnnotationPoint.svelte +11 -13
  4. package/dist/components/AnnotationPoint.svelte.d.ts +4 -2
  5. package/dist/components/Axis.svelte +10 -20
  6. package/dist/components/Axis.svelte.d.ts +2 -1
  7. package/dist/components/Bar.svelte +7 -4
  8. package/dist/components/Bar.svelte.d.ts +4 -1
  9. package/dist/components/Bars.svelte +9 -6
  10. package/dist/components/Bars.svelte.d.ts +3 -3
  11. package/dist/components/Blur.svelte +16 -10
  12. package/dist/components/ClipPath.svelte +14 -9
  13. package/dist/components/GeoEdgeFade.svelte +4 -3
  14. package/dist/components/GeoEdgeFade.svelte.d.ts +2 -2
  15. package/dist/components/Graticule.svelte +3 -2
  16. package/dist/components/Grid.svelte +8 -7
  17. package/dist/components/Grid.svelte.d.ts +2 -1
  18. package/dist/components/Group.svelte +45 -5
  19. package/dist/components/Group.svelte.d.ts +32 -4
  20. package/dist/components/Highlight.svelte +1 -1
  21. package/dist/components/Hull.svelte +4 -4
  22. package/dist/components/Hull.svelte.d.ts +2 -2
  23. package/dist/components/Labels.svelte +3 -2
  24. package/dist/components/Rule.svelte +3 -2
  25. package/dist/components/Voronoi.svelte +55 -36
  26. package/dist/components/Voronoi.svelte.d.ts +5 -3
  27. package/dist/components/charts/LineChart.svelte +0 -1
  28. package/dist/components/charts/utils.svelte.d.ts +1 -1
  29. package/dist/components/charts/utils.svelte.js +2 -2
  30. package/dist/components/tooltip/TooltipContext.svelte +25 -8
  31. package/dist/components/tooltip/TooltipContext.svelte.d.ts +1 -1
  32. package/package.json +3 -1
@@ -17,8 +17,11 @@
17
17
  /** Placement of the label */
18
18
  labelPlacement?: Placement;
19
19
 
20
- /** Offset of the label */
21
- labelOffset?: number;
20
+ /** X offset of the label */
21
+ labelXOffset?: number;
22
+
23
+ /** Y offset of the label */
24
+ labelYOffset?: number;
22
25
 
23
26
  /** Classes for inner elements */
24
27
  props?: {
@@ -44,7 +47,8 @@
44
47
  y,
45
48
  label,
46
49
  labelPlacement = 'top-right',
47
- labelOffset = 0,
50
+ labelXOffset = 0,
51
+ labelYOffset = 0,
48
52
  props,
49
53
  }: AnnotationLineProps = $props();
50
54
 
@@ -62,25 +66,17 @@
62
66
  const labelProps = $derived<ComponentProps<typeof Text>>(
63
67
  isVertical
64
68
  ? {
65
- x:
66
- line.x1 +
67
- (['top', 'bottom'].includes(labelPlacement)
68
- ? 0 // Offset applies to `y`
69
- : labelPlacement.includes('left')
70
- ? -labelOffset
71
- : labelOffset),
69
+ x: line.x1 + (labelPlacement.includes('left') ? -labelXOffset : labelXOffset),
72
70
  y:
73
71
  (labelPlacement.includes('top')
74
72
  ? line.y2
75
73
  : labelPlacement.includes('bottom')
76
74
  ? line.y1
77
75
  : (line.y1 - line.y2) / 2) +
78
- (labelPlacement === 'top'
79
- ? -labelOffset
80
- : labelPlacement === 'bottom'
81
- ? labelOffset
82
- : 0),
83
- dy: -2, // adjust for smaler font size
76
+ (['top', 'bottom-left', 'bottom-right'].includes(labelPlacement)
77
+ ? -labelYOffset
78
+ : labelYOffset),
79
+ dy: -2, // adjust for smaller font size
84
80
  textAnchor: labelPlacement.includes('left')
85
81
  ? 'end'
86
82
  : labelPlacement.includes('right')
@@ -104,19 +100,11 @@
104
100
  : labelPlacement.includes('right')
105
101
  ? line.x2
106
102
  : (line.x2 - line.x1) / 2) +
107
- (labelPlacement === 'left'
108
- ? -labelOffset
109
- : labelPlacement === 'right'
110
- ? labelOffset
111
- : 0),
112
- y:
113
- line.y1 +
114
- (['left', 'right'].includes(labelPlacement)
115
- ? 0 // Offset applies to `x`
116
- : labelPlacement.includes('top')
117
- ? -labelOffset
118
- : labelOffset),
119
- dy: -2, // adjust for smaler font size
103
+ (['left', 'top-right', 'bottom-right'].includes(labelPlacement)
104
+ ? -labelXOffset
105
+ : labelXOffset),
106
+ y: line.y1 + (labelPlacement.includes('top') ? -labelYOffset : labelYOffset),
107
+ dy: -2, // adjust for smaller font size
120
108
  textAnchor:
121
109
  labelPlacement === 'left'
122
110
  ? 'end' // place beside line
@@ -11,8 +11,10 @@ export type AnnotationLinePropsWithoutHTML = {
11
11
  label?: string;
12
12
  /** Placement of the label */
13
13
  labelPlacement?: Placement;
14
- /** Offset of the label */
15
- labelOffset?: number;
14
+ /** X offset of the label */
15
+ labelXOffset?: number;
16
+ /** Y offset of the label */
17
+ labelYOffset?: number;
16
18
  /** Classes for inner elements */
17
19
  props?: {
18
20
  label?: Partial<ComponentProps<typeof Text>>;
@@ -20,8 +20,11 @@
20
20
  /** Placement of the label */
21
21
  labelPlacement?: Placement;
22
22
 
23
- /** Offset of the label */
24
- labelOffset?: number;
23
+ /** X offset of the label */
24
+ labelXOffset?: number;
25
+
26
+ /** Y offset of the label */
27
+ labelYOffset?: number;
25
28
 
26
29
  /** Details (description, etc) useful to display in tooltip */
27
30
  details?: any;
@@ -51,7 +54,8 @@
51
54
  r = 4,
52
55
  label,
53
56
  labelPlacement = 'center',
54
- labelOffset = 0,
57
+ labelXOffset = 0,
58
+ labelYOffset = 0,
55
59
  details,
56
60
  props,
57
61
  }: AnnotationPointProps = $props();
@@ -66,18 +70,12 @@
66
70
  const labelProps = $derived<ComponentProps<typeof Text>>({
67
71
  x:
68
72
  point.x +
69
- (labelPlacement.includes('left')
70
- ? -(r + labelOffset)
71
- : labelPlacement.includes('right')
72
- ? r + labelOffset
73
- : 0),
73
+ ((['top', 'center', 'bottom'].includes(labelPlacement) ? 0 : r) + labelXOffset) *
74
+ (labelPlacement.includes('left') ? -1 : 1),
74
75
  y:
75
76
  point.y +
76
- (labelPlacement.includes('top')
77
- ? -(r + labelOffset)
78
- : labelPlacement.includes('bottom')
79
- ? r + labelOffset
80
- : 0),
77
+ ((['left', 'center', 'right'].includes(labelPlacement) ? 0 : r) + labelYOffset) *
78
+ (labelPlacement.includes('top') ? -1 : 1),
81
79
  dy: -2, // adjust for smaler font size
82
80
  textAnchor: labelPlacement.includes('left')
83
81
  ? 'end'
@@ -13,8 +13,10 @@ export type AnnotationPointPropsWithoutHTML = {
13
13
  label?: string;
14
14
  /** Placement of the label */
15
15
  labelPlacement?: Placement;
16
- /** Offset of the label */
17
- labelOffset?: number;
16
+ /** X offset of the label */
17
+ labelXOffset?: number;
18
+ /** Y offset of the label */
19
+ labelYOffset?: number;
18
20
  /** Details (description, etc) useful to display in tooltip */
19
21
  details?: any;
20
22
  /** Classes for inner elements */
@@ -104,13 +104,11 @@
104
104
  };
105
105
 
106
106
  export type AxisProps<In extends Transition = Transition> = AxisPropsWithoutHTML<In> &
107
- Without<SVGAttributes<SVGGElement>, AxisPropsWithoutHTML<In>>;
107
+ Without<GroupProps, AxisPropsWithoutHTML<In>>;
108
108
  </script>
109
109
 
110
110
  <script lang="ts" generics="T extends Transition = Transition">
111
111
  import { type ComponentProps, type Snippet } from 'svelte';
112
- import { fade } from 'svelte/transition';
113
- import { cubicIn } from 'svelte/easing';
114
112
  import type { SVGAttributes } from 'svelte/elements';
115
113
 
116
114
  import { extent } from 'd3-array';
@@ -119,6 +117,7 @@
119
117
  import { format as formatValue, type FormatType } from '@layerstack/utils';
120
118
  import { cls } from '@layerstack/tailwind';
121
119
 
120
+ import Group, { type GroupProps } from './Group.svelte';
122
121
  import Line from './Line.svelte';
123
122
  import Rule from './Rule.svelte';
124
123
  import Text from './Text.svelte';
@@ -126,7 +125,7 @@
126
125
 
127
126
  import { getChartContext } from './Chart.svelte';
128
127
  import { extractLayerProps, layerClass } from '../utils/attributes.js';
129
- import { extractTweenConfig, type MotionProp } from '../utils/motion.svelte.js';
128
+ import { type MotionProp } from '../utils/motion.svelte.js';
130
129
  import { resolveTickVals, type TicksConfig } from '../utils/ticks.js';
131
130
 
132
131
  let {
@@ -142,8 +141,8 @@
142
141
  format,
143
142
  tickLabelProps,
144
143
  motion,
145
- transitionIn: transitionInProp,
146
- transitionInParams: transitionInParamsProp,
144
+ transitionIn,
145
+ transitionInParams,
147
146
  scale: scaleProp,
148
147
  classes = {},
149
148
  class: className,
@@ -151,13 +150,6 @@
151
150
  ...restProps
152
151
  }: AxisProps<T> = $props();
153
152
 
154
- const transitionIn = $derived(
155
- transitionInProp ? transitionInProp : extractTweenConfig(motion)?.options ? fade : () => {}
156
- ) as T;
157
- const transitionInParams = $derived(
158
- transitionInParamsProp ? transitionInParamsProp : { easing: cubicIn }
159
- );
160
-
161
153
  const ctx = getChartContext();
162
154
 
163
155
  const orientation = $derived(
@@ -342,7 +334,7 @@
342
334
  }) satisfies ComponentProps<typeof Text>;
343
335
  </script>
344
336
 
345
- <g
337
+ <Group
346
338
  {...restProps}
347
339
  data-placement={placement}
348
340
  class={cls(layerClass('axis'), `placement-${placement}`, classes.root, className)}
@@ -364,7 +356,7 @@
364
356
  <Text {...resolvedLabelProps} />
365
357
  {/if}
366
358
 
367
- {#each tickVals as tick, index (tick)}
359
+ {#each tickVals as tick, index (tick.toString())}
368
360
  {@const tickCoords = getCoords(tick)}
369
361
  {@const [radialTickCoordsX, radialTickCoordsY] = pointRadial(tickCoords.x, tickCoords.y)}
370
362
  {@const [radialTickMarkCoordsX, radialTickMarkCoordsY] = pointRadial(
@@ -386,7 +378,7 @@
386
378
  ),
387
379
  }}
388
380
 
389
- <g in:transitionIn={transitionInParams} class={layerClass('axis-tick-group')}>
381
+ <Group {transitionIn} {transitionInParams} class={layerClass('axis-tick-group')}>
390
382
  {#if grid !== false}
391
383
  {@const ruleProps = extractLayerProps(grid, 'axis-grid')}
392
384
  <Rule
@@ -404,7 +396,6 @@
404
396
  'stroke-surface-content/50',
405
397
  classes.tick
406
398
  )}
407
- <!-- Tick marks -->
408
399
  {#if orientation === 'horizontal'}
409
400
  <Line
410
401
  x1={tickCoords.x}
@@ -434,13 +425,12 @@
434
425
  />
435
426
  {/if}
436
427
  {/if}
437
- <!-- TODO: Add tick marks for radial (angle)? -->
438
428
 
439
429
  {#if tickLabel}
440
430
  {@render tickLabel({ props: resolvedTickLabelProps, index })}
441
431
  {:else}
442
432
  <Text {...resolvedTickLabelProps} />
443
433
  {/if}
444
- </g>
434
+ </Group>
445
435
  {/each}
446
- </g>
436
+ </Group>
@@ -89,10 +89,11 @@ export type AxisPropsWithoutHTML<In extends Transition = Transition> = {
89
89
  };
90
90
  motion?: MotionProp;
91
91
  };
92
- export type AxisProps<In extends Transition = Transition> = AxisPropsWithoutHTML<In> & Without<SVGAttributes<SVGGElement>, AxisPropsWithoutHTML<In>>;
92
+ export type AxisProps<In extends Transition = Transition> = AxisPropsWithoutHTML<In> & Without<GroupProps, AxisPropsWithoutHTML<In>>;
93
93
  import { type ComponentProps, type Snippet } from 'svelte';
94
94
  import type { SVGAttributes } from 'svelte/elements';
95
95
  import { type FormatType } from '@layerstack/utils';
96
+ import { type GroupProps } from './Group.svelte';
96
97
  import Rule from './Rule.svelte';
97
98
  import Text from './Text.svelte';
98
99
  import { type MotionProp } from '../utils/motion.svelte.js';
@@ -2,7 +2,10 @@
2
2
  import { createDimensionGetter, type Insets } from '../utils/rect.svelte.js';
3
3
 
4
4
  export type BarPropsWithoutHTML = {
5
- bar: Object;
5
+ /**
6
+ * data to render the bar from
7
+ */
8
+ data: Object;
6
9
 
7
10
  /**
8
11
  * Override `x` from context. Useful for multiple Bar instances
@@ -89,7 +92,7 @@
89
92
  const ctx = getChartContext();
90
93
 
91
94
  let {
92
- bar,
95
+ data,
93
96
  x = ctx.x,
94
97
  y = ctx.y,
95
98
  x1,
@@ -122,11 +125,11 @@
122
125
  }))
123
126
  );
124
127
 
125
- const dimensions = $derived(getDimensions(bar) ?? { x: 0, y: 0, width: 0, height: 0 });
128
+ const dimensions = $derived(getDimensions(data) ?? { x: 0, y: 0, width: 0, height: 0 });
126
129
 
127
130
  const isVertical = $derived(isScaleBand(ctx.xScale));
128
131
  const valueAccessor = $derived(accessor(isVertical ? y : x));
129
- const value = $derived(valueAccessor(bar));
132
+ const value = $derived(valueAccessor(data));
130
133
  const resolvedValue = $derived(Array.isArray(value) ? greatestAbs(value) : value);
131
134
 
132
135
  // Resolved `rounded="edge"` based on orientation and value
@@ -1,6 +1,9 @@
1
1
  import { type Insets } from '../utils/rect.svelte.js';
2
2
  export type BarPropsWithoutHTML = {
3
- bar: Object;
3
+ /**
4
+ * data to render the bar from
5
+ */
6
+ data: Object;
4
7
  /**
5
8
  * Override `x` from context. Useful for multiple Bar instances
6
9
  *
@@ -21,16 +21,19 @@
21
21
  children?: Snippet;
22
22
  // TODO: investigate
23
23
  [key: string]: any;
24
- } & Omit<BarPropsWithoutHTML, 'bar' | 'children'>;
24
+ } & Omit<BarPropsWithoutHTML, 'data' | 'children'>;
25
25
 
26
- export type BarsProps = BarsPropsWithoutHTML & Omit<BarProps, 'bar'>;
26
+ export type BarsProps = BarsPropsWithoutHTML & Omit<BarProps, 'data'>;
27
27
  </script>
28
28
 
29
29
  <script lang="ts">
30
+ import type { Snippet } from 'svelte';
31
+
30
32
  import Bar, { type BarProps, type BarPropsWithoutHTML } from './Bar.svelte';
33
+ import Group from './Group.svelte';
34
+
31
35
  import { chartDataArray } from '../utils/common.js';
32
36
  import { getChartContext } from './Chart.svelte';
33
- import type { Snippet } from 'svelte';
34
37
  import { extractLayerProps, layerClass } from '../utils/attributes.js';
35
38
 
36
39
  let {
@@ -49,20 +52,20 @@
49
52
  const data = $derived(chartDataArray(dataProp ?? ctx.data));
50
53
  </script>
51
54
 
52
- <g class={layerClass('bars')}>
55
+ <Group class={layerClass('bars')}>
53
56
  {#if children}
54
57
  {@render children()}
55
58
  {:else}
56
59
  {#each data as d, i (key(d, i))}
57
60
  <Bar
61
+ data={d}
58
62
  {radius}
59
63
  {strokeWidth}
60
64
  {stroke}
61
- bar={d}
62
65
  fill={fill ?? (ctx.config.c ? ctx.cGet(d) : null)}
63
66
  onclick={(e) => onBarClick(e, { data: d })}
64
67
  {...extractLayerProps(restProps, 'bars-bar')}
65
68
  />
66
69
  {/each}
67
70
  {/if}
68
- </g>
71
+ </Group>
@@ -18,10 +18,10 @@ export type BarsPropsWithoutHTML = {
18
18
  }) => void;
19
19
  children?: Snippet;
20
20
  [key: string]: any;
21
- } & Omit<BarPropsWithoutHTML, 'bar' | 'children'>;
22
- export type BarsProps = BarsPropsWithoutHTML & Omit<BarProps, 'bar'>;
23
- import { type BarProps, type BarPropsWithoutHTML } from './Bar.svelte';
21
+ } & Omit<BarPropsWithoutHTML, 'data' | 'children'>;
22
+ export type BarsProps = BarsPropsWithoutHTML & Omit<BarProps, 'data'>;
24
23
  import type { Snippet } from 'svelte';
24
+ import { type BarProps, type BarPropsWithoutHTML } from './Bar.svelte';
25
25
  declare const Bars: import("svelte").Component<BarsProps, {}, "">;
26
26
  type Bars = ReturnType<typeof Bars>;
27
27
  export default Bars;
@@ -21,22 +21,28 @@
21
21
  </script>
22
22
 
23
23
  <script lang="ts">
24
+ import type { Snippet } from 'svelte';
25
+ import { getRenderContext } from './Chart.svelte';
24
26
  import { createId } from '../utils/createId.js';
25
27
  import { layerClass } from '../utils/attributes.js';
26
- import type { Snippet } from 'svelte';
28
+
27
29
  const uid = $props.id();
28
30
 
29
31
  let { id = createId('blur-', uid), stdDeviation = 5, children }: BlurProps = $props();
32
+
33
+ const renderContext = getRenderContext();
30
34
  </script>
31
35
 
32
- <defs>
33
- <filter {id} class={layerClass('blur-filter')}>
34
- <feGaussianBlur in="SourceGraphic" {stdDeviation} />
35
- </filter>
36
- </defs>
36
+ {#if renderContext === 'svg'}
37
+ <defs>
38
+ <filter {id} class={layerClass('blur-filter')}>
39
+ <feGaussianBlur in="SourceGraphic" {stdDeviation} />
40
+ </filter>
41
+ </defs>
37
42
 
38
- {#if children}
39
- <g filter="url(#{id})" class={layerClass('blur-g')}>
40
- {@render children({ id, url: `url(#${id})` })}
41
- </g>
43
+ {#if children}
44
+ <g filter="url(#{id})" class={layerClass('blur-g')}>
45
+ {@render children({ id, url: `url(#${id})` })}
46
+ </g>
47
+ {/if}
42
48
  {/if}
@@ -4,6 +4,7 @@
4
4
  import { layerClass } from '../utils/attributes.js';
5
5
  import type { Snippet } from 'svelte';
6
6
  import type { SVGAttributes } from 'svelte/elements';
7
+ import { getRenderContext } from './Chart.svelte';
7
8
 
8
9
  export type ClipPathPropsWithoutHTML = {
9
10
  /**
@@ -55,20 +56,24 @@
55
56
  }: ClipPathPropsWithoutHTML = $props();
56
57
 
57
58
  const url = $derived(`url(#${id})`);
59
+
60
+ const renderContext = getRenderContext();
58
61
  </script>
59
62
 
60
- <defs>
61
- <clipPath {id} {...restProps}>
62
- {@render clip?.({ id })}
63
+ {#if renderContext === 'svg'}
64
+ <defs>
65
+ <clipPath {id} {...restProps}>
66
+ {@render clip?.({ id })}
63
67
 
64
- {#if useId}
65
- <use href="#{useId}" />
66
- {/if}
67
- </clipPath>
68
- </defs>
68
+ {#if useId}
69
+ <use href="#{useId}" />
70
+ {/if}
71
+ </clipPath>
72
+ </defs>
73
+ {/if}
69
74
 
70
75
  {#if children}
71
- {#if disabled}
76
+ {#if disabled || renderContext !== 'svg'}
72
77
  {@render children({ id, url, useId })}
73
78
  {:else}
74
79
  <g style:clip-path={url} class={layerClass('clip-path-g')}>
@@ -15,7 +15,7 @@
15
15
  };
16
16
 
17
17
  export type GeoEdgeFadeProps = GeoEdgeFadePropsWithoutHTML &
18
- Without<SVGAttributes<SVGGElement>, GeoEdgeFadePropsWithoutHTML>;
18
+ Without<GroupProps, GeoEdgeFadePropsWithoutHTML>;
19
19
  </script>
20
20
 
21
21
  <script lang="ts">
@@ -23,6 +23,7 @@
23
23
  import { geoDistance } from 'd3-geo';
24
24
 
25
25
  import { getGeoContext } from './GeoContext.svelte';
26
+ import Group, { type GroupProps } from './Group.svelte';
26
27
  import { extractLayerProps } from '../utils/attributes.js';
27
28
 
28
29
  let {
@@ -55,6 +56,6 @@
55
56
  const opacity = $derived(opacityProp ?? clamper(fade(distance)));
56
57
  </script>
57
58
 
58
- <g {opacity} bind:this={ref} {...extractLayerProps(restProps, 'geo-edge-fade')}>
59
+ <Group {opacity} bind:ref {...extractLayerProps(restProps, 'geo-edge-fade')}>
59
60
  {@render children?.()}
60
- </g>
61
+ </Group>
@@ -1,6 +1,5 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { Without } from '../utils/types.js';
3
- import type { SVGAttributes } from 'svelte/elements';
4
3
  export type GeoEdgeFadePropsWithoutHTML = {
5
4
  link: {
6
5
  source: [number, number];
@@ -13,7 +12,8 @@ export type GeoEdgeFadePropsWithoutHTML = {
13
12
  ref?: SVGGElement;
14
13
  children?: Snippet;
15
14
  };
16
- export type GeoEdgeFadeProps = GeoEdgeFadePropsWithoutHTML & Without<SVGAttributes<SVGGElement>, GeoEdgeFadePropsWithoutHTML>;
15
+ export type GeoEdgeFadeProps = GeoEdgeFadePropsWithoutHTML & Without<GroupProps, GeoEdgeFadePropsWithoutHTML>;
16
+ import { type GroupProps } from './Group.svelte';
17
17
  declare const GeoEdgeFade: import("svelte").Component<GeoEdgeFadeProps, {}, "ref">;
18
18
  type GeoEdgeFade = ReturnType<typeof GeoEdgeFade>;
19
19
  export default GeoEdgeFade;
@@ -16,6 +16,7 @@
16
16
  <script lang="ts">
17
17
  import { geoGraticule } from 'd3-geo';
18
18
  import { extractLayerProps, layerClass } from '../utils/attributes.js';
19
+ import Group from './Group.svelte';
19
20
 
20
21
  let { lines, outline, step = [10, 10], ...restProps }: GraticuleProps = $props();
21
22
 
@@ -26,7 +27,7 @@
26
27
  });
27
28
  </script>
28
29
 
29
- <g class={layerClass('graticule-g')}>
30
+ <Group class={layerClass('graticule-g')}>
30
31
  <!-- TODO: Any reason to still render the single `MultiLineString` path if using `lines` and/or `outline` -->
31
32
  {#if !lines && !outline}
32
33
  <GeoPath geojson={graticule()} {...extractLayerProps(restProps, 'graticule-geo-path')} />
@@ -44,4 +45,4 @@
44
45
  {...extractLayerProps(outline, 'graticule-geo-outline')}
45
46
  />
46
47
  {/if}
47
- </g>
48
+ </Group>
@@ -77,7 +77,7 @@
77
77
  };
78
78
 
79
79
  export type GridProps<In extends Transition = Transition> = Omit<
80
- GridPropsWithoutHTML<In> & Without<SVGAttributes<SVGGElement>, GridPropsWithoutHTML<In>>,
80
+ GridPropsWithoutHTML<In> & Without<GroupProps, GridPropsWithoutHTML<In>>,
81
81
  'children'
82
82
  >;
83
83
  </script>
@@ -93,6 +93,7 @@
93
93
  import { isScaleBand } from '../utils/scales.svelte.js';
94
94
 
95
95
  import Circle from './Circle.svelte';
96
+ import Group, { type GroupProps } from './Group.svelte';
96
97
  import Line from './Line.svelte';
97
98
  import Rule from './Rule.svelte';
98
99
  import Spline from './Spline.svelte';
@@ -150,11 +151,11 @@
150
151
  );
151
152
  </script>
152
153
 
153
- <g bind:this={ref} class={cls(layerClass('grid'), classes.root, className)} {...restProps}>
154
+ <Group bind:ref class={cls(layerClass('grid'), classes.root, className)} {...restProps}>
154
155
  {#if x}
155
156
  {@const splineProps = extractLayerProps(x, 'grid-x-line')}
156
157
 
157
- <g in:transitionIn={transitionInParams} class={layerClass('grid-x')}>
158
+ <Group {transitionIn} {transitionInParams} class={layerClass('grid-x')}>
158
159
  {#each xTickVals as x (x)}
159
160
  {#if ctx.radial}
160
161
  {@const [x1, y1] = pointRadial(ctx.xScale(x), ctx.yRange[0])}
@@ -204,12 +205,12 @@
204
205
  )}
205
206
  />
206
207
  {/if}
207
- </g>
208
+ </Group>
208
209
  {/if}
209
210
 
210
211
  {#if y}
211
212
  {@const splineProps = extractLayerProps(y, 'grid-y-line')}
212
- <g in:transitionIn={transitionInParams} class={layerClass('grid-y')}>
213
+ <Group {transitionIn} {transitionInParams} class={layerClass('grid-y')}>
213
214
  {#each yTickVals as y (y)}
214
215
  {#if ctx.radial}
215
216
  {#if radialY === 'circle'}
@@ -285,6 +286,6 @@
285
286
  />
286
287
  {/if}
287
288
  {/if}
288
- </g>
289
+ </Group>
289
290
  {/if}
290
- </g>
291
+ </Group>
@@ -63,7 +63,8 @@ export type GridPropsWithoutHTML<In extends Transition = Transition> = {
63
63
  ref?: SVGGElement;
64
64
  motion?: MotionProp;
65
65
  };
66
- export type GridProps<In extends Transition = Transition> = Omit<GridPropsWithoutHTML<In> & Without<SVGAttributes<SVGGElement>, GridPropsWithoutHTML<In>>, 'children'>;
66
+ export type GridProps<In extends Transition = Transition> = Omit<GridPropsWithoutHTML<In> & Without<GroupProps, GridPropsWithoutHTML<In>>, 'children'>;
67
+ import { type GroupProps } from './Group.svelte';
67
68
  import { type TicksConfig } from '../utils/ticks.js';
68
69
  declare const Grid: import("svelte").Component<GridProps<Transition>, {}, "ref">;
69
70
  type Grid = ReturnType<typeof Grid>;
@@ -1,10 +1,10 @@
1
1
  <script lang="ts" module>
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes, TouchEventHandler } from 'svelte/elements';
4
- import type { Without } from '../utils/types.js';
5
- import { createMotion, type MotionProp } from '../utils/motion.svelte.js';
4
+ import type { Transition, TransitionParams, Without } from '../utils/types.js';
5
+ import { createMotion, extractTweenConfig, type MotionProp } from '../utils/motion.svelte.js';
6
6
 
7
- export type GroupPropsWithoutHTML = {
7
+ export type GroupPropsWithoutHTML<In extends Transition = Transition> = {
8
8
  /**
9
9
  * Translate x
10
10
  */
@@ -44,6 +44,11 @@
44
44
  */
45
45
  preventTouchMove?: boolean;
46
46
 
47
+ /**
48
+ * The opacity of the element. (0 to 1)
49
+ */
50
+ opacity?: number;
51
+
47
52
  children?: Snippet;
48
53
 
49
54
  /**
@@ -56,13 +61,28 @@
56
61
  ref?: Element;
57
62
 
58
63
  motion?: MotionProp;
64
+
65
+ /**
66
+ * Transition function for entering elements
67
+ * @default defaults to fade if the motion prop is set to tweened
68
+ */
69
+ transitionIn?: In;
70
+
71
+ /**
72
+ * Parameters for the transitionIn function
73
+ * @default { easing: cubicIn }
74
+ */
75
+ transitionInParams?: TransitionParams<In>;
59
76
  };
60
77
 
61
78
  export type GroupProps = GroupPropsWithoutHTML &
62
79
  Without<HTMLAttributes<Element>, GroupPropsWithoutHTML>;
63
80
  </script>
64
81
 
65
- <script lang="ts">
82
+ <script lang="ts" generics="T extends Transition = Transition">
83
+ import { fade } from 'svelte/transition';
84
+ import { cubicIn } from 'svelte/easing';
85
+
66
86
  import { cls } from '@layerstack/tailwind';
67
87
 
68
88
  import { getRenderContext } from './Chart.svelte';
@@ -80,7 +100,10 @@
80
100
  initialY: initialYProp,
81
101
  center = false,
82
102
  preventTouchMove = false,
103
+ opacity = 1,
83
104
  motion,
105
+ transitionIn: transitionInProp,
106
+ transitionInParams: transitionInParamsProp,
84
107
  class: className,
85
108
  children,
86
109
  ref: refProp = $bindable(),
@@ -101,6 +124,13 @@
101
124
  const motionX = createMotion(initialX, () => trueX, motion);
102
125
  const motionY = createMotion(initialY, () => trueY, motion);
103
126
 
127
+ const transitionIn = $derived(
128
+ transitionInProp ? transitionInProp : extractTweenConfig(motion)?.options ? fade : () => {}
129
+ ) as T;
130
+ const transitionInParams = $derived(
131
+ transitionInParamsProp ? transitionInParamsProp : { easing: cubicIn }
132
+ );
133
+
104
134
  const transform = $derived.by(() => {
105
135
  if (center || x != null || y != null) {
106
136
  return `translate(${motionX.current}px, ${motionY.current}px)`;
@@ -113,7 +143,13 @@
113
143
  registerCanvasComponent({
114
144
  name: 'Group',
115
145
  render: (ctx) => {
146
+ const currentGlobalAlpha = ctx.globalAlpha;
147
+ ctx.globalAlpha = opacity;
148
+
116
149
  ctx.translate(motionX.current ?? 0, motionY.current ?? 0);
150
+
151
+ // Restore in case it was modified by `opacity`
152
+ ctx.globalAlpha = currentGlobalAlpha;
117
153
  },
118
154
  retainState: true,
119
155
  events: {
@@ -124,7 +160,7 @@
124
160
  pointerleave: restProps.onpointerleave,
125
161
  pointerdown: restProps.onpointerdown,
126
162
  },
127
- deps: () => [motionX.current, motionY.current],
163
+ deps: () => [motionX.current, motionY.current, opacity],
128
164
  });
129
165
  }
130
166
 
@@ -143,6 +179,8 @@
143
179
  <g
144
180
  style:transform
145
181
  class={cls(layerClass('group-g'), className)}
182
+ in:transitionIn={transitionInParams}
183
+ {opacity}
146
184
  {...restProps}
147
185
  ontouchmove={handleTouchMove}
148
186
  bind:this={ref}
@@ -153,6 +191,8 @@
153
191
  <div
154
192
  bind:this={ref}
155
193
  style:transform
194
+ style:opacity
195
+ in:transitionIn={transitionInParams}
156
196
  {...restProps}
157
197
  class={cls(layerClass('group-div'), 'absolute', className)}
158
198
  ontouchmove={handleTouchMove}
@@ -1,8 +1,8 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
- import type { Without } from '../utils/types.js';
3
+ import type { Transition, TransitionParams, Without } from '../utils/types.js';
4
4
  import { type MotionProp } from '../utils/motion.svelte.js';
5
- export type GroupPropsWithoutHTML = {
5
+ export type GroupPropsWithoutHTML<In extends Transition = Transition> = {
6
6
  /**
7
7
  * Translate x
8
8
  */
@@ -36,6 +36,10 @@ export type GroupPropsWithoutHTML = {
36
36
  * @default false
37
37
  */
38
38
  preventTouchMove?: boolean;
39
+ /**
40
+ * The opacity of the element. (0 to 1)
41
+ */
42
+ opacity?: number;
39
43
  children?: Snippet;
40
44
  /**
41
45
  * A reference to the rendered DOM element, which could be
@@ -46,8 +50,32 @@ export type GroupPropsWithoutHTML = {
46
50
  */
47
51
  ref?: Element;
48
52
  motion?: MotionProp;
53
+ /**
54
+ * Transition function for entering elements
55
+ * @default defaults to fade if the motion prop is set to tweened
56
+ */
57
+ transitionIn?: In;
58
+ /**
59
+ * Parameters for the transitionIn function
60
+ * @default { easing: cubicIn }
61
+ */
62
+ transitionInParams?: TransitionParams<In>;
49
63
  };
50
64
  export type GroupProps = GroupPropsWithoutHTML & Without<HTMLAttributes<Element>, GroupPropsWithoutHTML>;
51
- declare const Group: import("svelte").Component<GroupProps, {}, "ref">;
52
- type Group = ReturnType<typeof Group>;
65
+ declare class __sveltets_Render<T extends Transition = Transition> {
66
+ props(): GroupProps;
67
+ events(): {};
68
+ slots(): {};
69
+ bindings(): "ref";
70
+ exports(): {};
71
+ }
72
+ interface $$IsomorphicComponent {
73
+ new <T extends Transition = Transition>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
74
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
75
+ } & ReturnType<__sveltets_Render<T>['exports']>;
76
+ <T extends Transition = Transition>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
77
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
78
+ }
79
+ declare const Group: $$IsomorphicComponent;
80
+ type Group<T extends Transition = Transition> = InstanceType<typeof Group<T>>;
53
81
  export default Group;
@@ -471,7 +471,7 @@
471
471
  {:else}
472
472
  <Bar
473
473
  motion={motion === 'spring' ? 'spring' : undefined}
474
- bar={highlightData}
474
+ data={highlightData}
475
475
  {...barProps}
476
476
  class={cls(!barProps.fill && 'fill-primary', barProps.class)}
477
477
  onclick={onBarClick && ((e) => onBarClick(e, { data: highlightData }))}
@@ -49,8 +49,7 @@
49
49
  ref?: SVGGElement;
50
50
  };
51
51
 
52
- export type HullProps = HullPropsWithoutHTML &
53
- Without<SVGAttributes<SVGElement>, HullPropsWithoutHTML>;
52
+ export type HullProps = HullPropsWithoutHTML & Without<GroupProps, HullPropsWithoutHTML>;
54
53
  </script>
55
54
 
56
55
  <script lang="ts">
@@ -62,6 +61,7 @@
62
61
  import { cls } from '@layerstack/tailwind';
63
62
 
64
63
  import GeoPath from './GeoPath.svelte';
64
+ import Group, { type GroupProps } from './Group.svelte';
65
65
  import Spline from './Spline.svelte';
66
66
  import { getChartContext } from './Chart.svelte';
67
67
  import { getGeoContext } from './GeoContext.svelte';
@@ -104,7 +104,7 @@
104
104
  );
105
105
  </script>
106
106
 
107
- <g {...restProps} class={cls(layerClass('hull-g'), classes.root, className)} bind:this={ref}>
107
+ <Group {...restProps} class={cls(layerClass('hull-g'), classes.root, className)} bind:ref>
108
108
  {#if geoCtx.projection}
109
109
  {@const polygon = geoVoronoi().hull(points)}
110
110
  <GeoPath
@@ -129,4 +129,4 @@
129
129
  {onpointerleave}
130
130
  />
131
131
  {/if}
132
- </g>
132
+ </Group>
@@ -1,5 +1,4 @@
1
1
  import type { Without } from '../utils/types.js';
2
- import type { SVGAttributes } from 'svelte/elements';
3
2
  import type { ComponentProps } from 'svelte';
4
3
  export type HullPropsWithoutHTML = {
5
4
  /**
@@ -34,8 +33,9 @@ export type HullPropsWithoutHTML = {
34
33
  */
35
34
  ref?: SVGGElement;
36
35
  };
37
- export type HullProps = HullPropsWithoutHTML & Without<SVGAttributes<SVGElement>, HullPropsWithoutHTML>;
36
+ export type HullProps = HullPropsWithoutHTML & Without<GroupProps, HullPropsWithoutHTML>;
38
37
  import { Delaunay } from 'd3-delaunay';
38
+ import { type GroupProps } from './Group.svelte';
39
39
  import Spline from './Spline.svelte';
40
40
  declare const Hull: import("svelte").Component<HullProps, {}, "ref">;
41
41
  type Hull = ReturnType<typeof Hull>;
@@ -73,6 +73,7 @@
73
73
 
74
74
  import { isScaleBand } from '../utils/scales.svelte.js';
75
75
  import { getChartContext } from './Chart.svelte';
76
+ import Group from './Group.svelte';
76
77
  import { extractLayerProps, layerClass } from '../utils/attributes.js';
77
78
 
78
79
  const ctx = getChartContext();
@@ -172,7 +173,7 @@
172
173
  }
173
174
  </script>
174
175
 
175
- <g class={layerClass('labels-g')}>
176
+ <Group class={layerClass('labels-g')}>
176
177
  <Points {data} {x} {y}>
177
178
  {#snippet children({ points })}
178
179
  {#each points as point, i (key(point.data, i))}
@@ -196,4 +197,4 @@
196
197
  {/each}
197
198
  {/snippet}
198
199
  </Points>
199
- </g>
200
+ </Group>
@@ -52,6 +52,7 @@
52
52
  import { cls } from '@layerstack/tailwind';
53
53
 
54
54
  import Circle from './Circle.svelte';
55
+ import Group from './Group.svelte';
55
56
  import Line, { type LinePropsWithoutHTML } from './Line.svelte';
56
57
  import { getChartContext } from './Chart.svelte';
57
58
  import { layerClass } from '../utils/attributes.js';
@@ -87,7 +88,7 @@
87
88
  }
88
89
  </script>
89
90
 
90
- <g class={layerClass('rule-g')}>
91
+ <Group class={layerClass('rule-g')}>
91
92
  {#if showRule(x, 'x')}
92
93
  {@const xCoord =
93
94
  x === true || x === 'left'
@@ -153,4 +154,4 @@
153
154
  />
154
155
  {/if}
155
156
  {/if}
156
- </g>
157
+ </Group>
@@ -1,6 +1,5 @@
1
1
  <script lang="ts" module>
2
2
  import type { Without } from '../utils/types.js';
3
- import type { SVGAttributes } from 'svelte/elements';
4
3
 
5
4
  export type VoronoiPropsWithoutHTML = {
6
5
  /**
@@ -8,6 +7,9 @@
8
7
  */
9
8
  data?: any;
10
9
 
10
+ /** Radius to clip voronoi cells. `0` or `undefined` to disables clipping */
11
+ r?: number;
12
+
11
13
  /**
12
14
  * Classes to apply to the root and path elements
13
15
  *
@@ -50,26 +52,30 @@
50
52
  };
51
53
 
52
54
  export type VoronoiProps = VoronoiPropsWithoutHTML &
53
- Without<Omit<SVGAttributes<SVGGElement>, 'children'>, VoronoiPropsWithoutHTML>;
55
+ Without<Omit<GroupProps, 'children'>, VoronoiPropsWithoutHTML>;
54
56
  </script>
55
57
 
56
58
  <script lang="ts">
57
59
  import { min } from 'd3-array';
58
60
  import { Delaunay } from 'd3-delaunay';
59
- import type { GeoPermissibleObjects } from 'd3-geo';
61
+ import { type GeoPermissibleObjects } from 'd3-geo';
60
62
  // @ts-expect-error
61
63
  import { geoVoronoi } from 'd3-geo-voronoi';
62
64
  import { pointRadial } from 'd3-shape';
63
65
  import { cls } from '@layerstack/tailwind';
64
66
 
65
67
  import GeoPath from './GeoPath.svelte';
68
+ import Group, { type GroupProps } from './Group.svelte';
66
69
  import Spline from './Spline.svelte';
67
70
  import { getChartContext } from './Chart.svelte';
68
71
  import { getGeoContext } from './GeoContext.svelte';
72
+ import CircleClipPath from './CircleClipPath.svelte';
73
+
69
74
  import { layerClass } from '../utils/attributes.js';
70
75
 
71
76
  let {
72
77
  data,
78
+ r,
73
79
  classes = {},
74
80
  onclick,
75
81
  onpointerenter,
@@ -108,54 +114,67 @@
108
114
  // Width and/or height can sometimes be negative (when loading data remotely and updately)
109
115
  const boundWidth = $derived(Math.max(ctx.width, 0));
110
116
  const boundHeight = $derived(Math.max(ctx.height, 0));
117
+
118
+ const disableClip = $derived(r === 0 || r == null || r === Infinity);
111
119
  </script>
112
120
 
113
- <g {...restProps} class={cls(layerClass('voronoi-g'), classes.root, className)}>
121
+ <Group {...restProps} class={cls(layerClass('voronoi-g'), classes.root, className)}>
114
122
  {#if geo.projection}
115
123
  {@const polygons = geoVoronoi().polygons(points)}
116
124
  {#each polygons.features as feature}
117
- <GeoPath
118
- geojson={feature}
119
- class={cls(
120
- layerClass('voronoi-geo-path'),
121
- 'fill-transparent stroke-transparent',
122
- classes.path
123
- )}
124
- onclick={(e) => onclick?.(e, { data: feature.properties.site.data, feature })}
125
- onpointerenter={(e) => onpointerenter?.(e, { data: feature.properties.site.data, feature })}
126
- onpointermove={(e) => onpointermove?.(e, { data: feature.properties.site.data, feature })}
127
- onpointerdown={(e) => onpointerdown?.(e, { data: feature.properties.site.data, feature })}
128
- {onpointerleave}
129
- ontouchmove={(e) => {
130
- // Prevent touch to not interfere with pointer
131
- e.preventDefault();
132
- }}
133
- />
134
- {/each}
135
- {:else}
136
- {@const voronoi = Delaunay.from(points).voronoi([0, 0, boundWidth, boundHeight])}
137
- {#each points as point, i}
138
- {@const pathData = voronoi.renderCell(i)}
139
- <!-- Wait to render Spline until pathData is available to fix path artifacts from injected tweened points in Spline -->
140
- {#if pathData}
141
- <Spline
142
- {pathData}
125
+ {@const point = r ? geo.projection?.(feature.properties.sitecoordinates) : null}
126
+ <CircleClipPath
127
+ cx={point?.[0]}
128
+ cy={point?.[1]}
129
+ r={r ?? 0}
130
+ disabled={point == null || disableClip}
131
+ >
132
+ <GeoPath
133
+ geojson={feature}
143
134
  class={cls(
144
- layerClass('voronoi-path'),
135
+ layerClass('voronoi-geo-path'),
145
136
  'fill-transparent stroke-transparent',
146
137
  classes.path
147
138
  )}
148
- onclick={(e) => onclick?.(e, { data: point.data, point })}
149
- onpointerenter={(e) => onpointerenter?.(e, { data: point.data, point })}
150
- onpointermove={(e) => onpointermove?.(e, { data: point.data, point })}
139
+ onclick={(e) => onclick?.(e, { data: feature.properties.site.data, feature })}
140
+ onpointerenter={(e) =>
141
+ onpointerenter?.(e, { data: feature.properties.site.data, feature })}
142
+ onpointermove={(e) => onpointermove?.(e, { data: feature.properties.site.data, feature })}
143
+ onpointerdown={(e) => onpointerdown?.(e, { data: feature.properties.site.data, feature })}
151
144
  {onpointerleave}
152
- onpointerdown={(e) => onpointerdown?.(e, { data: point.data, point })}
153
145
  ontouchmove={(e) => {
154
146
  // Prevent touch to not interfere with pointer
155
147
  e.preventDefault();
156
148
  }}
157
149
  />
150
+ </CircleClipPath>
151
+ {/each}
152
+ {:else}
153
+ {@const voronoi = Delaunay.from(points).voronoi([0, 0, boundWidth, boundHeight])}
154
+ {#each points as point, i}
155
+ {@const pathData = voronoi.renderCell(i)}
156
+ <!-- Wait to render Spline until pathData is available to fix path artifacts from injected tweened points in Spline -->
157
+ {#if pathData}
158
+ <CircleClipPath cx={point[0]} cy={point[1]} r={r ?? 0} disabled={disableClip}>
159
+ <Spline
160
+ {pathData}
161
+ class={cls(
162
+ layerClass('voronoi-path'),
163
+ 'fill-transparent stroke-transparent',
164
+ classes.path
165
+ )}
166
+ onclick={(e) => onclick?.(e, { data: point.data, point })}
167
+ onpointerenter={(e) => onpointerenter?.(e, { data: point.data, point })}
168
+ onpointermove={(e) => onpointermove?.(e, { data: point.data, point })}
169
+ {onpointerleave}
170
+ onpointerdown={(e) => onpointerdown?.(e, { data: point.data, point })}
171
+ ontouchmove={(e) => {
172
+ // Prevent touch to not interfere with pointer
173
+ e.preventDefault();
174
+ }}
175
+ />
176
+ </CircleClipPath>
158
177
  {/if}
159
178
  {/each}
160
179
  {/if}
161
- </g>
180
+ </Group>
@@ -1,10 +1,11 @@
1
1
  import type { Without } from '../utils/types.js';
2
- import type { SVGAttributes } from 'svelte/elements';
3
2
  export type VoronoiPropsWithoutHTML = {
4
3
  /**
5
4
  * Override data instead of using context
6
5
  */
7
6
  data?: any;
7
+ /** Radius to clip voronoi cells. `0` or `undefined` to disables clipping */
8
+ r?: number;
8
9
  /**
9
10
  * Classes to apply to the root and path elements
10
11
  *
@@ -35,8 +36,9 @@ export type VoronoiPropsWithoutHTML = {
35
36
  feature?: GeoPermissibleObjects;
36
37
  }) => void;
37
38
  };
38
- export type VoronoiProps = VoronoiPropsWithoutHTML & Without<Omit<SVGAttributes<SVGGElement>, 'children'>, VoronoiPropsWithoutHTML>;
39
- import type { GeoPermissibleObjects } from 'd3-geo';
39
+ export type VoronoiProps = VoronoiPropsWithoutHTML & Without<Omit<GroupProps, 'children'>, VoronoiPropsWithoutHTML>;
40
+ import { type GeoPermissibleObjects } from 'd3-geo';
41
+ import { type GroupProps } from './Group.svelte';
40
42
  declare const Voronoi: import("svelte").Component<VoronoiProps, {}, "">;
41
43
  type Voronoi = ReturnType<typeof Voronoi>;
42
44
  export default Voronoi;
@@ -377,7 +377,6 @@
377
377
  {@render childrenProp(snippetProps)}
378
378
  {:else}
379
379
  {@render belowContext?.(snippetProps)}
380
- <!-- TODO: Always use `Svg` until `Pattern` supports `Canvas` (issue #307) -->
381
380
 
382
381
  <Layer
383
382
  type={renderContext}
@@ -4,7 +4,7 @@ import type { SeriesData } from './types.js';
4
4
  import type Legend from '../Legend.svelte';
5
5
  export declare class HighlightKey<TData, SeriesComponent extends Component> {
6
6
  current: string | null;
7
- set(seriesKey: typeof this.current): void;
7
+ set: (seriesKey: typeof this.current) => void;
8
8
  }
9
9
  export declare class SeriesState<TData, TComponent extends Component> {
10
10
  #private;
@@ -2,9 +2,9 @@ import { SelectionState } from '@layerstack/svelte-state';
2
2
  import { scaleOrdinal } from 'd3-scale';
3
3
  export class HighlightKey {
4
4
  current = $state(null);
5
- set(seriesKey) {
5
+ set = (seriesKey) => {
6
6
  this.current = seriesKey;
7
- }
7
+ };
8
8
  }
9
9
  export class SeriesState {
10
10
  #series = $state.raw([]);
@@ -72,7 +72,7 @@
72
72
  locked?: boolean;
73
73
 
74
74
  /**
75
- * quadtree search radius
75
+ * quadtree search or voronoi clip radius
76
76
  * @default Infinity
77
77
  */
78
78
  radius?: number;
@@ -116,11 +116,14 @@
116
116
  </script>
117
117
 
118
118
  <script lang="ts" generics="TData = any">
119
+ import type { Snippet } from 'svelte';
119
120
  import { bisector, max, min } from 'd3-array';
120
121
  import { quadtree as d3Quadtree, type Quadtree } from 'd3-quadtree';
121
122
  import { sortFunc, localPoint } from '@layerstack/utils';
122
123
  import { cls } from '@layerstack/tailwind';
123
124
 
125
+ import { getChartContext } from '../Chart.svelte';
126
+ import { getGeoContext } from '../GeoContext.svelte';
124
127
  import Svg from './../layout/Svg.svelte';
125
128
  import Arc from '../Arc.svelte';
126
129
  import ChartClipPath from './../ChartClipPath.svelte';
@@ -130,8 +133,6 @@
130
133
  import { cartesianToPolar } from '../../utils/math.js';
131
134
  import { quadtreeRects } from '../../utils/quadtree.js';
132
135
  import { raise } from '../../utils/chart.js';
133
- import { getChartContext } from '../Chart.svelte';
134
- import type { Snippet } from 'svelte';
135
136
  import {
136
137
  getTooltipMetaContext,
137
138
  getTooltipPayload,
@@ -140,6 +141,7 @@
140
141
  import { layerClass } from '../../utils/attributes.js';
141
142
 
142
143
  const ctx = getChartContext<any>();
144
+ const geoCtx = getGeoContext();
143
145
 
144
146
  let {
145
147
  ref: refProp = $bindable(),
@@ -357,7 +359,11 @@
357
359
  }
358
360
 
359
361
  case 'quadtree': {
360
- tooltipData = quadtree?.find(point.x, point.y, radius);
362
+ tooltipData = quadtree?.find(
363
+ point.x - ctx.padding.left,
364
+ point.y - ctx.padding.top,
365
+ radius
366
+ );
361
367
  break;
362
368
  }
363
369
  }
@@ -402,11 +408,14 @@
402
408
  const quadtree: Quadtree<[number, number]> | undefined = $derived.by(() => {
403
409
  if (mode === 'quadtree') {
404
410
  return d3Quadtree()
405
- .extent([
406
- [0, 0],
407
- [ctx.width, ctx.height],
408
- ])
409
411
  .x((d) => {
412
+ if (geoCtx.projection) {
413
+ const lat = ctx.x(d);
414
+ const long = ctx.y(d);
415
+ const geoValue = geoCtx.projection([lat, long]) ?? [0, 0];
416
+ return geoValue[0];
417
+ }
418
+
410
419
  const value = ctx.xGet(d);
411
420
 
412
421
  if (Array.isArray(value)) {
@@ -420,6 +429,13 @@
420
429
  }
421
430
  })
422
431
  .y((d) => {
432
+ if (geoCtx.projection) {
433
+ const lat = ctx.x(d);
434
+ const long = ctx.y(d);
435
+ const geoValue = geoCtx.projection([lat, long]) ?? [0, 0];
436
+ return geoValue[1];
437
+ }
438
+
423
439
  const value = ctx.yGet(d);
424
440
 
425
441
  if (Array.isArray(value)) {
@@ -546,6 +562,7 @@
546
562
  {#if mode === 'voronoi'}
547
563
  <Svg>
548
564
  <Voronoi
565
+ r={radius}
549
566
  onpointerenter={(e, { data }) => {
550
567
  showTooltip(e, data);
551
568
  }}
@@ -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;
package/package.json CHANGED
@@ -4,9 +4,10 @@
4
4
  "author": "Sean Lynch <techniq35@gmail.com>",
5
5
  "license": "MIT",
6
6
  "repository": "techniq/layerchart",
7
- "version": "2.0.0-next.2",
7
+ "version": "2.0.0-next.4",
8
8
  "devDependencies": {
9
9
  "@changesets/cli": "^2.29.2",
10
+ "@iconify-json/lucide": "^1.2.42",
10
11
  "@mdi/js": "^7.4.47",
11
12
  "@rollup/plugin-dsv": "^3.0.5",
12
13
  "@sveltejs/adapter-cloudflare": "^4.9.0",
@@ -62,6 +63,7 @@
62
63
  "tslib": "^2.8.1",
63
64
  "typescript": "^5.8.3",
64
65
  "unist-util-visit": "^5.0.0",
66
+ "unplugin-icons": "^22.1.0",
65
67
  "us-atlas": "^3.0.1",
66
68
  "vite": "^6.3.4",
67
69
  "vitest": "^3.1.2"