layerchart 0.81.2 → 0.90.0

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 (64) hide show
  1. package/dist/components/Arc.svelte +49 -14
  2. package/dist/components/Area.svelte +35 -11
  3. package/dist/components/Bar.svelte +15 -10
  4. package/dist/components/Bars.svelte +3 -3
  5. package/dist/components/Brush.svelte +20 -21
  6. package/dist/components/Calendar.svelte +2 -2
  7. package/dist/components/Chart.svelte +17 -4
  8. package/dist/components/Chart.svelte.d.ts +21 -10
  9. package/dist/components/ChartClipPath.svelte +0 -1
  10. package/dist/components/ChartClipPath.svelte.d.ts +0 -2
  11. package/dist/components/ChartContext.svelte +4 -11
  12. package/dist/components/ChartContext.svelte.d.ts +1 -1
  13. package/dist/components/Circle.svelte +35 -11
  14. package/dist/components/ClipPath.svelte +1 -1
  15. package/dist/components/ClipPath.svelte.d.ts +0 -5
  16. package/dist/components/ForceSimulation.svelte +8 -12
  17. package/dist/components/ForceSimulation.svelte.d.ts +6 -8
  18. package/dist/components/Frame.svelte +4 -7
  19. package/dist/components/Frame.svelte.d.ts +1 -5
  20. package/dist/components/GeoCircle.svelte +7 -1
  21. package/dist/components/GeoCircle.svelte.d.ts +6 -7
  22. package/dist/components/GeoPath.svelte +68 -39
  23. package/dist/components/GeoPath.svelte.d.ts +6 -12
  24. package/dist/components/GeoPoint.svelte +3 -24
  25. package/dist/components/GeoPoint.svelte.d.ts +4 -5
  26. package/dist/components/Graticule.svelte.d.ts +12 -6
  27. package/dist/components/Group.svelte +26 -6
  28. package/dist/components/Highlight.svelte +22 -12
  29. package/dist/components/Hull.svelte +20 -15
  30. package/dist/components/Hull.svelte.d.ts +7 -9
  31. package/dist/components/Legend.svelte +7 -7
  32. package/dist/components/Legend.svelte.d.ts +3 -3
  33. package/dist/components/Line.svelte +35 -10
  34. package/dist/components/Link.svelte +16 -9
  35. package/dist/components/Points.svelte +24 -57
  36. package/dist/components/Points.svelte.d.ts +0 -1
  37. package/dist/components/Rect.svelte +46 -15
  38. package/dist/components/Sankey.svelte +3 -4
  39. package/dist/components/Sankey.svelte.d.ts +1 -2
  40. package/dist/components/Spline.svelte +47 -11
  41. package/dist/components/Text.svelte +12 -6
  42. package/dist/components/TransformContext.svelte +8 -10
  43. package/dist/components/TransformContext.svelte.d.ts +9 -9
  44. package/dist/components/Voronoi.svelte +62 -40
  45. package/dist/components/Voronoi.svelte.d.ts +14 -13
  46. package/dist/components/charts/AreaChart.svelte +23 -15
  47. package/dist/components/charts/BarChart.svelte +24 -11
  48. package/dist/components/charts/LineChart.svelte +23 -15
  49. package/dist/components/charts/PieChart.svelte +24 -19
  50. package/dist/components/charts/PieChart.svelte.d.ts +29 -7
  51. package/dist/components/charts/ScatterChart.svelte +13 -7
  52. package/dist/components/index.d.ts +0 -1
  53. package/dist/components/index.js +0 -1
  54. package/dist/components/layout/Canvas.svelte +148 -7
  55. package/dist/components/layout/Canvas.svelte.d.ts +15 -2
  56. package/dist/components/tooltip/TooltipContext.svelte +25 -20
  57. package/dist/components/tooltip/TooltipContext.svelte.d.ts +6 -2
  58. package/dist/utils/canvas.js +39 -33
  59. package/dist/utils/color.d.ts +15 -0
  60. package/dist/utils/color.js +15 -0
  61. package/package.json +1 -1
  62. package/dist/components/Brush.svelte.d.ts +0 -65
  63. package/dist/components/HitCanvas.svelte +0 -118
  64. package/dist/components/HitCanvas.svelte.d.ts +0 -32
