layerchart 2.0.0-next.1 → 2.0.0-next.11

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 (275) hide show
  1. package/dist/actions/movable.d.ts +28 -0
  2. package/dist/actions/movable.js +91 -0
  3. package/dist/components/AnnotationLine.svelte +143 -0
  4. package/dist/components/AnnotationLine.svelte.d.ts +30 -0
  5. package/dist/components/AnnotationPoint.svelte +119 -0
  6. package/dist/components/AnnotationPoint.svelte.d.ts +34 -0
  7. package/dist/components/AnnotationRange.svelte +147 -0
  8. package/dist/components/AnnotationRange.svelte.d.ts +40 -0
  9. package/dist/components/Arc.svelte +344 -151
  10. package/dist/components/Arc.svelte.d.ts +138 -0
  11. package/dist/components/Area.svelte +165 -149
  12. package/dist/components/Area.svelte.d.ts +45 -0
  13. package/dist/components/Axis.svelte +320 -179
  14. package/dist/components/Axis.svelte.d.ts +127 -0
  15. package/dist/components/Bar.svelte +166 -107
  16. package/dist/components/Bar.svelte.d.ts +51 -0
  17. package/dist/components/Bars.svelte +56 -67
  18. package/dist/components/Bars.svelte.d.ts +27 -0
  19. package/dist/components/Blur.svelte +42 -12
  20. package/dist/components/Blur.svelte.d.ts +23 -21
  21. package/dist/components/Bounds.svelte +49 -19
  22. package/dist/components/Bounds.svelte.d.ts +24 -50
  23. package/dist/components/BrushContext.svelte +296 -168
  24. package/dist/components/BrushContext.svelte.d.ts +97 -65
  25. package/dist/components/Calendar.svelte +116 -59
  26. package/dist/components/Calendar.svelte.d.ts +50 -31
  27. package/dist/components/Chart.svelte +1289 -398
  28. package/dist/components/Chart.svelte.d.ts +535 -410
  29. package/dist/components/ChartClipPath.svelte +37 -15
  30. package/dist/components/ChartClipPath.svelte.d.ts +21 -19
  31. package/dist/components/Circle.svelte +124 -85
  32. package/dist/components/Circle.svelte.d.ts +52 -0
  33. package/dist/components/CircleClipPath.svelte +76 -16
  34. package/dist/components/CircleClipPath.svelte.d.ts +46 -0
  35. package/dist/components/ClipPath.svelte +71 -21
  36. package/dist/components/ClipPath.svelte.d.ts +40 -27
  37. package/dist/components/ColorRamp.svelte +75 -9
  38. package/dist/components/ColorRamp.svelte.d.ts +37 -19
  39. package/dist/components/ComputedStyles.svelte +17 -5
  40. package/dist/components/ComputedStyles.svelte.d.ts +11 -19
  41. package/dist/components/Connector.svelte +149 -0
  42. package/dist/components/Connector.svelte.d.ts +51 -0
  43. package/dist/components/Dagre.svelte +211 -122
  44. package/dist/components/Dagre.svelte.d.ts +119 -56
  45. package/dist/components/ForceSimulation.svelte +215 -90
  46. package/dist/components/ForceSimulation.svelte.d.ts +82 -35
  47. package/dist/components/Frame.svelte +33 -13
  48. package/dist/components/Frame.svelte.d.ts +13 -17
  49. package/dist/components/GeoCircle.svelte +29 -16
  50. package/dist/components/GeoCircle.svelte.d.ts +22 -24
  51. package/dist/components/GeoContext.svelte +113 -72
  52. package/dist/components/GeoContext.svelte.d.ts +49 -41
  53. package/dist/components/GeoEdgeFade.svelte +49 -13
  54. package/dist/components/GeoEdgeFade.svelte.d.ts +17 -19
  55. package/dist/components/GeoPath.svelte +157 -127
  56. package/dist/components/GeoPath.svelte.d.ts +48 -36
  57. package/dist/components/GeoPoint.svelte +52 -20
  58. package/dist/components/GeoPoint.svelte.d.ts +25 -22
  59. package/dist/components/GeoSpline.svelte +75 -26
  60. package/dist/components/GeoSpline.svelte.d.ts +29 -20
  61. package/dist/components/GeoTile.svelte +100 -49
  62. package/dist/components/GeoTile.svelte.d.ts +38 -23
  63. package/dist/components/GeoVisible.svelte +17 -9
  64. package/dist/components/GeoVisible.svelte.d.ts +10 -18
  65. package/dist/components/Graticule.svelte +30 -14
  66. package/dist/components/Graticule.svelte.d.ts +11 -52
  67. package/dist/components/Grid.svelte +230 -117
  68. package/dist/components/Grid.svelte.d.ts +71 -0
  69. package/dist/components/Group.svelte +173 -106
  70. package/dist/components/Group.svelte.d.ts +81 -0
  71. package/dist/components/Highlight.svelte +410 -308
  72. package/dist/components/Highlight.svelte.d.ts +107 -0
  73. package/dist/components/Hull.svelte +97 -46
  74. package/dist/components/Hull.svelte.d.ts +40 -30
  75. package/dist/components/Labels.svelte +127 -47
  76. package/dist/components/Labels.svelte.d.ts +70 -27
  77. package/dist/components/Legend.svelte +374 -190
  78. package/dist/components/Legend.svelte.d.ts +95 -44
  79. package/dist/components/Line.svelte +163 -125
  80. package/dist/components/Line.svelte.d.ts +75 -0
  81. package/dist/components/LinearGradient.svelte +153 -78
  82. package/dist/components/LinearGradient.svelte.d.ts +66 -31
  83. package/dist/components/Link.svelte +160 -104
  84. package/dist/components/Link.svelte.d.ts +54 -0
  85. package/dist/components/Marker.svelte +100 -39
  86. package/dist/components/Marker.svelte.d.ts +59 -27
  87. package/dist/components/MarkerWrapper.svelte +35 -0
  88. package/dist/components/MarkerWrapper.svelte.d.ts +18 -0
  89. package/dist/components/MonthPath.svelte +65 -20
  90. package/dist/components/MonthPath.svelte.d.ts +23 -17
  91. package/dist/components/MotionPath.svelte +80 -24
  92. package/dist/components/MotionPath.svelte.d.ts +46 -27
  93. package/dist/components/Pack.svelte +53 -17
  94. package/dist/components/Pack.svelte.d.ts +42 -21
  95. package/dist/components/Partition.svelte +64 -22
  96. package/dist/components/Partition.svelte.d.ts +49 -26
  97. package/dist/components/Pattern.svelte +297 -11
  98. package/dist/components/Pattern.svelte.d.ts +103 -19
  99. package/dist/components/Pie.svelte +122 -76
  100. package/dist/components/Pie.svelte.d.ts +65 -51
  101. package/dist/components/Point.svelte +20 -9
  102. package/dist/components/Point.svelte.d.ts +16 -20
  103. package/dist/components/Points.svelte +148 -137
  104. package/dist/components/Points.svelte.d.ts +45 -34
  105. package/dist/components/RadialGradient.svelte +148 -77
  106. package/dist/components/RadialGradient.svelte.d.ts +69 -31
  107. package/dist/components/Rect.svelte +121 -102
  108. package/dist/components/Rect.svelte.d.ts +36 -0
  109. package/dist/components/RectClipPath.svelte +82 -18
  110. package/dist/components/RectClipPath.svelte.d.ts +55 -0
  111. package/dist/components/Rule.svelte +107 -63
  112. package/dist/components/Rule.svelte.d.ts +40 -19
  113. package/dist/components/Sankey.svelte +132 -55
  114. package/dist/components/Sankey.svelte.d.ts +61 -31
  115. package/dist/components/Spline.svelte +281 -218
  116. package/dist/components/Spline.svelte.d.ts +95 -0
  117. package/dist/components/Text.svelte +463 -197
  118. package/dist/components/Text.svelte.d.ts +136 -0
  119. package/dist/components/Threshold.svelte +48 -16
  120. package/dist/components/Threshold.svelte.d.ts +29 -31
  121. package/dist/components/TileImage.svelte +103 -30
  122. package/dist/components/TileImage.svelte.d.ts +48 -23
  123. package/dist/components/TransformContext.svelte +365 -171
  124. package/dist/components/TransformControls.svelte +50 -26
  125. package/dist/components/TransformControls.svelte.d.ts +27 -19
  126. package/dist/components/Tree.svelte +74 -33
  127. package/dist/components/Tree.svelte.d.ts +42 -30
  128. package/dist/components/Treemap.svelte +119 -42
  129. package/dist/components/Treemap.svelte.d.ts +75 -27
  130. package/dist/components/Voronoi.svelte +153 -103
  131. package/dist/components/Voronoi.svelte.d.ts +42 -41
  132. package/dist/components/charts/ArcChart.svelte +464 -0
  133. package/dist/components/charts/ArcChart.svelte.d.ts +90 -0
  134. package/dist/components/charts/AreaChart.svelte +444 -393
  135. package/dist/components/charts/AreaChart.svelte.d.ts +61 -0
  136. package/dist/components/charts/BarChart.svelte +463 -389
  137. package/dist/components/charts/BarChart.svelte.d.ts +76 -0
  138. package/dist/components/charts/ChartAnnotations.svelte +37 -0
  139. package/dist/components/charts/ChartAnnotations.svelte.d.ts +10 -0
  140. package/dist/components/charts/DefaultTooltip.svelte +60 -0
  141. package/dist/components/charts/DefaultTooltip.svelte.d.ts +10 -0
  142. package/dist/components/charts/LineChart.svelte +366 -315
  143. package/dist/components/charts/LineChart.svelte.d.ts +53 -0
  144. package/dist/components/charts/PieChart.svelte +458 -316
  145. package/dist/components/charts/PieChart.svelte.d.ts +137 -353
  146. package/dist/components/charts/ScatterChart.svelte +332 -296
  147. package/dist/components/charts/ScatterChart.svelte.d.ts +39 -0
  148. package/dist/components/charts/index.d.ts +8 -0
  149. package/dist/components/charts/index.js +7 -0
  150. package/dist/components/charts/types.d.ts +253 -0
  151. package/dist/components/charts/utils.svelte.d.ts +30 -0
  152. package/dist/components/charts/utils.svelte.js +59 -0
  153. package/dist/components/index.d.ts +76 -4
  154. package/dist/components/index.js +76 -5
  155. package/dist/components/layout/Canvas.svelte +347 -171
  156. package/dist/components/layout/Canvas.svelte.d.ts +110 -55
  157. package/dist/components/layout/Html.svelte +82 -42
  158. package/dist/components/layout/Html.svelte.d.ts +39 -28
  159. package/dist/components/layout/Layer.svelte +39 -0
  160. package/dist/components/layout/Layer.svelte.d.ts +17 -0
  161. package/dist/components/layout/Svg.svelte +122 -70
  162. package/dist/components/layout/Svg.svelte.d.ts +53 -34
  163. package/dist/components/layout/WebGL.svelte +135 -0
  164. package/dist/components/layout/WebGL.svelte.d.ts +50 -0
  165. package/dist/components/tooltip/Tooltip.svelte +253 -78
  166. package/dist/components/tooltip/Tooltip.svelte.d.ts +149 -31
  167. package/dist/components/tooltip/TooltipContext.svelte +426 -271
  168. package/dist/components/tooltip/TooltipContext.svelte.d.ts +86 -55
  169. package/dist/components/tooltip/TooltipHeader.svelte +100 -11
  170. package/dist/components/tooltip/TooltipHeader.svelte.d.ts +43 -23
  171. package/dist/components/tooltip/TooltipItem.svelte +167 -27
  172. package/dist/components/tooltip/TooltipItem.svelte.d.ts +63 -31
  173. package/dist/components/tooltip/TooltipList.svelte +22 -3
  174. package/dist/components/tooltip/TooltipList.svelte.d.ts +6 -17
  175. package/dist/components/tooltip/TooltipSeparator.svelte +27 -1
  176. package/dist/components/tooltip/TooltipSeparator.svelte.d.ts +6 -15
  177. package/dist/components/tooltip/index.d.ts +6 -0
  178. package/dist/components/tooltip/index.js +6 -0
  179. package/dist/components/tooltip/tooltipMetaContext.d.ts +79 -0
  180. package/dist/components/tooltip/tooltipMetaContext.js +139 -0
  181. package/dist/components/types.d.ts +1 -0
  182. package/dist/components/types.js +1 -0
  183. package/dist/docs/Blockquote.svelte +3 -1
  184. package/dist/docs/Blockquote.svelte.d.ts +5 -16
  185. package/dist/docs/Code.svelte +20 -12
  186. package/dist/docs/Code.svelte.d.ts +12 -22
  187. package/dist/docs/ConnectorSweepMenuField.svelte +17 -0
  188. package/dist/docs/ConnectorSweepMenuField.svelte.d.ts +7 -0
  189. package/dist/docs/ConnectorTypeMenuField.svelte +17 -0
  190. package/dist/docs/ConnectorTypeMenuField.svelte.d.ts +7 -0
  191. package/dist/docs/CurveMenuField.svelte +14 -3
  192. package/dist/docs/CurveMenuField.svelte.d.ts +9 -18
  193. package/dist/docs/GeoDebug.svelte +47 -42
  194. package/dist/docs/GeoDebug.svelte.d.ts +4 -16
  195. package/dist/docs/Header1.svelte +4 -2
  196. package/dist/docs/Header1.svelte.d.ts +5 -18
  197. package/dist/docs/Json.svelte +11 -3
  198. package/dist/docs/Json.svelte.d.ts +9 -17
  199. package/dist/docs/Layout.svelte +10 -7
  200. package/dist/docs/Layout.svelte.d.ts +5 -15
  201. package/dist/docs/Link.svelte +7 -3
  202. package/dist/docs/Link.svelte.d.ts +5 -27
  203. package/dist/docs/PathDataMenuField.svelte +14 -10
  204. package/dist/docs/PathDataMenuField.svelte.d.ts +8 -18
  205. package/dist/docs/Preview.svelte +20 -7
  206. package/dist/docs/Preview.svelte.d.ts +12 -22
  207. package/dist/docs/TilesetField.svelte +20 -19
  208. package/dist/docs/TilesetField.svelte.d.ts +6 -19
  209. package/dist/docs/TransformDebug.svelte +5 -6
  210. package/dist/docs/TransformDebug.svelte.d.ts +18 -14
  211. package/dist/docs/ViewSourceButton.svelte +7 -4
  212. package/dist/docs/ViewSourceButton.svelte.d.ts +8 -18
  213. package/dist/types/d3-shape-extentions.d.ts +7 -0
  214. package/dist/utils/afterTick.d.ts +5 -0
  215. package/dist/utils/afterTick.js +8 -0
  216. package/dist/utils/arcText.svelte.d.ts +57 -0
  217. package/dist/utils/arcText.svelte.js +262 -0
  218. package/dist/utils/array.d.ts +9 -1
  219. package/dist/utils/array.js +13 -0
  220. package/dist/utils/attributes.d.ts +29 -0
  221. package/dist/utils/attributes.js +40 -0
  222. package/dist/utils/canvas.js +47 -10
  223. package/dist/utils/chart.d.ts +78 -0
  224. package/dist/utils/chart.js +512 -0
  225. package/dist/utils/color.d.ts +1 -0
  226. package/dist/utils/color.js +8 -0
  227. package/dist/utils/common.d.ts +3 -5
  228. package/dist/utils/common.js +3 -2
  229. package/dist/utils/connectorUtils.d.ts +21 -0
  230. package/dist/utils/connectorUtils.js +111 -0
  231. package/dist/utils/createId.d.ts +7 -0
  232. package/dist/utils/createId.js +9 -0
  233. package/dist/utils/debug.d.ts +1 -0
  234. package/dist/utils/debug.js +84 -0
  235. package/dist/utils/filterObject.d.ts +9 -0
  236. package/dist/utils/filterObject.js +12 -0
  237. package/dist/utils/graph/dagre.d.ts +34 -0
  238. package/dist/utils/graph/dagre.js +78 -0
  239. package/dist/utils/graph/dagre.test.d.ts +1 -0
  240. package/dist/utils/{graph.test.js → graph/dagre.test.js} +19 -33
  241. package/dist/utils/graph/sankey.d.ts +28 -0
  242. package/dist/utils/{graph.js → graph/sankey.js} +13 -41
  243. package/dist/utils/index.d.ts +3 -1
  244. package/dist/utils/index.js +3 -1
  245. package/dist/utils/key.svelte.d.ts +3 -0
  246. package/dist/utils/key.svelte.js +11 -0
  247. package/dist/utils/legendPayload.d.ts +7 -0
  248. package/dist/utils/legendPayload.js +8 -0
  249. package/dist/utils/motion.svelte.d.ts +140 -0
  250. package/dist/utils/motion.svelte.js +180 -0
  251. package/dist/utils/motion.test.d.ts +1 -0
  252. package/dist/utils/motion.test.js +213 -0
  253. package/dist/utils/{rect.d.ts → rect.svelte.d.ts} +7 -4
  254. package/dist/utils/rect.svelte.js +105 -0
  255. package/dist/utils/scales.svelte.d.ts +91 -0
  256. package/dist/utils/scales.svelte.js +201 -0
  257. package/dist/utils/stack.d.ts +2 -3
  258. package/dist/utils/stack.js +1 -1
  259. package/dist/utils/string.js +87 -0
  260. package/dist/utils/ticks.d.ts +9 -3
  261. package/dist/utils/ticks.js +122 -147
  262. package/dist/utils/ticks.test.d.ts +1 -0
  263. package/dist/utils/ticks.test.js +57 -0
  264. package/dist/utils/types.d.ts +81 -0
  265. package/package.json +28 -24
  266. package/dist/components/ChartContext.svelte +0 -295
  267. package/dist/components/ChartContext.svelte.d.ts +0 -139
  268. package/dist/components/TransformContext.svelte.d.ts +0 -158
  269. package/dist/stores/motionStore.d.ts +0 -30
  270. package/dist/stores/motionStore.js +0 -62
  271. package/dist/utils/graph.d.ts +0 -37
  272. package/dist/utils/rect.js +0 -107
  273. package/dist/utils/scales.d.ts +0 -66
  274. package/dist/utils/scales.js +0 -136
  275. /package/dist/{utils/graph.test.d.ts → components/charts/types.js} +0 -0
