svelteplot 0.0.1-alpha.9 → 0.1.3-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 (251) hide show
  1. package/LICENSE.md +5 -0
  2. package/README.md +3 -36
  3. package/dist/Mark.svelte +290 -0
  4. package/dist/Mark.svelte.d.ts +22 -0
  5. package/dist/Plot.svelte +148 -156
  6. package/dist/Plot.svelte.d.ts +15 -15
  7. package/dist/constants.d.ts +14 -0
  8. package/dist/constants.js +109 -0
  9. package/dist/core/Facet.svelte +59 -0
  10. package/dist/core/Facet.svelte.d.ts +18 -0
  11. package/dist/core/FacetAxes.svelte +66 -0
  12. package/dist/core/FacetAxes.svelte.d.ts +4 -0
  13. package/dist/core/FacetGrid.svelte +86 -0
  14. package/dist/core/FacetGrid.svelte.d.ts +13 -0
  15. package/dist/core/Plot.svelte +567 -0
  16. package/dist/core/Plot.svelte.d.ts +14 -0
  17. package/dist/helpers/arrowPath.d.ts +14 -0
  18. package/dist/helpers/arrowPath.js +129 -0
  19. package/dist/helpers/autoProjection.d.ts +19 -0
  20. package/dist/helpers/autoProjection.js +87 -0
  21. package/dist/helpers/autoScales.d.ts +23 -0
  22. package/dist/helpers/autoScales.js +203 -0
  23. package/dist/helpers/autoTicks.d.ts +3 -0
  24. package/dist/helpers/autoTicks.js +40 -0
  25. package/dist/helpers/autoTimeFormat.d.ts +2 -2
  26. package/dist/helpers/autoTimeFormat.js +34 -5
  27. package/dist/helpers/callWithProps.d.ts +8 -0
  28. package/dist/helpers/callWithProps.js +13 -0
  29. package/dist/helpers/colors.js +17 -2
  30. package/dist/helpers/curves.d.ts +3 -0
  31. package/dist/helpers/curves.js +42 -0
  32. package/dist/helpers/data.d.ts +9 -0
  33. package/dist/helpers/data.js +16 -0
  34. package/dist/helpers/facets.d.ts +12 -0
  35. package/dist/helpers/facets.js +49 -0
  36. package/dist/helpers/formats.d.ts +3 -0
  37. package/dist/helpers/formats.js +3 -0
  38. package/dist/helpers/getBaseStyles.d.ts +7 -2
  39. package/dist/helpers/getBaseStyles.js +34 -10
  40. package/dist/helpers/getLogTicks.js +5 -5
  41. package/dist/helpers/group.d.ts +6 -0
  42. package/dist/helpers/group.js +53 -0
  43. package/dist/helpers/index.d.ts +18 -0
  44. package/dist/helpers/index.js +53 -0
  45. package/dist/helpers/isRawValue.d.ts +2 -0
  46. package/dist/helpers/isRawValue.js +5 -0
  47. package/dist/helpers/isValid.d.ts +6 -0
  48. package/dist/helpers/isValid.js +6 -0
  49. package/dist/helpers/math.d.ts +19 -0
  50. package/dist/helpers/math.js +116 -0
  51. package/dist/helpers/mergeDeep.d.ts +1 -1
  52. package/dist/helpers/noise.d.ts +1 -0
  53. package/dist/helpers/noise.js +72 -0
  54. package/dist/helpers/projection.d.ts +33 -0
  55. package/dist/helpers/projection.js +100 -0
  56. package/dist/helpers/reduce.d.ts +10 -0
  57. package/dist/helpers/reduce.js +85 -0
  58. package/dist/helpers/regressionLoess.d.ts +12 -0
  59. package/dist/helpers/regressionLoess.js +47 -0
  60. package/dist/helpers/removeIdenticalLines.d.ts +8 -1
  61. package/dist/helpers/removeIdenticalLines.js +14 -7
  62. package/dist/helpers/resolve.d.ts +17 -0
  63. package/dist/helpers/resolve.js +152 -0
  64. package/dist/helpers/roundedRect.d.ts +9 -0
  65. package/dist/helpers/roundedRect.js +31 -0
  66. package/dist/helpers/scales.d.ts +42 -0
  67. package/dist/helpers/scales.js +309 -0
  68. package/dist/helpers/time.d.ts +6 -0
  69. package/dist/helpers/time.js +282 -0
  70. package/dist/helpers/typeChecks.d.ts +8 -5
  71. package/dist/helpers/typeChecks.js +27 -6
  72. package/dist/index.d.ts +49 -1
  73. package/dist/index.js +53 -2
  74. package/dist/marks/Area.svelte +146 -0
  75. package/dist/marks/Area.svelte.d.ts +30 -0
  76. package/dist/marks/AreaX.svelte +27 -0
  77. package/dist/marks/AreaX.svelte.d.ts +12 -0
  78. package/dist/marks/AreaY.svelte +38 -0
  79. package/dist/marks/AreaY.svelte.d.ts +19 -0
  80. package/dist/marks/Arrow.svelte +139 -0
  81. package/dist/marks/Arrow.svelte.d.ts +44 -0
  82. package/dist/marks/AxisX.svelte +198 -93
  83. package/dist/marks/AxisX.svelte.d.ts +17 -16
  84. package/dist/marks/AxisY.svelte +176 -62
  85. package/dist/marks/AxisY.svelte.d.ts +17 -14
  86. package/dist/marks/BarX.svelte +81 -0
  87. package/dist/marks/BarX.svelte.d.ts +4 -0
  88. package/dist/marks/BarY.svelte +95 -0
  89. package/dist/marks/BarY.svelte.d.ts +4 -0
  90. package/dist/marks/BollingerX.svelte +44 -0
  91. package/dist/marks/BollingerX.svelte.d.ts +18 -0
  92. package/dist/marks/BollingerY.svelte +39 -0
  93. package/dist/marks/BollingerY.svelte.d.ts +18 -0
  94. package/dist/marks/BoxX.svelte +89 -0
  95. package/dist/marks/BoxX.svelte.d.ts +4 -0
  96. package/dist/marks/BoxY.svelte +110 -0
  97. package/dist/marks/BoxY.svelte.d.ts +29 -0
  98. package/dist/marks/Cell.svelte +110 -0
  99. package/dist/marks/Cell.svelte.d.ts +16 -0
  100. package/dist/marks/CellX.svelte +24 -0
  101. package/dist/marks/CellX.svelte.d.ts +3 -0
  102. package/dist/marks/CellY.svelte +24 -0
  103. package/dist/marks/CellY.svelte.d.ts +3 -0
  104. package/dist/marks/ColorLegend.svelte +148 -27
  105. package/dist/marks/ColorLegend.svelte.d.ts +12 -13
  106. package/dist/marks/CustomMark.svelte +43 -0
  107. package/dist/marks/CustomMark.svelte.d.ts +16 -0
  108. package/dist/marks/CustomMarkHTML.svelte +103 -0
  109. package/dist/marks/CustomMarkHTML.svelte.d.ts +17 -0
  110. package/dist/marks/DifferenceY.svelte +144 -0
  111. package/dist/marks/DifferenceY.svelte.d.ts +30 -0
  112. package/dist/marks/Dot.svelte +128 -73
  113. package/dist/marks/Dot.svelte.d.ts +24 -14
  114. package/dist/marks/DotX.svelte +15 -3
  115. package/dist/marks/DotX.svelte.d.ts +8 -16
  116. package/dist/marks/DotY.svelte +8 -3
  117. package/dist/marks/DotY.svelte.d.ts +5 -17
  118. package/dist/marks/Frame.svelte +32 -31
  119. package/dist/marks/Frame.svelte.d.ts +7 -14
  120. package/dist/marks/Geo.svelte +102 -0
  121. package/dist/marks/Geo.svelte.d.ts +10 -0
  122. package/dist/marks/Graticule.svelte +28 -0
  123. package/dist/marks/Graticule.svelte.d.ts +9 -0
  124. package/dist/marks/GridX.svelte +67 -36
  125. package/dist/marks/GridX.svelte.d.ts +7 -18
  126. package/dist/marks/GridY.svelte +64 -25
  127. package/dist/marks/GridY.svelte.d.ts +7 -14
  128. package/dist/marks/HTMLTooltip.svelte +91 -0
  129. package/dist/marks/HTMLTooltip.svelte.d.ts +11 -0
  130. package/dist/marks/Line.svelte +219 -58
  131. package/dist/marks/Line.svelte.d.ts +30 -14
  132. package/dist/marks/LineX.svelte +8 -8
  133. package/dist/marks/LineX.svelte.d.ts +4 -17
  134. package/dist/marks/LineY.svelte +7 -8
  135. package/dist/marks/LineY.svelte.d.ts +4 -17
  136. package/dist/marks/Link.svelte +173 -0
  137. package/dist/marks/Link.svelte.d.ts +21 -0
  138. package/dist/marks/Pointer.svelte +126 -0
  139. package/dist/marks/Pointer.svelte.d.ts +23 -0
  140. package/dist/marks/Rect.svelte +103 -0
  141. package/dist/marks/Rect.svelte.d.ts +15 -0
  142. package/dist/marks/RectX.svelte +33 -0
  143. package/dist/marks/RectX.svelte.d.ts +15 -0
  144. package/dist/marks/RectY.svelte +33 -0
  145. package/dist/marks/RectY.svelte.d.ts +15 -0
  146. package/dist/marks/RegressionX.svelte +26 -0
  147. package/dist/marks/RegressionX.svelte.d.ts +4 -0
  148. package/dist/marks/RegressionY.svelte +26 -0
  149. package/dist/marks/RegressionY.svelte.d.ts +4 -0
  150. package/dist/marks/RuleX.svelte +52 -28
  151. package/dist/marks/RuleX.svelte.d.ts +14 -14
  152. package/dist/marks/RuleY.svelte +52 -28
  153. package/dist/marks/RuleY.svelte.d.ts +14 -14
  154. package/dist/marks/Sphere.svelte +8 -0
  155. package/dist/marks/Sphere.svelte.d.ts +51 -0
  156. package/dist/marks/Spike.svelte +15 -0
  157. package/dist/marks/Spike.svelte.d.ts +4 -0
  158. package/dist/marks/SymbolLegend.svelte +27 -12
  159. package/dist/marks/SymbolLegend.svelte.d.ts +8 -14
  160. package/dist/marks/Text.svelte +185 -0
  161. package/dist/marks/Text.svelte.d.ts +26 -0
  162. package/dist/marks/TickX.svelte +89 -0
  163. package/dist/marks/TickX.svelte.d.ts +22 -0
  164. package/dist/marks/TickY.svelte +90 -0
  165. package/dist/marks/TickY.svelte.d.ts +22 -0
  166. package/dist/marks/Vector.svelte +213 -0
  167. package/dist/marks/Vector.svelte.d.ts +31 -0
  168. package/dist/marks/helpers/BaseAxisX.svelte +210 -0
  169. package/dist/marks/helpers/BaseAxisX.svelte.d.ts +24 -0
  170. package/dist/marks/helpers/BaseAxisY.svelte +187 -0
  171. package/dist/marks/helpers/BaseAxisY.svelte.d.ts +23 -0
  172. package/dist/marks/helpers/CanvasLayer.svelte +38 -0
  173. package/dist/marks/helpers/CanvasLayer.svelte.d.ts +13 -0
  174. package/dist/marks/helpers/DotCanvas.svelte +184 -0
  175. package/dist/marks/helpers/DotCanvas.svelte.d.ts +11 -0
  176. package/dist/marks/helpers/GeoCanvas.svelte +165 -0
  177. package/dist/marks/helpers/GeoCanvas.svelte.d.ts +13 -0
  178. package/dist/marks/helpers/GroupMultiple.svelte +17 -0
  179. package/dist/marks/helpers/GroupMultiple.svelte.d.ts +9 -0
  180. package/dist/marks/helpers/Marker.svelte +93 -0
  181. package/dist/marks/helpers/Marker.svelte.d.ts +10 -0
  182. package/dist/marks/helpers/MarkerPath.svelte +164 -0
  183. package/dist/marks/helpers/MarkerPath.svelte.d.ts +44 -0
  184. package/dist/marks/helpers/Regression.svelte +174 -0
  185. package/dist/marks/helpers/Regression.svelte.d.ts +26 -0
  186. package/dist/marks/helpers/events.d.ts +8 -0
  187. package/dist/marks/helpers/events.js +64 -0
  188. package/dist/transforms/bin.d.ts +51 -0
  189. package/dist/transforms/bin.js +171 -0
  190. package/dist/transforms/bollinger.d.ts +21 -0
  191. package/dist/transforms/bollinger.js +53 -0
  192. package/dist/transforms/centroid.d.ts +9 -0
  193. package/dist/transforms/centroid.js +13 -0
  194. package/dist/transforms/facet.d.ts +1 -0
  195. package/dist/transforms/facet.js +1 -0
  196. package/dist/transforms/filter.d.ts +2 -0
  197. package/dist/transforms/filter.js +8 -0
  198. package/dist/transforms/group.d.ts +66 -0
  199. package/dist/transforms/group.js +109 -0
  200. package/dist/transforms/interval.d.ts +11 -0
  201. package/dist/transforms/interval.js +34 -0
  202. package/dist/transforms/jitter.d.ts +0 -0
  203. package/dist/transforms/jitter.js +1 -0
  204. package/dist/transforms/map.d.ts +10 -0
  205. package/dist/transforms/map.js +89 -0
  206. package/dist/transforms/normalize.d.ts +9 -0
  207. package/dist/transforms/normalize.js +86 -0
  208. package/dist/transforms/recordize.d.ts +13 -0
  209. package/dist/transforms/recordize.js +75 -0
  210. package/dist/transforms/rename.d.ts +14 -0
  211. package/dist/transforms/rename.js +42 -0
  212. package/dist/transforms/select.d.ts +35 -0
  213. package/dist/transforms/select.js +55 -0
  214. package/dist/transforms/shift.d.ts +13 -0
  215. package/dist/transforms/shift.js +45 -0
  216. package/dist/transforms/sort.d.ts +28 -0
  217. package/dist/transforms/sort.js +61 -0
  218. package/dist/transforms/stack.d.ts +10 -0
  219. package/dist/transforms/stack.js +110 -0
  220. package/dist/transforms/window.d.ts +22 -0
  221. package/dist/transforms/window.js +73 -0
  222. package/dist/types.d.ts +625 -188
  223. package/dist/ui/Checkbox.svelte +6 -0
  224. package/dist/ui/Checkbox.svelte.d.ts +13 -0
  225. package/dist/ui/RadioInput.svelte +27 -0
  226. package/dist/ui/RadioInput.svelte.d.ts +9 -0
  227. package/dist/ui/Select.svelte +27 -0
  228. package/dist/ui/Select.svelte.d.ts +9 -0
  229. package/dist/ui/Slider.svelte +47 -0
  230. package/dist/ui/Slider.svelte.d.ts +11 -0
  231. package/dist/ui/Spiral.svelte +46 -0
  232. package/dist/ui/Spiral.svelte.d.ts +15 -0
  233. package/dist/ui/index.d.ts +4 -0
  234. package/dist/ui/index.js +4 -0
  235. package/package.json +79 -40
  236. package/LICENSE +0 -11
  237. package/dist/classes/Channel.svelte.js +0 -74
  238. package/dist/classes/Mark.svelte.js +0 -17
  239. package/dist/classes/Plot.svelte.js +0 -98
  240. package/dist/contants.d.ts +0 -3
  241. package/dist/contants.js +0 -40
  242. package/dist/helpers/GroupMultiple.svelte +0 -8
  243. package/dist/helpers/GroupMultiple.svelte.d.ts +0 -19
  244. package/dist/helpers/createScale.d.ts +0 -5
  245. package/dist/helpers/createScale.js +0 -57
  246. package/dist/helpers/resolveChannel.d.ts +0 -2
  247. package/dist/helpers/resolveChannel.js +0 -28
  248. package/dist/helpers/wrapArray.d.ts +0 -2
  249. package/dist/helpers/wrapArray.js +0 -4
  250. package/dist/marks/BaseMark.svelte +0 -22
  251. package/dist/marks/BaseMark.svelte.d.ts +0 -19