@@ -32,6 +32,7 @@
32
32
  interface $$Props extends ComponentProps<Chart<TData>> {
33
33
  axis?: typeof axis;
34
34
  brush?: typeof brush;
35
+ debug?: typeof debug;
35
36
  grid?: typeof grid;
36
37
  labels?: typeof labels;
37
38
  legend?: typeof legend;
@@ -41,8 +42,8 @@
41
42
  rule?: typeof rule;
42
43
  series?: typeof series;
43
44
  renderContext?: typeof renderContext;
44
- onPointClick?: typeof onPointClick;
45
- onTooltipClick?: typeof onTooltipClick;
45
+ onpointclick?: typeof onpointclick;
46
+ ontooltipclick?: typeof ontooltipclick;
46
47
  }
47
48
 
48
49
  export let data: $$Props['data'] = [];
@@ -75,13 +76,16 @@
75
76
  export let rule: ComponentProps<Rule> | boolean = true;
76
77
 
77
78
  /** Event dispatched with current tooltip data */
78
- export let onTooltipClick: (e: { data: any }) => void = () => {};
79
+ export let ontooltipclick: (e: MouseEvent, details: { data: any }) => void = () => {};
79
80
 
80
81
  /** Event dispatched when Highlight point is clicked (useful with multiple series) */
81
- export let onPointClick: (e: {
82
- data: HighlightPointData;
83
- series: (typeof series)[number];
84
- }) => void = () => {};
82
+ export let onpointclick: (
83
+ e: MouseEvent,
84
+ details: {
85
+ data: HighlightPointData;
86
+ series: (typeof series)[number];
87
+ }
88
+ ) => void = () => {};
85
89
 
86
90
  export let props: {
87
91
  brush?: Partial<ComponentProps<Brush>>;
@@ -109,6 +113,9 @@
109
113
  /** Log initial render performance using `console.time` */
110
114
  export let profile = false;
111
115
 
116
+ /** Enable debug mode */
117
+ export let debug = false;
118
+
112
119
  $: allSeriesData = series
113
120
  .flatMap((s) => s.data?.map((d) => ({ seriesKey: s.key, ...d })))
114
121
  .filter((d) => d) as Array<TData & { stackData?: any }>;
@@ -208,7 +215,8 @@
208
215
  ? false
209
216
  : {
210
217
  mode: 'bisect-x',
211
- onClick: onTooltipClick,
218
+ onclick: ontooltipclick,
219
+ debug,
212
220
  ...props.tooltip?.context,
213
221
  ...$$props.tooltip,
214
222
  }}
@@ -241,7 +249,7 @@
241
249
  getSplineProps,
242
250
  }}
243
251
  <slot {...slotProps}>
244
- <svelte:component this={renderContext === 'canvas' ? Canvas : Svg} center={radial}>
252
+ <svelte:component this={renderContext === 'canvas' ? Canvas : Svg} center={radial} {debug}>
245
253
  <slot name="grid" {...slotProps}>
246
254
  {#if grid}
247
255
  <Grid x={radial} y {...typeof grid === 'object' ? grid : null} {...props.grid} />
@@ -313,9 +321,9 @@
313
321
  ),
314
322
  }}
315
323
  lines={i === 0}
316
- onPointClick={(e) => onPointClick({ ...e, series: s })}
317
- onPointEnter={() => (highlightSeriesKey = s.key)}
318
- onPointLeave={() => (highlightSeriesKey = null)}
324
+ onpointclick={(e, detail) => onpointclick(e, { ...detail, series: s })}
325
+ onpointenter={() => (highlightSeriesKey = s.key)}
326
+ onpointleave={() => (highlightSeriesKey = null)}
319
327
  {...props.highlight}
320
328
  />
321
329
  {/each}
@@ -350,9 +358,9 @@
350
358
  tickFormat={(key) => series.find((s) => s.key === key)?.label ?? key}
351
359
  placement="bottom"
352
360
  variant="swatches"
353
- onClick={(item) => $selectedSeries.toggleSelected(item.value)}
354
- onPointerEnter={(item) => (highlightSeriesKey = item.value)}
355
- onPointerLeave={(item) => (highlightSeriesKey = null)}
361
+ onclick={(e, item) => $selectedSeries.toggleSelected(item.value)}
362
+ onpointerenter={(e, item) => (highlightSeriesKey = item.value)}
363
+ onpointerleave={(e) => (highlightSeriesKey = null)}
356
364
  {...props.legend}
357
365
  {...typeof legend === 'object' ? legend : null}