@@ -1,110 +1,225 @@
1
- <script lang="ts" context="module">
2
- import { getContext, setContext } from 'svelte';
1
+ <script lang="ts" module>
2
+ export type CanvasPropsWithoutHTML = {
3
+ /**
4
+ * The `<canvas>` tag. Useful for bindings.
5
+ *
6
+ * @bindable
7
+ */
8
+ ref?: HTMLCanvasElement;
9
+
10
+ /**
11
+ * The `<canvas>`'s 2d context. Useful for bindings.
12
+ *
13
+ * @bindable
14
+ */
15
+ canvasContext?: CanvasRenderingContext2D;
16
+
17
+ /**
18
+ * Force the use of a software (instead of hardware accelerated) 2D canvas, which can
19
+ * save memory when calling getImageData() frequently.
20
+ *
21
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext#willreadfrequently
22
+ *
23
+ * @default false
24
+ */
25
+ willReadFrequently?: boolean;
26
+
27
+ /**
28
+ * The `z-index` style to apply to the layer.
29
+ *
30
+ * @default 0
31
+ */
32
+ zIndex?: number;
33
+
34
+ /**
35
+ *
36
+ * Whether pointer events should be enabled on the canvas.
37
+ *
38
+ * - `false`: `pointer-events: none;` will be set on the entire layer.
39
+ * - `true`: pointer events will operate normally.
40
+ *
41
+ * @default true
42
+ */
43
+ pointerEvents?: boolean;
44
+
45
+ /**
46
+ * The content to display if canvas is not supported or cannot be rendered.
47
+ * This can either be a string or a snippet with custom markup.
48
+ */
49
+ fallback?: string | Snippet;
50
+
51
+ /**
52
+ * Translate children to center of the canvas (useful for radial layouts).
53
+ *
54
+ * @default false
55
+ */
56
+ center?: boolean | 'x' | 'y';
57
+
58
+ /**
59
+ * Ignore TransformContext.
60
+ *
61
+ * Useful to add static elements such as legends.
62
+ *
63
+ * @default false
64
+ */
65
+ ignoreTransform?: boolean;
66
+
67
+ /**
68
+ * Disable the hit canvas (useful when animations are playing)
69
+ *
70
+ * @default false
71
+ */
72
+ disableHitCanvas?: boolean;
73
+
74
+ /**
75
+ * Show the hit canvas for debugging purposes.
76
+ *
77
+ * @default false
78
+ */
79
+ debug?: boolean;
80
+
81
+ children?: Snippet<
82
+ [{ ref: HTMLCanvasElement; canvasContext: CanvasRenderingContext2D | undefined }]
83
+ >;
84
+ };
85
+
86
+ export type CanvasProps = CanvasPropsWithoutHTML &
87
+ Without<HTMLCanvasAttributes, CanvasPropsWithoutHTML>;
3
88
 