@@ -0,0 +1,165 @@
1
+ <script lang="ts">
2
+ import type { PlotState, Mark, DataRecord, BaseMarkProps } from '../../types.js';
3
+ import { CSS_VAR } from '../../constants.js';
4
+ import { testFilter } from '../../helpers/index.js';
5
+ import { resolveProp, resolveScaledStyleProps } from '../../helpers/resolve.js';
6
+ import { untrack } from 'svelte';
7
+ import { isEqual } from 'es-toolkit';
8
+ import { type GeoPath } from 'd3-geo';
9
+
10
+ let canvas: HTMLCanvasElement | undefined = $state();
11
+ let devicePixelRatio = $state(1);
12
+
13
+ let {
14
+ mark,
15
+ plot,
16
+ data,
17
+ testFacet,
18
+ usedScales,
19
+ path
20
+ }: {
21
+ mark: Mark<BaseMarkProps>;
22
+ plot: PlotState;
23
+ data: DataRecord[];
24
+ testFacet: any;
25
+ usedScales: any;
26
+ path: GeoPath;
27
+ } = $props();
28
+
29
+ function scaleHash(scale) {
30
+ return { domain: scale.domain, type: scale.type, range: scale.range };
31
+ }
32
+
33
+ let _plotSize = $state([plot.width, plot.height]);
34
+ let _usedScales = $state(usedScales);
35
+ let _markOptions = $state(mark.options);
36
+
37
+ const filteredData = $derived(
38
+ data.filter((datum) => testFilter(datum, _markOptions) && testFacet(datum, _markOptions))
39
+ );
40
+
41
+ let _filteredData: DataRecord[] = $state([]);
42
+
43
+ $effect(() => {
44
+ // update _usedScales only if changed
45
+ if (!isEqual(usedScales, _usedScales)) _usedScales = usedScales;
46
+ if (!isEqual(mark.options, _markOptions)) _markOptions = mark.options;
47
+
48
+ const plotSize = [plot.width, plot.height];
49
+ if (!isEqual(plotSize, _plotSize)) _plotSize = plotSize;
50
+
51
+ if (
52
+ _markOptions.filter
53
+ ? !isEqual(filteredData, _filteredData)
54
+ : filteredData.length !== _filteredData.length
55
+ ) {
56
+ _filteredData = filteredData;
57
+ }
58
+ });
59
+
60
+ $effect(() => {
61
+ // track plot size, since we're untracking the scales
62
+ _plotSize;
63
+ _markOptions;
64
+
65
+ const plotScales = untrack(() => plot.scales);
66
+ const context = canvas.getContext('2d');
67
+ if (context === null) return;
68
+ // this will re-run whenever `color` or `size` change
69
+ context.resetTransform();
70
+ context.scale(devicePixelRatio, devicePixelRatio);
71
+
72
+ let currentColor;
73
+
74
+ path.context(context);
75
+
76
+ const plot_ = untrack(() => plot);
77
+
78
+ for (const datum of _filteredData) {
79
+ // untrack the filter test to avoid redrawing when not necessary
80
+ let { stroke, fill, opacity, ...restStyles } = resolveScaledStyleProps(
81
+ datum,
82
+ _markOptions,
83
+ _usedScales,
84
+ plot_,
85
+ 'fill'
86
+ );
87
+
88
+ const fillOpacity = restStyles['fill-opacity'];
89
+ const strokeOpacity = restStyles['stroke-opacity'];
90
+
91
+ if (`${fill}`.toLowerCase() === 'currentcolor')
92
+ fill =
93
+ currentColor ||
94
+ (currentColor = getComputedStyle(
95
+ canvas?.parentElement?.parentElement
96
+ ).getPropertyValue('color'));
97
+ if (`${stroke}`.toLowerCase() === 'currentcolor')
98
+ stroke =
99
+ currentColor ||
100
+ (currentColor = getComputedStyle(
101
+ canvas?.parentElement?.parentElement
102
+ ).getPropertyValue('color'));
103
+ if (CSS_VAR.test(fill))
104
+ fill = getComputedStyle(canvas).getPropertyValue(fill.slice(4, -1));
105
+ if (CSS_VAR.test(stroke))
106
+ stroke = getComputedStyle(canvas).getPropertyValue(stroke.slice(4, -1));
107
+
108
+ if (stroke && stroke !== 'none') {
109
+ const strokeWidth = resolveProp(_markOptions.strokeWidth, datum, 1.6);
110
+ context.lineWidth = strokeWidth;
111
+ }
112
+ context.fillStyle = fill ? fill : 'none';
113
+ context.strokeStyle = stroke ? stroke : 'none';
114
+
115
+ context.beginPath();
116
+ path(datum);
117
+ context.closePath();
118
+
119
+ if (opacity != null) context.globalAlpha = opacity ?? 1;
120
+ if (fillOpacity != null) context.globalAlpha = (opacity ?? 1) * fillOpacity;
121
+
122
+ if (fill && fill !== 'none') context.fill();
123
+ if (strokeOpacity != null) context.globalAlpha = (opacity ?? 1) * strokeOpacity;
124
+ if (stroke && stroke !== 'none') context.stroke();
125
+ }
126
+ return () => {
127
+ canvas?.getContext('2d')?.clearRect(0, 0, canvas?.width, canvas?.height);
128
+ };
129
+ });
130
+
131
+ // code from https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
132
+ let remove: null | (() => void) = null;
133
+
134
+ function updatePixelRatio() {
135
+ if (remove != null) {
136
+ remove();
137
+ }
138
+ const mqString = `(resolution: ${window.devicePixelRatio}dppx)`;
139
+ const media = matchMedia(mqString);
140
+ media.addEventListener('change', updatePixelRatio);
141
+ remove = () => {
142
+ media.removeEventListener('change', updatePixelRatio);
143
+ };
144
+ devicePixelRatio = window.devicePixelRatio;
145
+ }
146
+ $effect(() => {
147
+ updatePixelRatio();
148
+ });
149
+ </script>
150
+
151
+ <foreignObject x="0" y="0" width={plot.width} height={plot.height}>
152
+ <canvas
153
+ xmlns="http://www.w3.org/1999/xhtml"
154
+ bind:this={canvas}
155
+ width={plot.width * devicePixelRatio}
156
+ height={plot.height * devicePixelRatio}
157
+ style="width: {plot.width}px; height: {plot.height}px;"></canvas>
158
+ </foreignObject>
159
+
160
+ <style>
161
+ foreignObject,
162
+ canvas {
163
+ color: currentColor;
164
+ }
165
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { PlotState, Mark, DataRecord, BaseMarkProps } from '../../types.js';
2
+ import { type GeoPath } from 'd3-geo';
3
+ type $$ComponentProps = {
4
+ mark: Mark<BaseMarkProps>;
5
+ plot: PlotState;
6
+ data: DataRecord[];
7
+ testFacet: any;
8
+ usedScales: any;
9
+ path: GeoPath;
10
+ };
11
+ declare const GeoCanvas: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type GeoCanvas = ReturnType<typeof GeoCanvas>;
13
+ export default GeoCanvas;
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ let {
4
+ length = 1,
5
+ children,
6
+ class: className = null,
7
+ ...groupProps
8
+ }: { children: Snippet; length?: number; class?: string | null } = $props();
9
+ </script>
10
+
11
+ {#if length > 1 || className}
12
+ <g class={className} {...groupProps}>
13
+ {@render children()}
14
+ </g>
15
+ {:else}
16
+ {@render children()}
17
+ {/if}
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children: Snippet;
4
+ length?: number;
5
+ class?: string | null;
6
+ };
7
+ declare const GroupMultiple: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type GroupMultiple = ReturnType<typeof GroupMultiple>;
9
+ export default GroupMultiple;
@@ -0,0 +1,93 @@
1
+ <script module lang="ts">
2
+ export type MarkerShape =
3
+ | 'dot'
4
+ | 'circle'
5
+ | 'circle-stroke'
6
+ | 'arrow'
7
+ | 'arrow-reverse'
8
+ | 'tick'
9
+ | 'tick-x'
10
+ | 'tick-y';
11
+ </script>
12
+
13
+ <!--
14
+ @component Marker is a helper component that creates a marker for use in a line or path.
15
+ -->
16
+
17
+ <script lang="ts">
18
+ import { getContext } from 'svelte';
19
+
20
+ type MarkerProps = {
21
+ id: string;
22
+ shape: MarkerShape;
23
+ color: string;
24
+ };
25
+
26
+ let { id, shape, color }: MarkerProps = $props();
27
+
28
+ const tickMarker = (orient: number | 'auto') => ({
29
+ viewBox: '-3 -3 6 6',
30
+ path: 'M0,-3v6',
31
+ width: 6,
32
+ height: 6,
33
+ orient,
34
+ color: 'stroke'
35
+ });
36
+
37
+ const MARKERS: Record<
38
+ MarkerShape,
39
+ {
40
+ path?: string;
41
+ width: number;
42
+ height: number;
43
+ orient: number | 'auto';
44
+ color: 'fill' | 'stroke';
45
+ bg?: 'fill' | 'stroke';
46
+ viewBox?: string;
47
+ }
48
+ > = {
49
+ circle: { width: 6.67, height: 6.67, orient: 0, color: 'fill', bg: 'stroke' },
50
+ dot: { width: 6.67, height: 6.67, orient: 0, color: 'fill' },
51
+ 'circle-stroke': { width: 6.67, height: 6.67, orient: 0, color: 'stroke', bg: 'fill' },
52
+ tick: tickMarker('auto'),
53
+ 'tick-x': tickMarker(90),
54
+ 'tick-y': tickMarker(0),
55
+ arrow: {
56
+ path: 'M-1.5,-3l3,3l-3,3',
57
+ width: 6.67,
58
+ height: 6.67,
59
+ orient: 'auto',
60
+ color: 'stroke'
61
+ },
62
+ 'arrow-reverse': {
63
+ path: 'M1.5,-3l-3,3l3,3',
64
+ width: 6.67,
65
+ height: 6.67,
66
+ orient: 'auto',
67
+ color: 'stroke'
68
+ }
69
+ };
70
+
71
+ const defaultDotRadius = getContext('svelteplot/_defaults').markerDotRadius;
72
+
73
+ const markerColors = $derived({
74
+ fill: 'none',
75
+ [MARKERS[shape].color]: color,
76
+ ...(MARKERS[shape].bg ? { [MARKERS[shape].bg as string]: 'var(--svelteplot-bg)' } : {})
77
+ });
78
+ </script>
79
+
80
+ <marker
81
+ {id}
82
+ viewBox={MARKERS[shape].viewBox || '-5 -5 10 10'}
83
+ markerWidth={MARKERS[shape].width}
84
+ orient={MARKERS[shape].orient}
85
+ markerHeight={MARKERS[shape].height}
86
+ stroke-width="1.5"
87
+ {...markerColors}>
88
+ {#if shape === 'dot' || shape === 'circle' || shape === 'circle-stroke'}
89
+ <circle r={defaultDotRadius} />
90
+ {:else}
91
+ <path d={MARKERS[shape].path} />
92
+ {/if}
93
+ </marker>
@@ -0,0 +1,10 @@
1
+ export type MarkerShape = 'dot' | 'circle' | 'circle-stroke' | 'arrow' | 'arrow-reverse' | 'tick' | 'tick-x' | 'tick-y';
2
+ type MarkerProps = {
3
+ id: string;
4
+ shape: MarkerShape;
5
+ color: string;
6
+ };
7
+ /** Marker is a helper component that creates a marker for use in a line or path. */
8
+ declare const Marker: import("svelte").Component<MarkerProps, {}, "">;
9
+ type Marker = ReturnType<typeof Marker>;
10
+ export default Marker;
@@ -0,0 +1,164 @@
1
+ <!--
2
+ @component
3
+ Helper component for paths with markers and optional text along the path.
4
+ -->
5
+ <script lang="ts">
6
+ import Marker, { type MarkerShape } from './Marker.svelte';
7
+ import { isSnippet, randomId } from '../../helpers/index.js';
8
+ import { resolveProp } from '../../helpers/resolve.js';
9
+ import type {
10
+ BaseMarkProps,
11
+ ConstantAccessor,
12
+ DataRecord,
13
+ Mark,
14
+ PlotContext,
15
+ PlotScales
16
+ } from '../../types.js';
17
+ import { addEventHandlers } from './events.js';
18
+ import { getContext } from 'svelte';
19
+
20
+ type MarkerPathProps = BaseMarkProps & {
21
+ /**
22
+ * the datum associated with this path, usually the first
23
+ * element of the data array group
24
+ */
25
+ datum: DataRecord;
26
+ /**
27
+ * the marker shape to use at the start of the path, defaults to
28
+ * cirlce
29
+ */
30
+ markerStart?: boolean | MarkerShape;
31
+ /**
32
+ * the marker shape to use at the middle of the path, defaults to circle
33
+ */
34
+ markerMid?: boolean | MarkerShape;
35
+ /**
36
+ * the marker shape to use at the end of the path, defaults to circle
37
+ */
38
+ markerEnd?: boolean | MarkerShape;
39
+ /**
40
+ * shorthand for setting all markers
41
+ */
42
+ marker?: boolean | MarkerShape;
43
+ /**
44
+ * path string
45
+ */
46
+ d: string;
47
+ style: string;
48
+ startOffset: string;
49
+ textStyle: string;
50
+ textStyleClass?: string | null;
51
+ text: string;
52
+ transform: string;
53
+ color: string;
54
+ strokeWidth: ConstantAccessor<number>;
55
+ mark: Mark<BaseMarkProps>;
56
+ scales: PlotScales;
57
+ };
58
+
59
+ let {
60
+ datum,
61
+ markerStart,
62
+ markerMid,
63
+ markerEnd,
64
+ marker,
65
+ d,
66
+ style,
67
+ class: className = null,
68
+ textStyleClass = null,
69
+ startOffset,
70
+ textStyle,
71
+ text,
72
+ transform,
73
+ color,
74
+ strokeWidth,
75
+ mark
76
+ }: MarkerPathProps = $props();
77
+
78
+ const id = randomId();
79
+
80
+ const { getPlotState } = getContext<PlotContext>('svelteplot');
81
+
82
+ const points = $derived(text && d != null ? d.split(/[LMC]/).slice(1) : []);
83
+ const hasPath = $derived(points.length > 0);
84
+ const firstPt = $derived(text && hasPath ? points.at(0).split(',').map(Number) : []);
85
+ const lastPt = $derived(text && hasPath ? points.at(-1).split(',').map(Number) : []);
86
+ const leftToRight = $derived(text && hasPath ? firstPt[0] < lastPt.at(-2) : true);
87
+ const pathIsCurve = $derived(text && hasPath ? d.includes('C') : false);
88
+ // this rather complicated code "reverses" the path to ensure that the text
89
+ // is not turned upside down
90
+ const textPath = $derived(
91
+ !text || leftToRight
92
+ ? hasPath
93
+ : pathIsCurve
94
+ ? [
95
+ 'M',
96
+ points.at(-1).split(',').slice(-2).join(','),
97
+ 'C',
98
+ points.at(-1).split(',').slice(2, 4).join(','),
99
+ ',',
100
+ points.at(-1).split(',').slice(0, 2).join(','),
101
+ ',',
102
+ points[0]
103
+ ].join('')
104
+ : [
105
+ 'M',
106
+ points.at(-1),
107
+ ...points
108
+ .toReversed()
109
+ .slice(1)
110
+ .map((pt) => `L${pt}`)
111
+ ].join('')
112
+ );
113
+
114
+ const strokeWidth_ = $derived(resolveProp(strokeWidth, datum, 1.4));
115
+ </script>
116
+
117
+ <g
118
+ {transform}
119
+ class={className}
120
+ stroke-width={strokeWidth_}
121
+ use:addEventHandlers={{ getPlotState, options: mark.options, datum }}>
122
+ {#each Object.entries( { start: markerStart, mid: markerMid, end: markerEnd, all: marker } ) as [key, marker]}
123
+ {@const markerId = `marker-${key === 'all' ? '' : `${key}-`}${id}`}
124
+ {#if isSnippet(marker)}
125
+ {@render marker(markerId, color)}
126
+ {:else if marker}
127
+ <Marker
128
+ id={markerId}
129
+ shape={marker === true ? 'circle' : resolveProp(marker, datum)}
130
+ {color} />
131
+ {/if}
132
+ {/each}
133
+ {#if mark.options.onmouseenter || mark.options.onclick}
134
+ <!-- add invisible path in bg for easier mouse access -->
135
+ <path
136
+ {d}
137
+ style="fill:none;stroke-width: {(strokeWidth || 1) +
138
+ 10}; stroke: transparent; stroke-opacity:0" />
139
+ {/if}
140
+ <path
141
+ marker-start={markerStart || marker
142
+ ? `url(#marker-${markerStart ? 'start-' : ''}${id})`
143
+ : null}
144
+ marker-mid={markerMid || marker ? `url(#marker-${markerMid ? 'mid-' : ''}${id})` : null}
145
+ marker-end={markerEnd || marker ? `url(#marker-${markerEnd ? 'end-' : ''}${id})` : null}
146
+ {d}
147
+ {style}
148
+ use:addEventHandlers={{ getPlotState, options: mark.options, datum }} />
149
+ {#if text}
150
+ <!-- since textPath.side is not yet supported, we have to use an invisible
151
+ path in order to keep the text from turning upside down -->
152
+ <path d={textPath} {id} stroke="none" fill="none" />
153
+ <text dy="-3" style={textStyle} class={textStyleClass}>
154
+ <textPath {startOffset} href="#{id}">{text}</textPath>
155
+ </text>
156
+ {/if}
157
+ </g>
158
+
159
+ <style>
160
+ text {
161
+ font-size: 12px;
162
+ paint-order: stroke fill;
163
+ }
164
+ </style>
@@ -0,0 +1,44 @@
1
+ import { type MarkerShape } from './Marker.svelte';
2
+ import type { BaseMarkProps, ConstantAccessor, DataRecord, Mark, PlotScales } from '../../types.js';
3
+ type MarkerPathProps = BaseMarkProps & {
4
+ /**
5
+ * the datum associated with this path, usually the first
6
+ * element of the data array group
7
+ */
8
+ datum: DataRecord;
9
+ /**
10
+ * the marker shape to use at the start of the path, defaults to
11
+ * cirlce
12
+ */
13
+ markerStart?: boolean | MarkerShape;
14
+ /**
15
+ * the marker shape to use at the middle of the path, defaults to circle
16
+ */
17
+ markerMid?: boolean | MarkerShape;
18
+ /**
19
+ * the marker shape to use at the end of the path, defaults to circle
20
+ */
21
+ markerEnd?: boolean | MarkerShape;
22
+ /**
23
+ * shorthand for setting all markers
24
+ */
25
+ marker?: boolean | MarkerShape;
26
+ /**
27
+ * path string
28
+ */
29
+ d: string;
30
+ style: string;
31
+ startOffset: string;
32
+ textStyle: string;
33
+ textStyleClass?: string | null;
34
+ text: string;
35
+ transform: string;
36
+ color: string;
37
+ strokeWidth: ConstantAccessor<number>;
38
+ mark: Mark<BaseMarkProps>;
39
+ scales: PlotScales;
40
+ };
41
+ /** Helper component for paths with markers and optional text along the path. */
42
+ declare const MarkerPath: import("svelte").Component<MarkerPathProps, {}, "">;
43
+ type MarkerPath = ReturnType<typeof MarkerPath>;
44
+ export default MarkerPath;
@@ -0,0 +1,174 @@
1
+ <script module lang="ts">
2
+ import type { BaseMarkProps, ChannelAccessor, FacetContext, PlotContext } from '../../types.js';
3
+
4
+ type RegressionType = 'linear' | 'quad' | 'poly' | 'exp' | 'log' | 'pow' | 'loess';
5
+
6
+ export type RegressionMarkProps = BaseMarkProps & {
7
+ x: ChannelAccessor;
8
+ y: ChannelAccessor;
9
+ type: RegressionType;
10
+ /**
11
+ * If order is specified, sets the regression's order to the specified number.
12
+ * For example, if order is set to 4, the regression generator will perform a
13
+ * fourth-degree polynomial regression. Likewise, if order is set to 2, the
14
+ * regression generator will perform a quadratic regression. Be careful about
15
+ * attempting to fit your data with higher order polynomials; though the
16
+ * regression line will fit your data with a high determination coefficient,
17
+ * it may have little predictive power for data outside of your domain.
18
+ */
19
+ order: number;
20
+ // for log
21
+ base: number;
22
+ // for loess
23
+ span: number;
24
+ // for confidence bands
25
+ confidence: number;
26
+ };
27
+ </script>
28
+
29
+ <script lang="ts">
30
+ import { getContext } from 'svelte';
31
+ import { Line, Area } from '../../index.js';
32
+
33
+ import {
34
+ regressionLinear,
35
+ regressionQuad,
36
+ regressionPoly,
37
+ regressionExp,
38
+ regressionLog,
39
+ regressionPow,
40
+ regressionLoess
41
+ } from 'd3-regression/dist/d3-regression.esm.js';
42
+ import { resolveChannel } from '../../helpers/resolve.js';
43
+ import { confidenceInterval } from '../../helpers/math.js';
44
+ import callWithProps from '../../helpers/callWithProps.js';
45
+ import { isDate } from '../../helpers/typeChecks';
46
+
47
+ const regressions = new Map<RegressionType, typeof regressionLinear>([
48
+ ['linear', regressionLinear],
49
+ ['quad', regressionQuad],
50
+ ['poly', regressionPoly],
51
+ ['exp', regressionExp],
52
+ ['log', regressionLog],
53
+ ['pow', regressionPow],
54
+ ['loess', regressionLoess]
55
+ ]);
56
+
57
+ function maybeRegression(name: string) {
58
+ name = `${name}`.toLowerCase();
59
+ if (regressions.has(name)) return regressions.get(name);
60
+ throw new Error('unknown regression ' + name);
61
+ }
62
+
63
+ const { getPlotState } = getContext<PlotContext>('svelteplot');
64
+ let plot = $derived(getPlotState());
65
+
66
+ let {
67
+ data,
68
+ dependent,
69
+ type = 'linear',
70
+ order = 3,
71
+ base = 2.71828,
72
+ span = 0.3,
73
+ confidence = 0.99,
74
+ class: className = null,
75
+ ...options
76
+ }: RegressionMarkProps & { dependent: 'x' | 'y' } = $props();
77
+
78
+ const { getTestFacet } = getContext<FacetContext>('svelteplot/facet');
79
+ let testFacet = $derived(getTestFacet());
80
+
81
+ let filteredData = $derived(data.filter((d) => testFacet(d, options)));
82
+
83
+ let independent: 'x' | 'y' = $derived(dependent === 'x' ? 'y' : 'x');
84
+
85
+ let regressionFn = $derived(maybeRegression(type));
86
+
87
+ let regression = $derived(
88
+ callWithProps(regressionFn, [], {
89
+ x: (d) => resolveChannel(independent, d, options),
90
+ y: (d) => resolveChannel(dependent, d, options),
91
+ ...(type === 'poly' ? { order } : {}),
92
+ ...(type === 'log' ? { base } : {}),
93
+ ...(!type.startsWith('loess') ? { domain: plot.scales[independent].domain } : {}),
94
+ ...(type === 'loess' ? { bandwidth: span } : {})
95
+ })(filteredData)
96
+ );
97
+
98
+ let regrPoints = $derived([
99
+ ...new Set([
100
+ plot.scales[independent].domain[0],
101
+ ...plot.scales[independent].fn.ticks(40),
102
+ plot.scales[independent].domain[1]
103
+ ])
104
+ ]);
105
+
106
+ let regrData = $derived(
107
+ regression.predictMany
108
+ ? regression.predictMany(regrPoints).map((__y, i) => ({ __x: regrPoints[i], __y }))
109
+ : regression.predict
110
+ ? regrPoints.map((__x) => {
111
+ // const __x = x;
112
+ const __y = regression.predict(__x);
113
+ return { __x, __y };
114
+ })
115
+ : regression.map(([__x, __y]) => ({
116
+ __x: plot.scales[independent].type === 'time' ? new Date(__x) : __x,
117
+ __y
118
+ }))
119
+ );
120
+
121
+ let stroke = $derived(
122
+ options.stroke != null ? resolveChannel('stroke', filteredData[0], options) : null
123
+ );
124
+
125
+ let confBandGen = $derived(
126
+ confidence !== false
127
+ ? confidenceInterval(
128
+ data
129
+ .map((d) => ({
130
+ x: resolveChannel(independent, d, options),
131
+ y: resolveChannel(dependent, d, options)
132
+ }))
133
+ .filter(
134
+ ({ x, y }) => (Number.isFinite(x) || isDate(x)) && Number.isFinite(y)
135
+ ),
136
+ regression.predict,
137
+ 1 - confidence
138
+ )
139
+ : null
140
+ );
141
+
142
+ let confBandData = $derived(
143
+ confidence !== false && regression.predict
144
+ ? regrPoints.map((x) => {
145
+ const { x: __x, left, right } = confBandGen(x);
146
+ return { __x, __y1: left, __y2: right };
147
+ })
148
+ : []
149
+ );
150
+ </script>
151
+
152
+ {#if filteredData.length}
153
+ <g class="regression-{independent} {className || ''}">
154
+ <Line
155
+ data={regrData}
156
+ {...{
157
+ ...options,
158
+ fx: null,
159
+ fy: null,
160
+ stroke,
161
+ x: dependent === 'y' ? '__x' : '__y',
162
+ y: dependent === 'y' ? '__y' : '__x'
163
+ }} />
164
+ {#if confBandData.length}
165
+ <Area
166
+ data={confBandData}
167
+ {...dependent === 'y'
168
+ ? { x1: '__x', y1: '__y1', y2: '__y2' }
169
+ : { y1: '__x', x1: '__y1', x2: '__y2' }}
170
+ fill={stroke || 'currentColor'}
171
+ opacity={0.15} />
172
+ {/if}
173
+ </g>
174
+ {/if}