358
366
  classes={{
@@ -16,9 +16,7 @@
16
16
 
17
17
  import { accessor, chartDataArray, type Accessor } from '../../utils/common.js';
18
18
 
19
- type ChartProps = ComponentProps<Chart<TData>>;
20
-
21
- interface $$Props extends ChartProps {
19
+ interface $$Props extends ComponentProps<Chart<TData>> {
22
20
  cornerRadius?: typeof cornerRadius;
23
21
  innerRadius?: typeof innerRadius;
24
22
  key?: typeof key;
@@ -30,16 +28,17 @@
30
28
  center?: typeof center;
31
29
  placement?: typeof placement;
32
30
  profile?: typeof profile;
31
+ debug?: typeof debug;
33
32
  props?: typeof props;
34
33
  range?: typeof range;
35
34
  series?: typeof series;
36
35
  value?: typeof value;
37
36
  renderContext?: typeof renderContext;
38
- onArcClick?: typeof onArcClick;
39
- onTooltipClick?: typeof onTooltipClick;
37
+ onarcclick?: typeof onarcclick;
38
+ ontooltipclick?: typeof ontooltipclick;
40
39
  }
41
40
 
42
- export let data: ChartProps['data'] = [];
41
+ export let data: $$Props['data'] = [];
43
42
 
44
43
  /** Key accessor */
45
44
  export let key: Accessor<TData> = 'key';
@@ -98,12 +97,15 @@
98
97
  /** Center chart. Override and use `props.group` for more control */
99
98
  export let center = placement === 'center';
100
99
 
101
- // TODO: Not usable with manual tooltip / arc path. Use `onArcClick`?
100
+ // TODO: Not usable with manual tooltip / arc path. Use `onarcclick`?
102
101
  /** Event dispatched with current tooltip data */
103
- export let onTooltipClick: (e: { data: any }) => void = () => {};
102
+ export let ontooltipclick: (e: MouseEvent, detail: { data: any }) => void = () => {};
104
103
 
105
104
  /** Event dispatched when individual Arc is clicked (useful with multiple series) */
106
- export let onArcClick: (e: { data: any; series: (typeof series)[number] }) => void = () => {};
105
+ export let onarcclick: (
106
+ e: MouseEvent,
107
+ detail: { data: any; series: (typeof series)[number] }
108
+ ) => void = () => {};
107
109
 
108
110
  export let props: {
109
111
  pie?: Partial<ComponentProps<Pie>>;
@@ -125,6 +127,9 @@
125
127
  /** Log initial render performance using `console.time` */
126
128
  export let profile = false;
127
129
 
130
+ /** Enable debug mode */
131
+ export let debug = false;
132
+
128
133
  $: allSeriesData = series
129
134
  .flatMap((s) => s.data?.map((d) => ({ seriesKey: s.key, ...d })))
130
135
  .filter((d) => d) as Array<TData>;
@@ -201,7 +206,7 @@
201
206
  visibleData,
202
207
  }}
203
208
  <slot {...slotProps}>
204
- <svelte:component this={renderContext === 'canvas' ? Canvas : Svg} {center}>
209
+ <svelte:component this={renderContext === 'canvas' ? Canvas : Svg} {center} {debug}>
205
210
  <slot name="belowMarks" {...slotProps} />
206
211
 
207
212
  <slot name="marks" {...slotProps}>
@@ -230,10 +235,10 @@
230
235
  track={{ fill: s.color ?? cScale?.(c(d)), 'fill-opacity': 0.1 }}
231
236
  {tooltip}
232
237
  data={d}
233
- on:click={() => {
234
- onArcClick({ data: d, series: s });
238
+ onclick={(e) => {
239
+ onarcclick(e, { data: d, series: s });
235
240
  // Workaround for `tooltip={{ mode: 'manual' }}
236
- onTooltipClick({ data: d });
241
+ ontooltipclick(e, { data: d });
237
242
  }}
238
243
  {...props.arc}
239
244
  {...s.props}
@@ -266,10 +271,10 @@
266
271
  fill={cScale?.(c(arc.data))}
267
272
  data={arc.data}
268
273
  {tooltip}
269
- on:click={() => {
270
- onArcClick({ data: arc.data, series: s });
274
+ onclick={(e) => {
275
+ onarcclick(e, { data: arc.data, series: s });
271
276
  // Workaround for `tooltip={{ mode: 'manual' }}
272
- onTooltipClick({ data: arc.data });
277
+ ontooltipclick(e, { data: arc.data });
273
278
  }}
274
279
  class={cls(
275
280
  'transition-opacity',
@@ -297,9 +302,9 @@
297
302
  }}
298
303
  placement="bottom"
299
304
  variant="swatches"
300
- onClick={(item) => $selectedKeys.toggleSelected(item.value)}
301
- onPointerEnter={(item) => (highlightKey = item.value)}
302
- onPointerLeave={(item) => (highlightKey = null)}
305
+ onclick={(e, item) => $selectedKeys.toggleSelected(item.value)}
306
+ onpointerenter={(e, item) => (highlightKey = item.value)}
307
+ onpointerleave={(e) => (highlightKey = null)}
303
308
  {...props.legend}
304
309
  {...typeof legend === 'object' ? legend : null}
305
310
  classes={{
@@ -121,13 +121,13 @@ declare class __sveltets_Render<TData> {
121
121
  geo?: import("svelte/store").Writable<import("d3-geo").GeoProjection>;
122
122
  }> | undefined;
123
123
  tooltip?: boolean | Partial<{
124
- mode?: "bisect-x" | "bisect-y" | "band" | "bisect-band" | "bounds" | "voronoi" | "quadtree" | "manual";
124
+ mode?: "manual" | "bisect-x" | "bisect-y" | "band" | "bisect-band" | "bounds" | "voronoi" | "quadtree";
125
125
  findTooltipData?: "closest" | "left" | "right";
126
126
  raiseTarget?: boolean;
127
127
  locked?: boolean;
128
128
  radius?: number;
129
129
  debug?: boolean;
130
- onClick?: ({ data }: {
130
+ onclick?: ({ data }: {
131
131
  data: any;
132
132
  }) => any;
133
133
  tooltip?: import("svelte/store").Writable<{
@@ -136,6 +136,7 @@ declare class __sveltets_Render<TData> {
136
136
  data: any;
137
137
  show: (e: PointerEvent, tooltipData?: any) => void;
138
138
  hide: () => void;
139
+ mode: "manual" | "bisect-x" | "bisect-y" | "band" | "bisect-band" | "bounds" | "voronoi" | "quadtree";
139
140
  }>;
140
141
  }> | undefined;
141
142
  transform?: Partial<{
@@ -150,6 +151,15 @@ declare class __sveltets_Render<TData> {
150
151
  disablePointer?: boolean;
151
152
  initialScrollMode?: "none" | "scale" | "translate";
152
153
  clickDistance?: number;
154
+ ondragstart?: (() => void) | undefined;
155
+ ondragend?: (() => void) | undefined;
156
+ ontransform?: ((e: {
157
+ scale: number;
158
+ translate: {
159
+ x: number;
160
+ y: number;
161
+ };
162
+ }) => void) | undefined;
153
163
  initialTranslate?: {
154
164
  x: number;
155
165
  y: number;
@@ -192,7 +202,18 @@ declare class __sveltets_Render<TData> {
192
202
  data: any;
193
203
  show: (e: PointerEvent, tooltipData?: any) => void;
194
204
  hide: () => void;
205
+ mode: "manual" | "bisect-x" | "bisect-y" | "band" | "bisect-band" | "bounds" | "voronoi" | "quadtree";
195
206
  }> | undefined;
207
+ onresize?: ((e: import("../ChartContext.svelte").ChartResizeDetail) => void) | undefined;
208
+ ondragstart?: (() => void) | undefined;
209
+ ondragend?: (() => void) | undefined;
210
+ ontransform?: ((e: {
211
+ scale: number;
212
+ translate: {
213
+ x: number;
214
+ y: number;
215
+ };
216
+ }) => void) | undefined;
196
217
  } & {
197
218
  cornerRadius?: number;
198
219
  innerRadius?: number | undefined;
@@ -211,9 +232,9 @@ declare class __sveltets_Render<TData> {
211
232
  tickLength?: number | undefined;
212
233
  placement?: ("center" | "bottom" | "left" | "right" | "top" | "top-left" | "top-right" | "bottom-left" | "bottom-right") | undefined;
213
234
  orientation?: "horizontal" | "vertical" | undefined;
214
- onClick?: ((item: any) => any) | undefined | undefined;
215
- onPointerEnter?: ((item: any) => any) | undefined | undefined;
216
- onPointerLeave?: ((item: any) => any) | undefined | undefined;
235
+ onclick?: ((e: MouseEvent, detail: any) => any) | undefined | undefined;
236
+ onpointerenter?: ((e: MouseEvent, detail: any) => any) | undefined | undefined;
237
+ onpointerleave?: ((e: MouseEvent, detail: any) => any) | undefined | undefined;
217
238
  variant?: "ramp" | "swatches" | undefined;
218
239
  classes?: {
219
240
  root?: string;
@@ -231,6 +252,7 @@ declare class __sveltets_Render<TData> {
231
252
  center?: boolean;
232
253
  placement?: "center" | "left" | "right";
233
254
  profile?: boolean;
255
+ debug?: boolean;
234
256
  props?: {
235
257
  pie?: Partial<ComponentProps<Pie>>;
236
258
  group?: Partial<ComponentProps<Group>>;
@@ -259,7 +281,7 @@ declare class __sveltets_Render<TData> {
259
281
  }[] | undefined;
260
282
  value?: Accessor<TData>;
261
283
  renderContext?: "canvas" | "svg";
262
- onArcClick?: ((e: {
284
+ onarcclick?: ((e: MouseEvent, detail: {
263
285
  data: any;
264
286
  series: {
265
287
  key: string | number;
@@ -273,7 +295,7 @@ declare class __sveltets_Render<TData> {
273
295
  props?: Partial<ComponentProps<Arc>>;
274
296
  };
275
297
  }) => void) | undefined;
276
- onTooltipClick?: ((e: {
298
+ ontooltipclick?: ((e: MouseEvent, detail: {
277
299
  data: any;
278
300
  }) => void) | undefined;
279
301
  };
@@ -36,7 +36,7 @@
36
36
  props?: typeof props;
37
37
  series?: typeof series;
38
38
  renderContext?: typeof renderContext;
39
- onTooltipClick?: typeof onTooltipClick;
39
+ ontooltipclick?: typeof ontooltipclick;
40
40
  }
41
41
 
42
42
  export let data: $$Props['data'] = [];
@@ -65,15 +65,17 @@
65
65
  export let rule: ComponentProps<Rule> | boolean = true;
66
66
 
67
67
  /** Event dispatched with current tooltip data */
68
- export let onTooltipClick: (e: { data: any }) => void = () => {};
68
+ export let ontooltipclick: (e: MouseEvent, detail: { data: any }) => void = () => {};
69
69
 
70
70
  export let props: {
71
71
  brush?: Partial<ComponentProps<Brush>>;
72
+ debug?: typeof debug;
72
73
  grid?: Partial<ComponentProps<Grid>>;
73
74
  highlight?: Partial<ComponentProps<Highlight>>;
74
75
  labels?: Partial<ComponentProps<Labels>>;
75
76
  legend?: Partial<ComponentProps<Legend>>;
76
77
  points?: Partial<ComponentProps<Points>>;
78
+ profile?: typeof profile;
77
79
  rule?: Partial<ComponentProps<Rule>>;
78
80
  tooltip?: {
79
81
  context?: Partial<ComponentProps<Tooltip.Context>>;
@@ -92,6 +94,9 @@
92
94
  /** Log initial render performance using `console.time` */
93
95
  export let profile = false;
94
96
 
97
+ /** Enable debug mode */
98
+ export let debug = false;
99
+
95
100
  // Default xScale based on first data's `x` value
96
101
  $: xScale =
97
102
  $$props.xScale ??
@@ -175,7 +180,8 @@
175
180
  ? false
176
181
  : {
177
182
  mode: 'voronoi',
178
- onClick: onTooltipClick,
183
+ onclick: ontooltipclick,
184
+ debug,
179
185
  ...props.tooltip?.context,
180
186
  ...$$props.tooltip,
181
187
  }}
@@ -213,7 +219,7 @@
213
219
  : null}
214
220
 
215
221
  <slot {...slotProps}>
216
- <svelte:component this={renderContext === 'canvas' ? Canvas : Svg}>
222
+ <svelte:component this={renderContext === 'canvas' ? Canvas : Svg} {debug}>
217
223
  <slot name="grid" {...slotProps}>
218
224
  {#if grid}
219
225
  <Grid x y {...typeof grid === 'object' ? grid : null} {...props.grid} />
@@ -300,9 +306,9 @@
300
306
  tickFormat={(key) => series.find((s) => s.key === key)?.label ?? key}
301
307
  placement="bottom"
302
308
  variant="swatches"
303
- onClick={(item) => $selectedSeries.toggleSelected(item.value)}
304
- onPointerEnter={(item) => (highlightSeriesKey = item.value)}
305
- onPointerLeave={(item) => (highlightSeriesKey = null)}
309
+ onclick={(e, item) => $selectedSeries.toggleSelected(item.value)}
310
+ onpointerenter={(e, item) => (highlightSeriesKey = item.value)}
311
+ onpointerleave={(e) => (highlightSeriesKey = null)}
306
312
  {...props.legend}
307
313
  {...typeof legend === 'object' ? legend : null}
308
314
  classes={{
@@ -31,7 +31,6 @@ export { default as Graticule } from './Graticule.svelte';
31
31
  export { default as Grid } from './Grid.svelte';
32
32
  export { default as Group } from './Group.svelte';
33
33
  export { default as Highlight } from './Highlight.svelte';
34
- export { default as HitCanvas } from './HitCanvas.svelte';
35
34
  export { default as Hull } from './Hull.svelte';
36
35
  export { default as Labels } from './Labels.svelte';
37
36
  export { default as Legend } from './Legend.svelte';
@@ -32,7 +32,6 @@ export { default as Graticule } from './Graticule.svelte';
32
32
  export { default as Grid } from './Grid.svelte';
33
33
  export { default as Group } from './Group.svelte';
34
34
  export { default as Highlight } from './Highlight.svelte';
35
- export { default as HitCanvas } from './HitCanvas.svelte';
36
35
  export { default as Hull } from './Hull.svelte';
37
36
  export { default as Labels } from './Labels.svelte';
38
37
  export { default as Legend } from './Legend.svelte';
@@ -3,8 +3,19 @@
3
3
 
4
4
  type ComponentRender = {
5
5
  name: string;
6
- render: (ctx: CanvasRenderingContext2D) => any;
6
+ render: (ctx: CanvasRenderingContext2D, styleOverrides?: ComputedStylesOptions) => any;
7
7
  retainState?: boolean;
8
+ events?: {
9
+ click?: (e: MouseEvent) => void;
10
+ dblclick?: (e: MouseEvent) => void;
11
+ pointerenter?: (e: PointerEvent) => void;
12
+ pointerover?: (e: PointerEvent) => void;
13
+ pointermove?: (e: PointerEvent) => void;
14
+ pointerleave?: (e: PointerEvent) => void;
15
+ pointerout?: (e: PointerEvent) => void;
16
+ pointerdown?: (e: PointerEvent) => void;
17
+ touchmove?: (e: TouchEvent) => void;
18
+ };
8
19
  };
9
20
 
10
21
  export type CanvasContext = {
@@ -30,7 +41,10 @@
30
41
 
31
42
  import { chartContext } from '../ChartContext.svelte';
32
43
  import { transformContext } from '../TransformContext.svelte';
33
- import { scaleCanvas } from '../../utils/canvas.js';
44
+ import { getPixelColor, scaleCanvas, type ComputedStylesOptions } from '../../utils/canvas.js';
45
+ import { getColorStr, rgbColorGenerator } from '../../utils/color.js';
46
+ import { localPoint } from '../../utils/event.js';
47
+ import { tooltipContext } from '../tooltip/TooltipContext.svelte';
34
48
 
35
49
  const { width, height, containerWidth, containerHeight, padding } = chartContext();
36
50
 
@@ -68,14 +82,78 @@
68
82
  */
69
83
  export let center: boolean | 'x' | 'y' = false;
70
84
 
85
+ /** Show hit canvas for debugging */
86
+ export let debug = false;
87
+
71
88
  let components = new Map<Symbol, ComponentRender>();
72
89
  let pendingInvalidation = false;
73
90
  let frameId: number | undefined;
74
91
 
75
- const { mode, scale, translate } = transformContext();
92
+ const { mode, scale, translate, dragging, moving } = transformContext();
93
+ const tooltip = tooltipContext();
94
+
95
+ /**
96
+ * HitCanvas
97
+ */
98
+ let hitCanvasElement: HTMLCanvasElement | undefined = undefined;
99
+ let hitCanvasContext: CanvasRenderingContext2D | undefined = undefined;
100
+ let colorGenerator = rgbColorGenerator();
101
+ let activeCanvas = false;
102
+ let lastActiveComponent: ComponentRender | undefined | null;
103
+ const componentByColor = new Map<string, ComponentRender>();
104
+
105
+ function getPointerComponent(e: PointerEvent | MouseEvent | TouchEvent) {
106
+ const { x, y } = localPoint(e.target as HTMLCanvasElement, e) ?? { x: 0, y: 0 };
107
+ const color = getPixelColor(hitCanvasContext!, x, y);
108
+ const colorKey = getColorStr(color);
109
+ return componentByColor.get(colorKey);
110
+ }
111
+
112
+ function onPointerMove(e: PointerEvent) {
113
+ activeCanvas = true;
114
+ const component = getPointerComponent(e);
115
+
116
+ if (lastActiveComponent == null) {
117
+ // TODO: Should these be handled differently
118
+ component?.events?.pointerenter?.(e);
119
+ component?.events?.pointerover?.(e);
120
+ } else if (lastActiveComponent != component) {
121
+ // TODO: Should these be handled differently
122
+ lastActiveComponent?.events?.pointerleave?.(e);
123
+ lastActiveComponent?.events?.pointerout?.(e);
124
+
125
+ component?.events?.pointermove?.(e);
126
+ } else {
127
+ component?.events?.pointermove?.(e);
128
+ }
129
+
130
+ if (e.buttons === 1) {
131
+ component?.events?.pointerdown?.(e);
132
+ }
133
+
134
+ lastActiveComponent = component;
135
+ }
136
+
137
+ function onPointerLeave(e: PointerEvent) {
138
+ // Pointer outside of canvas
139
+
140
+ // Call last active component `pointerleave` event in case it was not triggered by hit canvas (quickly exiting canvas element before `pointermove` is triggered)
141
+ lastActiveComponent?.events?.pointerleave?.(e);
142
+ lastActiveComponent?.events?.pointerout?.(e);
143
+
144
+ lastActiveComponent = null;
145
+ activeCanvas = false;
146
+ }
147
+ /**
148
+ * end HitCanvas
149
+ */
76
150
 
77
151
  onMount(() => {
78
152
  context = element?.getContext('2d', { willReadFrequently }) as CanvasRenderingContext2D;
153
+
154
+ hitCanvasContext = hitCanvasElement?.getContext('2d', {
155
+ willReadFrequently: true,
156
+ }) as CanvasRenderingContext2D;
79
157
  });
80
158
 
81
159
  onDestroy(() => {
@@ -110,6 +188,16 @@
110
188
  context.scale($scale, $scale);
111
189
  }
112
190
 
191
+ // Sync hit canvas transform with main canvas
192
+ if (hitCanvasContext) {
193
+ scaleCanvas(hitCanvasContext, $containerWidth, $containerHeight);
194
+ hitCanvasContext.clearRect(0, 0, $containerWidth, $containerHeight);
195
+ hitCanvasContext.setTransform(context.getTransform());
196
+
197
+ // Reset color generator whenever updated so always reusing same colors (and not exhausting)
198
+ colorGenerator = rgbColorGenerator();
199
+ }
200
+
113
201
  components.forEach((c) => {
114
202
  if (c.retainState) {
115
203
  // Do not call save/restore canvas draw state (https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save) (ex. Group ctx.translate() affecting children)
@@ -119,6 +207,27 @@
119
207
  c.render(context);
120
208
  context.restore();
121
209
  }
210
+
211
+ // Delayed rendering using `activeCanvas` can cause a delay for tooltip interactivity for complex canvases (ex. country choropleth) so only ignore while moving/animating programmatically (ex. clicking on countries on Animated Globe)
212
+ const inactiveMoving = !activeCanvas && $moving;
213
+
214
+ const componentHasEvents = c.events && Object.values(c.events).filter((d) => d).length > 0;
215
+
216
+ if (hitCanvasContext && componentHasEvents && !inactiveMoving && !$dragging) {
217
+ const color = getColorStr(colorGenerator.next().value);
218
+ // Stroking shape seems to help with dark border, but there is still antialising and thus gaps
219
+ const styleOverrides = { styles: { fill: color, stroke: color, _fillOpacity: 0.1 } };
220
+
221
+ if (c.retainState) {
222
+ c.render(hitCanvasContext, styleOverrides);
223
+ } else {
224
+ hitCanvasContext.save();
225
+ c.render(hitCanvasContext, styleOverrides);
226
+ hitCanvasContext.restore();
227
+ }
228
+
229
+ componentByColor.set(color, c);
230
+ }
122
231
  });
123
232
 
124
233
  pendingInvalidation = false;
@@ -144,8 +253,8 @@
144
253
  };
145
254
 
146
255
  $: {
147
- // Redraw when resized
148
- $containerWidth, $containerHeight;
256
+ // Redraw when resized or transform dragging changes. Note: adding `activeCanvas` (pointer enters/exits canvas) causes initial interactivity issues while canvas is rendering and is not needed
257
+ $containerWidth, $containerHeight && $dragging;
149
258
  canvasContext.invalidate();
150
259
  }
151
260
 
@@ -164,16 +273,48 @@
164
273
  aria-label={label}
165
274
  aria-labelledby={labelledBy}
166
275
  aria-describedby={describedBy}
276
+ on:click={(e) => {
277
+ const component = getPointerComponent(e);
278
+ component?.events?.click?.(e);
279
+ }}
280
+ on:click
281
+ on:dblclick={(e) => {
282
+ const component = getPointerComponent(e);
283
+ component?.events?.dblclick?.(e);
284
+ }}
285
+ on:pointerenter={onPointerMove}
167
286
  on:pointerenter
287
+ on:pointermove={onPointerMove}
168
288
  on:pointermove
289
+ on:pointerleave={onPointerLeave}
169
290
  on:pointerleave
170
- on:pointerleave
291
+ on:touchmove={(e) => {
292
+ // Prevent touch from interfering with pointer if over data
293
+ if (lastActiveComponent) {
294
+ e.preventDefault();
295
+ }
296
+
297
+ const component = getPointerComponent(e);
298
+ component?.events?.touchmove?.(e);
299
+ }}
171
300
  on:touchmove
172
- on:click
173
301
  >
174
302
  <slot name="fallback">
175
303
  {fallback || ''}
176
304
  </slot>
177
305
  </canvas>
178
306
 
307
+ <!-- Hit canvas used for hidden context -->
308
+ <canvas
309
+ bind:this={hitCanvasElement}
310
+ class={cls(
311
+ 'layerchart-hitcanvas',
312
+ 'absolute top-0 left-0 w-full h-full',
313
+ 'pointer-events-none', // events all handled by main canvas
314
+ // '[image-rendering:pixelated]', // https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering
315
+ 'border border-danger',
316
+ !debug && 'opacity-0'
317
+ )}
318
+ ></canvas>
319
+
179
320
  <slot {element} {context}></slot>
@@ -1,8 +1,19 @@
1
1
  import { SvelteComponentTyped } from "svelte";
2
2
  type ComponentRender = {
3
3
  name: string;
4
- render: (ctx: CanvasRenderingContext2D) => any;
4
+ render: (ctx: CanvasRenderingContext2D, styleOverrides?: ComputedStylesOptions) => any;
5
5
  retainState?: boolean;
6
+ events?: {
7
+ click?: (e: MouseEvent) => void;
8
+ dblclick?: (e: MouseEvent) => void;
9
+ pointerenter?: (e: PointerEvent) => void;
10
+ pointerover?: (e: PointerEvent) => void;
11
+ pointermove?: (e: PointerEvent) => void;
12
+ pointerleave?: (e: PointerEvent) => void;
13
+ pointerout?: (e: PointerEvent) => void;
14
+ pointerdown?: (e: PointerEvent) => void;
15
+ touchmove?: (e: TouchEvent) => void;
16
+ };
6
17
  };
7
18
  export type CanvasContext = {
8
19
  /** Register component to render. Returns method to unregister on component destory */
@@ -11,6 +22,7 @@ export type CanvasContext = {
11
22
  };
12
23
  export declare const canvasContextKey: unique symbol;
13
24
  export declare function getCanvasContext(): CanvasContext;
25
+ import { type ComputedStylesOptions } from '../../utils/canvas.js';
14
26
  declare const __propDef: {
15
27
  props: {
16
28
  [x: string]: any;
@@ -24,13 +36,14 @@ declare const __propDef: {
24
36
  labelledBy?: string | undefined | undefined;
25
37
  describedBy?: string | undefined | undefined;
26
38
  center?: boolean | "x" | "y" | undefined;
39
+ debug?: boolean | undefined;
27
40
  };
28
41
  events: {
42
+ click: MouseEvent;
29
43
  pointerenter: PointerEvent;
30
44
  pointermove: PointerEvent;
31
45
  pointerleave: PointerEvent;
32
46
  touchmove: TouchEvent;
33
- click: MouseEvent;
34
47
  } & {
35
48
  [evt: string]: CustomEvent<any>;
36
49
  };