4
- type ComponentRender = {
89
+ type ComponentRender<T extends Element = Element> = {
5
90
  name: string;
6
91
  render: (ctx: CanvasRenderingContext2D, styleOverrides?: ComputedStylesOptions) => any;
7
92
  retainState?: boolean;
8
93
  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;
94
+ click?: MouseEventHandler<T> | null;
95
+ dblclick?: MouseEventHandler<T> | null;
96
+ pointerenter?: PointerEventHandler<T> | null;
97
+ pointerover?: PointerEventHandler<T> | null;
98
+ pointermove?: PointerEventHandler<T> | null;
99
+ pointerleave?: PointerEventHandler<T> | null;
100
+ pointerout?: PointerEventHandler<T> | null;
101
+ pointerdown?: PointerEventHandler<T> | null;
102
+ touchmove?: TouchEventHandler<T> | null;
18
103
  };
104
+ /**
105
+ * Optional dependencies to track and invalidate the canvas context when they change.
106
+ */
107
+ deps?: () => any[];
19
108
  };
20
109
 
21
- export type CanvasContext = {
22
- /** Register component to render. Returns method to unregister on component destory */
23
- register(component: ComponentRender): () => void;
110
+ export type CanvasContextValue = {
111
+ /**
112
+ * Register component to render.
113
+ *
114
+ * Returns method to unregister on component destroy
115
+ */
116
+ register<T extends Element>(component: ComponentRender<T>): () => void;
24
117
  invalidate(): void;
25
118
  };
26
119
 
27
- export const canvasContextKey = Symbol();
120
+ const CanvasContext = new Context<CanvasContextValue>('CanvasContext');
121
+
122
+ const defaultCanvasContext: CanvasContextValue = {
123
+ register: <T extends Element>(_: ComponentRender<T>) => {
124
+ return () => {};
125
+ },
126
+ invalidate: () => {},
127
+ };
28
128
 
29
129
  export function getCanvasContext() {
30
- return getContext<CanvasContext>(canvasContextKey);
130
+ return CanvasContext.getOr(defaultCanvasContext);
131
+ }
132
+
133
+ function setCanvasContext(context: CanvasContextValue) {
134
+ return CanvasContext.set(context);
31
135
  }
32
136
 
33
- function setCanvasContext(context: CanvasContext) {
34
- setContext(canvasContextKey, context);
137
+ /**
138
+ * Handles the automatic registration of the component to the canvas context,
139
+ * with dependency tracking and cleanup on destroy.
140
+ */
141
+ export function registerCanvasComponent<T extends Element>(component: ComponentRender<T>) {
142
+ const canvasContext = getCanvasContext();
143
+
144
+ $effect.pre(() => {
145
+ return untrack(() => canvasContext.register(component));
146
+ });
35
147
  }
36
148
  </script>
37
149
 
38
150
  <script lang="ts">
39
- import { onMount, onDestroy } from 'svelte';
151
+ import { onMount, untrack, type Snippet } from 'svelte';
40
152
  import { cls } from '@layerstack/tailwind';
41
153
  import { Logger, localPoint } from '@layerstack/utils';
42
- import { darkColorScheme } from '@layerstack/svelte-stores';
154
+ import { MediaQueryPresets } from '@layerstack/svelte-state';
43
155
 
44
156
  import { setRenderContext } from '../Chart.svelte';
45
- import { chartContext } from '../ChartContext.svelte';
46
- import { transformContext } from '../TransformContext.svelte';
157
+ import { getTransformContext } from '../TransformContext.svelte';
47
158
  import { getPixelColor, scaleCanvas, type ComputedStylesOptions } from '../../utils/canvas.js';
48
159
  import { getColorStr, rgbColorGenerator } from '../../utils/color.js';
160
+ import { Context, useMutationObserver, watch } from 'runed';
161
+ import type {
162
+ HTMLCanvasAttributes,
163
+ MouseEventHandler,
164
+ PointerEventHandler,
165
+ TouchEventHandler,
166
+ } from 'svelte/elements';
167
+ import type { Without } from '../../utils/types.js';
168
+ import { getChartContext } from '../Chart.svelte';
169
+ import { layerClass } from '../../utils/attributes.js';
170
+
171
+ let {
172
+ ref: refProp = $bindable(),
173
+ canvasContext: canvasContextProp = $bindable(),
174
+ willReadFrequently = false,
175
+ debug = false,
176
+ zIndex = 0,
177
+ pointerEvents = true,
178
+ fallback,
179
+ center = false,
180
+ ignoreTransform = false,
181
+ disableHitCanvas = false,
182
+ class: className,
183
+ children,
184
+ onclick,
185
+ ondblclick,
186
+ onpointerenter,
187
+ onpointermove,
188
+ onpointerleave,
189
+ onpointerdown,
190
+ ontouchmove,
191
+ ...restProps
192
+ }: CanvasProps = $props();
193
+
194
+ let ref = $state<HTMLCanvasElement>();
195
+ let context = $state<CanvasRenderingContext2D>();
196
+
197
+ $effect.pre(() => {
198
+ refProp = ref;
199
+ });
49
200
 
50
- const { width, height, containerWidth, containerHeight, padding } = chartContext();
51
-
52
- /** The `<canvas>` tag. Useful for bindings. */
53
- export let element: HTMLCanvasElement | undefined = undefined;
54
-
55
- /** The `<canvas>`'s 2d context. Useful for bindings. */
56
- // @ts-expect-error: set during onMount()
57
- export let context: CanvasRenderingContext2D = undefined;
58
-
59
- /** Force the use of a software (instead of hardware accelerated) 2D canvas and can save memory when calling getImageData() frequently.
60
- * see: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext#willreadfrequently */
61
- export let willReadFrequently = false;
62
-
63
- /** The layer's z-index. */
64
- export let zIndex = undefined;
65
-
66
- /** Set this to `false` to set `pointer-events: none;` on the entire layer. */
67
- export let pointerEvents: boolean | undefined = undefined;
68
-
69
- /** Text to display if the browser won't render a canvas tag. You can also set arbitrary HTML via the "fallback" slot but this is fine if you just need text. If you use the "fallback" slot, this prop is ignored. */
70
- export let fallback = '';
71
-
72
- /** A string passed to the `aria-label` on the `<canvas>` tag. */
73
- export let label: string | undefined = undefined;
74
-
75
- /** A string passed to the `aria-labelledby` on the `<canvas>` tag. */
76
- export let labelledBy: string | undefined = undefined;
77
-
78
- /** A string passed to `aria-describedby` property on the `<canvas>` tag. */
79
- export let describedBy: string | undefined = undefined;
80
-
81
- /**
82
- * Translate children to center (useful for radial layouts)
83
- */
84
- export let center: boolean | 'x' | 'y' = false;
85
-
86
- /** Ignore TransformContext. Useful to add static elements such as legends. */
87
- export let ignoreTransform = false;
201
+ $effect.pre(() => {
202
+ canvasContextProp = context;
203
+ });
88
204
 
89
- /** Show hit canvas for debugging */
90
- export let debug = false;
205
+ const ctx = getChartContext();
206
+ const transformCtx = getTransformContext();
91
207
 
92
208
  const logger = new Logger('Canvas');
93
209
 
94
- let components = new Map<Symbol, ComponentRender>();
210
+ let components = new Map<Symbol, ComponentRender<Element>>();
95
211
  let pendingInvalidation = false;
96
212
  let frameId: number | undefined;
97
213
 
98
- const { mode, scale, translate, dragging, moving } = transformContext();
99
-
100
214
  /**
101
215
  * HitCanvas
102
216
  */
103
- let hitCanvasElement: HTMLCanvasElement | undefined = undefined;
104
- let hitCanvasContext: CanvasRenderingContext2D | undefined = undefined;
217
+ let hitCanvasElement = $state<HTMLCanvasElement>();
218
+ let hitCanvasContext = $state<CanvasRenderingContext2D>();
105
219
  let colorGenerator = rgbColorGenerator();
106
- let activeCanvas = false;
107
- let lastActiveComponent: ComponentRender | undefined | null;
220
+ let activeCanvas = $state(false);
221
+ let lastActiveComponent: ComponentRender | null | undefined = null;
222
+
108
223
  const componentByColor = new Map<string, ComponentRender>();
109
224
 
110
225
  function getPointerComponent(e: PointerEvent | MouseEvent | TouchEvent) {
@@ -116,7 +231,7 @@
116
231
  return component;
117
232
  }
118
233
 
119
- function onPointerMove(e: PointerEvent) {
234
+ const onPointerMove: PointerEventHandler<Element> = (e) => {
120
235
  activeCanvas = true;
121
236
  const component = getPointerComponent(e);
122
237
 
@@ -133,9 +248,9 @@
133
248
  component?.events?.pointermove?.(e);
134
249
 
135
250
  lastActiveComponent = component;
136
- }
251
+ };
137
252
 
138
- function onPointerLeave(e: PointerEvent) {
253
+ const onPointerLeave: PointerEventHandler<Element> = (e) => {
139
254
  // Pointer outside of canvas
140
255
 
141
256
  // Call last active component `pointerleave` event in case it was not triggered by hit canvas (quickly exiting canvas element before `pointermove` is triggered)
@@ -144,170 +259,226 @@
144
259
 
145
260
  lastActiveComponent = null;
146
261
  activeCanvas = false;
147
- }
262
+ };
148
263
  /**
149
264
  * end HitCanvas
150
265
  */
151
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
+
152
284
  onMount(() => {
153
- context = element?.getContext('2d', { willReadFrequently }) as CanvasRenderingContext2D;
285
+ context = ref?.getContext('2d', { willReadFrequently }) as CanvasRenderingContext2D;
154
286
 
155
287
  hitCanvasContext = hitCanvasElement?.getContext('2d', {
156
288
  willReadFrequently: false, // Explicitly set to `false` to resolve pixel artifacts between fill and stroke with the same color (issue #372)
157
289
  }) as CanvasRenderingContext2D;
158
290
 
159
- // Invalidate/redraw if color scheme changes, either via browser `prefers-color-scheme` (including emulation) or by changing `<html class="dark">` or `<html data-theme="...">`
160
- darkColorScheme.subscribe(() => {
161
- canvasContext.invalidate();
162
- });
163
-
164
- const observer = new MutationObserver(() => {
165
- canvasContext.invalidate();
166
- });
167
-
168
- observer.observe(document.documentElement, {
169
- attributes: true,
170
- attributeFilter: ['class', 'data-theme'],
171
- });
172
-
173
291
  return () => {
174
- observer.disconnect();
292
+ if (frameId) {
293
+ cancelAnimationFrame(frameId);
294
+ }
175
295
  };
176
296
  });
177
297
 
178
- onDestroy(() => {
179
- if (frameId) {
180
- cancelAnimationFrame(frameId);
181
- }
182
- });
183
-
184
298
  function update() {
185
299
  if (!context) return;
186
- // TODO: only `scaleCanvas()` when containerWidth/Height change (not all invalidations)
187
- // scaleCanvas in `update()` to fix `requestAnimationFrame()` timing causing flash of blank canvas
188
- scaleCanvas(context, $containerWidth, $containerHeight);
189
300
 
190
- context.clearRect(0, 0, $containerWidth, $containerHeight);
301
+ // scale main canvas
302
+ scaleCanvas(context, ctx.containerWidth, ctx.containerHeight);
303
+ context.clearRect(0, 0, ctx.containerWidth, ctx.containerHeight);
304
+
305
+ // apply padding translation
306
+ context.translate(ctx.padding.left ?? 0, ctx.padding.top ?? 0);
191
307
 
192
- context.translate($padding.left ?? 0, $padding.top ?? 0);
308
+ let newTranslate: undefined | { x: number; y: number };
193
309
 
310
+ // apply centering or transform
194
311
  if (center) {
195
- const newTranslate = {
196
- x: center === 'x' || center === true ? $width / 2 : 0,
197
- y: center === 'y' || center === true ? $height / 2 : 0,
312
+ newTranslate = {
313
+ x: center === 'x' || center === true ? ctx.width / 2 : 0,
314
+ y: center === 'y' || center === true ? ctx.height / 2 : 0,
198
315
  };
199
316
  context.translate(newTranslate.x, newTranslate.y);
200
- } else if (mode === 'canvas' && !ignoreTransform) {
201
- context.translate($translate.x, $translate.y);
202
- context.scale($scale, $scale);
317
+ } else if (transformCtx.mode === 'canvas' && !ignoreTransform) {
318
+ context.translate(transformCtx.translate.x, transformCtx.translate.y);
319
+ context.scale(transformCtx.scale, transformCtx.scale);
203
320
  }
204
321
 
205
- // Sync hit canvas transform with main canvas
206
- if (hitCanvasContext) {
207
- scaleCanvas(hitCanvasContext, $containerWidth, $containerHeight);
208
- hitCanvasContext.clearRect(0, 0, $containerWidth, $containerHeight);
209
- hitCanvasContext.setTransform(context.getTransform());
210
-
211
- // Reset color generator whenever updated so always reusing same colors (and not exhausting)
212
- colorGenerator = rgbColorGenerator();
213
- }
322
+ // separate components into those that retain state and those that don't
323
+ const retainStateComponents: ComponentRender[] = [];
324
+ const nonRetainStateComponents: ComponentRender[] = [];
214
325
 
215
- components.forEach((c) => {
326
+ for (const [_, c] of components) {
216
327
  if (c.retainState) {
217
- // 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)
218
- c.render(context);
328
+ retainStateComponents.push(c);
219
329
  } else {
220
- context.save();
221
- c.render(context);
222
- context.restore();
330
+ nonRetainStateComponents.push(c);
223
331
  }
332
+ }
224
333
 
225
- // 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)
226
- const inactiveMoving = !activeCanvas && $moving;
334
+ // render retainState components on main canvas first
335
+ for (const c of retainStateComponents) {
336
+ c.render(context);
337
+ }
227
338
 
228
- const componentHasEvents = c.events && Object.values(c.events).filter((d) => d).length > 0;
339
+ // store the main canvas transform after retainState components
340
+ const mainTransformAfterRetain = context.getTransform();
229
341
 
230
- if (hitCanvasContext && componentHasEvents && !inactiveMoving && !$dragging) {
231
- const color = getColorStr(colorGenerator.next().value);
232
- // Stroking shape seems to help with dark border, but there is still antialising and thus gaps
233
- const styleOverrides = { styles: { fill: color, stroke: color, _fillOpacity: 0.1 } };
342
+ // render non-retainState components on main canvas
343
+ for (const c of nonRetainStateComponents) {
344
+ context.save();
345
+ c.render(context);
346
+ context.restore();
347
+ }
348
+
349
+ // sync hit canvas with main canvas
350
+ if (hitCanvasContext) {
351
+ // scale hit canvas to match main canvas
352
+ scaleCanvas(hitCanvasContext, ctx.containerWidth, ctx.containerHeight);
353
+ hitCanvasContext.clearRect(0, 0, ctx.containerWidth, ctx.containerHeight);
354
+
355
+ // reset and sync transform to the state after retainState components
356
+ hitCanvasContext.resetTransform();
357
+ hitCanvasContext.setTransform(mainTransformAfterRetain);
358
+
359
+ // reset color generator
360
+ colorGenerator = rgbColorGenerator();
361
+
362
+ const inactiveMoving = !activeCanvas && transformCtx.moving;
363
+
364
+ // render retainState components on hit canvas (e.g., Group)
365
+ for (const c of retainStateComponents) {
366
+ const componentHasEvents = c.events && Object.values(c.events).filter((d) => d).length > 0;
367
+
368
+ if (componentHasEvents && !inactiveMoving && !transformCtx.dragging) {
369
+ // since the transform was already applied via setTransform, skip rendering
370
+ // the retainState component's transform again; proceed to its children
371
+ continue;
372
+ }
373
+ }
374
+
375
+ // render non-retainState components on hit canvas
376
+ for (const c of nonRetainStateComponents) {
377
+ const componentHasEvents = c.events && Object.values(c.events).filter((d) => d).length > 0;
378
+
379
+ if (componentHasEvents && !inactiveMoving && !transformCtx.dragging && !disableHitCanvas) {
380
+ const color = getColorStr(colorGenerator.next().value);
381
+ const styleOverrides = { styles: { fill: color, stroke: color, _fillOpacity: 0.1 } };
234
382
 
235
- if (c.retainState) {
236
- c.render(hitCanvasContext, styleOverrides);
237
- } else {
238
383
  hitCanvasContext.save();
239
384
  c.render(hitCanvasContext, styleOverrides);
240
385
  hitCanvasContext.restore();
241
- }
242
386
 
243
- componentByColor.set(color, c);
387
+ componentByColor.set(color, c);
388
+ }
244
389
  }
245
- });
390
+ }
246
391
 
247
392
  pendingInvalidation = false;
248
393
  }
249
394
 
250
- const canvasContext: CanvasContext = {
251
- register(component) {
395
+ function createCanvasContext(): CanvasContextValue {
396
+ function register<T extends Element>(component: ComponentRender<T>) {
252
397
  const key = Symbol();
253
- components.set(key, component);
254
- this.invalidate();
398
+ components.set(key, component as ComponentRender<Element>);
399
+ invalidate();
400
+
401
+ const cleanupRoot = $effect.root(() => {
402
+ if (component.deps) {
403
+ $effect.pre(() => {
404
+ component.deps?.(); // track deps
405
+ invalidate(); // invalidate when deps change.
406
+ });
407
+ }
408
+ });
409
+
410
+ $effect.pre(() => {
411
+ return cleanupRoot;
412
+ });
255
413
 
256
- // Unregister
414
+ /**
415
+ * Removes the component from the registry and cleans up the invalidation
416
+ * effect
417
+ */
257
418
  return () => {
258
419
  components.delete(key);
259
- this.invalidate();
420
+ cleanupRoot();
421
+ invalidate();
260
422
  };
261
- },
262
- invalidate() {
423
+ }
424
+
425
+ function invalidate() {
263
426
  if (pendingInvalidation) return;
264
427
  pendingInvalidation = true;
265
428
  frameId = requestAnimationFrame(update);
266
- },
267
- };
429
+ }
268
430
 
269
- $: {
270
- // 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
271
- $containerWidth, $containerHeight && $dragging;
272
- canvasContext.invalidate();
431
+ return { register, invalidate };
273
432
  }
274
433
 
434
+ const canvasContext = createCanvasContext();
435
+
436
+ $effect.pre(() => {
437
+ [ctx.height, ctx.width, ctx.containerHeight, ctx.containerWidth, transformCtx.dragging];
438
+ canvasContext.invalidate();
439
+ });
440
+
275
441
  setCanvasContext(canvasContext);
276
442
  setRenderContext('canvas');
277
443
  </script>
278
444
 
279
445
  <canvas
280
- bind:this={element}
446
+ bind:this={ref}
281
447
  style:z-index={zIndex}
282
448
  class={cls(
283
- 'layercake-layout-canvas',
449
+ layerClass('layout-canvas'),
284
450
  'absolute top-0 left-0 w-full h-full',
285
451
  pointerEvents === false && 'pointer-events-none',
286
- $$props.class
452
+ className
287
453
  )}
288
- aria-label={label}
289
- aria-labelledby={labelledBy}
290
- aria-describedby={describedBy}
291
- on:click={(e) => {
454
+ onclick={(e) => {
292
455
  const component = getPointerComponent(e);
293
456
  component?.events?.click?.(e);
457
+ onclick?.(e);
294
458
  }}
295
- on:click
296
- on:dblclick={(e) => {
459
+ ondblclick={(e) => {
297
460
  const component = getPointerComponent(e);
298
461
  component?.events?.dblclick?.(e);
462
+ ondblclick?.(e);
299
463
  }}
300
- on:pointerdown={(e) => {
464
+ onpointerdown={(e) => {
301
465
  const component = getPointerComponent(e);
302
466
  component?.events?.pointerdown?.(e);
467
+ onpointerdown?.(e);
468
+ }}
469
+ onpointerenter={(e) => {
470
+ onpointerenter?.(e);
471
+ onPointerMove(e);
472
+ }}
473
+ onpointermove={(e) => {
474
+ onpointermove?.(e);
475
+ onPointerMove(e);
476
+ }}
477
+ onpointerleave={(e) => {
478
+ onpointerleave?.(e);
479
+ onPointerLeave(e);
303
480
  }}
304
- on:pointerenter={onPointerMove}
305
- on:pointerenter
306
- on:pointermove={onPointerMove}
307
- on:pointermove
308
- on:pointerleave={onPointerLeave}
309
- on:pointerleave
310
- on:touchmove={(e) => {
481
+ ontouchmove={(e) => {
311
482
  // Prevent touch from interfering with pointer if over data
312
483
  if (lastActiveComponent) {
313
484
  e.preventDefault();
@@ -316,17 +487,22 @@
316
487
  const component = getPointerComponent(e);
317
488
  component?.events?.touchmove?.(e);
318
489
  }}
319
- on:touchmove
490
+ {...restProps}
320
491
  >
321
- <slot name="fallback">
322
- {fallback || ''}
323
- </slot>
492
+ {#if fallback}
493
+ {#if typeof fallback === 'function'}
494
+ {@render fallback()}
495
+ {:else}
496
+ {fallback}
497
+ {/if}
498
+ {/if}
324
499
  </canvas>
325
500
 
326
501
  <!-- Hit canvas used for hidden context -->
327
502
  <canvas
328
503
  bind:this={hitCanvasElement}
329
504
  class={cls(
505
+ layerClass('hit-canvas'),
330
506
  'layerchart-hitcanvas',
331
507
  'absolute top-0 left-0 w-full h-full',
332
508
  'pointer-events-none', // events all handled by main canvas
@@ -336,4 +512,4 @@
336
512
  )}
337
513
  ></canvas>
338
514
 
339
- <slot {element} {context}></slot>
515
+ {@render children?.({ ref, canvasContext: context })}