svelteplot 0.0.1-alpha.8 → 0.1.3-next.3

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 +292 -0
  4. package/dist/Mark.svelte.d.ts +22 -0
  5. package/dist/Plot.svelte +148 -153
  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
package/LICENSE.md ADDED
@@ -0,0 +1,5 @@
1
+ Copyright 2024, Gregor Aisch
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
+
5
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,38 +1,5 @@
1
- # create-svelte
1
+ # SveltePlot
2
2
 
3
- Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
3
+ SveltePlot is a new reactive visualization framework based on the [layered grammar of graphics](https://vita.had.co.nz/papers/layered-grammar.html) ideas. It's API is heavily inspired by [Observable Plot](https://github.com/observablehq/plot). Created by Gregor Aisch.
4
4
 
5
- ## Creating a project
6
-
7
- If you're seeing this, you've probably already done this step. Congrats!
8
-
9
- ```bash
10
- # create a new project in the current directory
11
- npm create svelte@latest
12
-
13
- # create a new project in my-app
14
- npm create svelte@latest my-app
15
- ```
16
-
17
- ## Developing
18
-
19
- Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
20
-
21
- ```bash
22
- npm run dev
23
-
24
- # or start the server and open the app in a new browser tab
25
- npm run dev -- --open
26
- ```
27
-
28
- ## Building
29
-
30
- To create a production version of your app:
31
-
32
- ```bash
33
- npm run build
34
- ```
35
-
36
- You can preview the production build with `npm run preview`.
37
-
38
- > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
5
+ <img src="static/logo.png" alt="logo" width="400" />
@@ -0,0 +1,292 @@
1
+ <script lang="ts">
2
+ import { getContext, untrack, type Snippet } from 'svelte';
3
+
4
+ import { CHANNEL_SCALE } from './constants.js';
5
+ import type {
6
+ ScaledChannelName,
7
+ MarkType,
8
+ DataRecord,
9
+ PlotContext,
10
+ ChannelName,
11
+ GenericMarkOptions,
12
+ ChannelAccessor,
13
+ BaseMarkProps,
14
+ FacetContext,
15
+ ScaleName,
16
+ RawValue,
17
+ ResolvedDataRecord,
18
+ ScaledDataRecord,
19
+ ScaleType
20
+ } from './types.js';
21
+ import { getUsedScales, projectXY, projectX, projectY } from './helpers/scales.js';
22
+ import { testFilter, isValid } from './helpers/index.js';
23
+ import { resolveChannel, resolveProp } from './helpers/resolve.js';
24
+
25
+ type MarkProps = {
26
+ data?: DataRecord[];
27
+ automatic?: boolean;
28
+ type: MarkType;
29
+ channels?: ScaledChannelName[];
30
+ required?: ScaledChannelName[];
31
+ requiredScales?: Partial<Record<ScaleName, ScaleType[]>>;
32
+ children?: Snippet<
33
+ [
34
+ {
35
+ mark: Mark<GenericMarkOptions>;
36
+ usedScales: ReturnType<typeof getUsedScales>;
37
+ scaledData: ScaledDataRecord[];
38
+ }
39
+ ]
40
+ >;
41
+ defaults?: Partial<Record<ScaledChannelName, RawValue>>;
42
+ } & Partial<Record<ChannelName, ChannelAccessor>> &
43
+ Partial<BaseMarkProps>;
44
+
45
+ let {
46
+ data = [],
47
+ children,
48
+ type,
49
+ channels = [],
50
+ required = [],
51
+ requiredScales = {},
52
+ defaults = {},
53
+ ...options
54
+ }: MarkProps = $props();
55
+
56
+ const channelsWithFacets: ScaledChannelName[] = $derived([...channels, 'fx', 'fy']);
57
+
58
+ const { addMark, updateMark, updatePlotState, removeMark, getTopLevelFacet, getPlotState } =
59
+ getContext<PlotContext>('svelteplot');
60
+
61
+ const plot = $derived(getPlotState());
62
+ const facet = $derived(getTopLevelFacet());
63
+
64
+ const { getFacetState } = getContext<FacetContext>('svelteplot/facet');
65
+ const { left, top } = $derived(getFacetState());
66
+
67
+ class Mark {
68
+ id;
69
+ type;
70
+ channels: ScaledChannelName[] = $state.raw([]);
71
+ scales: Set<ScaleName> = $state.raw(new Set());
72
+ data: DataRecord[] = $state.raw([]);
73
+ options: GenericMarkOptions = $state.raw({});
74
+
75
+ constructor(type: MarkType) {
76
+ this.id = Symbol();
77
+ this.type = type;
78
+ }
79
+ }
80
+
81
+ const mark = new Mark(type);
82
+
83
+ $effect(() => {
84
+ return () => {
85
+ removeMark(mark);
86
+ added = false;
87
+ };
88
+ });
89
+
90
+ // let mark2 = $state(mark);
91
+ const facetMode = $derived(options.facet || 'auto');
92
+
93
+ const optionsWithAutoFacet = $derived({
94
+ ...options,
95
+ __firstFacet: left && top,
96
+ ...(facet &&
97
+ facet.data &&
98
+ ((facetMode === 'auto' && facet.data === data) || facetMode === 'include')
99
+ ? {
100
+ fx: facet.x,
101
+ fy: facet.y
102
+ }
103
+ : {})
104
+ });
105
+
106
+ let added = false;
107
+
108
+ $effect(() => {
109
+ if (added) return;
110
+ // without using untrack() here we end up with inexplicable
111
+ // circular dependency updates resulting in a stack overflow
112
+ const channels = untrack(() => channelsWithFacets);
113
+ mark.channels = channels;
114
+ mark.scales = new Set(
115
+ channels
116
+ .filter((channel) => options[channel] !== 0)
117
+ .map((channel) => CHANNEL_SCALE[channel])
118
+ );
119
+ mark.data = untrack(() => data);
120
+ mark.options = untrack(() => optionsWithAutoFacet);
121
+ addMark(mark);
122
+ added = true;
123
+ });
124
+
125
+ const { getTestFacet } = getContext<FacetContext>('svelteplot/facet');
126
+ const testFacet = $derived(getTestFacet());
127
+
128
+ const resolvedData: ResolvedDataRecord[] = $derived(
129
+ data.flatMap((row) => {
130
+ const channels = options as Record<ChannelName, ChannelAccessor>;
131
+ if (!testFacet(row, channels) || !testFilter(row, channels)) return [];
132
+ const out: ResolvedDataRecord = {
133
+ datum: row
134
+ };
135
+ for (const [channel] of Object.entries(CHANNEL_SCALE) as [
136
+ ScaledChannelName,
137
+ ScaleName
138
+ ][]) {
139
+ // check if the mark has defined an accessor for this channel
140
+ if (options?.[channel] !== undefined && out[channel] === undefined) {
141
+ // resolve value
142
+ out[channel] = resolveChannel(channel, row, options);
143
+ }
144
+ }
145
+ return [out];
146
+ })
147
+ );
148
+
149
+ let prevResolvedData: ResolvedDataRecord[] = [];
150
+
151
+ $effect(() => {
152
+ if (isDifferent(resolvedData, prevResolvedData)) {
153
+ prevResolvedData = resolvedData;
154
+ // data has changed
155
+ mark.data = data;
156
+ }
157
+ });
158
+
159
+ function isDifferent(array1: ResolvedDataRecord[], array2: ResolvedDataRecord[]) {
160
+ if (array1.length !== array2.length) return true;
161
+ for (let i = 0; i < array1.length; i++) {
162
+ for (const [channel] of Object.entries(CHANNEL_SCALE) as [
163
+ ScaledChannelName,
164
+ ScaleName
165
+ ][]) {
166
+ if (array1[i][channel] !== array2[i][channel]) return true;
167
+ }
168
+ }
169
+ return false;
170
+ }
171
+
172
+ const errors = $derived([
173
+ ...required
174
+ .filter((name) => options[name] == null)
175
+ .map((name) => `missing channel value for ${mark.type} mark: ${name}`),
176
+ ...Object.entries(requiredScales)
177
+ .filter(([scale, types]) => {
178
+ return !types.includes(plot.scales[scale].type);
179
+ })
180
+ .map(
181
+ ([scale, types]) => `scale type mismatch for ${scale} (needs ${types.join(' or ')})`
182
+ )
183
+ ]);
184
+
185
+ $effect(() => {
186
+ for (const name of required) {
187
+ if (options[name] == null) throw new Error(`missing channel value: ${name}`);
188
+ }
189
+ });
190
+
191
+ const usedScales = $derived(getUsedScales(plot, optionsWithAutoFacet, mark));
192
+ /**
193
+ * based on the data and the global scales we can now map the data
194
+ * elements to the scales
195
+ */
196
+ const scaledData = $derived(
197
+ resolvedData.flatMap((row) => {
198
+ const out: ScaledDataRecord = {
199
+ datum: row.datum,
200
+ valid: true
201
+ };
202
+ // compute dx/dy
203
+ const dx = Number(resolveProp<number>(options.dx, out.datum, 0));
204
+ const dy = Number(resolveProp<number>(options.dy, out.datum, 0));
205
+
206
+ // special handling if there's a projection
207
+ if (plot.scales.projection && mark.type !== 'geo') {
208
+ for (const suffix of ['', '1', '2']) {
209
+ if (
210
+ options?.[`x${suffix}`] !== undefined &&
211
+ options?.[`y${suffix}`] !== undefined
212
+ ) {
213
+ // we have two-dimensional accessors
214
+ // for the x and y channels
215
+ const rx = resolveChannel(`x${suffix}`, row, options);
216
+ const ry = resolveChannel(`y${suffix}`, row, options);
217
+ const [x, y] =
218
+ mark.type === 'line'
219
+ ? [rx, ry] // line paths are projected later
220
+ : projectXY(
221
+ plot.scales,
222
+ rx,
223
+ ry,
224
+ usedScales.x,
225
+ usedScales.y,
226
+ suffix
227
+ );
228
+ out[`x${suffix}`] = x;
229
+ out[`y${suffix}`] = y;
230
+ out.valid = out.valid && isValid(rx) && isValid(ry);
231
+ }
232
+ }
233
+ }
234
+
235
+ // iterate over all scaled channels
236
+ for (const [channel, scale] of Object.entries(CHANNEL_SCALE) as [
237
+ ScaledChannelName,
238
+ ScaleName
239
+ ][]) {
240
+ // check if the mark has defined an accessor for this channel
241
+ if (options?.[channel] !== undefined && out[channel] === undefined) {
242
+ // resolve value
243
+ const value = row[channel];
244
+
245
+ const scaled = usedScales[channel]
246
+ ? scale === 'x'
247
+ ? projectX(channel as 'x' | 'x1' | 'x2', plot.scales, value)
248
+ : scale === 'y'
249
+ ? projectY(channel as 'y' | 'y1' | 'y1', plot.scales, value)
250
+ : plot.scales[scale].fn(value)
251
+ : value;
252
+
253
+ out.valid = out.valid && isValid(value);
254
+ // apply dx/dy transform
255
+ out[channel] =
256
+ scale === 'x' && Number.isFinite(scaled) ? (scaled as number) + dx : scaled;
257
+ out[channel] =
258
+ scale === 'y' && Number.isFinite(scaled) ? (scaled as number) + dy : scaled;
259
+ } else if (defaults[channel]) {
260
+ out[channel] = defaults[channel];
261
+ }
262
+ }
263
+
264
+ return [out];
265
+ })
266
+ );
267
+ </script>
268
+
269
+ {#if errors.length}
270
+ <text transform="translate(10,10)">
271
+ {#each errors as error, i}
272
+ <tspan x="0" dy={i ? 14 : 0}>{error}</tspan>
273
+ {/each}
274
+ </text>
275
+ {:else if children}
276
+ {@render children({
277
+ mark,
278
+ usedScales,
279
+ scaledData
280
+ })}
281
+ {/if}
282
+
283
+ <style>
284
+ text {
285
+ stroke: var(--plot-bg);
286
+ fill: crimson;
287
+ font-size: 11px;
288
+ stroke-width: 3px;
289
+ font-weight: bold;
290
+ paint-order: stroke fill;
291
+ }
292
+ </style>
@@ -0,0 +1,22 @@
1
+ import { type Snippet } from 'svelte';
2
+ import type { ScaledChannelName, MarkType, DataRecord, ChannelName, GenericMarkOptions, ChannelAccessor, BaseMarkProps, ScaleName, RawValue, ScaledDataRecord, ScaleType } from './types.js';
3
+ import { getUsedScales } from './helpers/scales.js';
4
+ type MarkProps = {
5
+ data?: DataRecord[];
6
+ automatic?: boolean;
7
+ type: MarkType;
8
+ channels?: ScaledChannelName[];
9
+ required?: ScaledChannelName[];
10
+ requiredScales?: Partial<Record<ScaleName, ScaleType[]>>;
11
+ children?: Snippet<[
12
+ {
13
+ mark: Mark<GenericMarkOptions>;
14
+ usedScales: ReturnType<typeof getUsedScales>;
15
+ scaledData: ScaledDataRecord[];
16
+ }
17
+ ]>;
18
+ defaults?: Partial<Record<ScaledChannelName, RawValue>>;
19
+ } & Partial<Record<ChannelName, ChannelAccessor>> & Partial<BaseMarkProps>;
20
+ declare const Mark: import("svelte").Component<MarkProps, {}, "">;
21
+ type Mark = ReturnType<typeof Mark>;
22
+ export default Mark;
package/dist/Plot.svelte CHANGED
@@ -1,168 +1,163 @@
1
- <script>import { setContext } from "svelte";
2
- import { Frame, GridX, GridY, AxisX, AxisY, ColorLegend, SymbolLegend } from "./index.js";
3
- import { DEFAULT_PLOT_OPTIONS, Plot } from "./classes/Plot.svelte";
4
- import mergeDeep from "./helpers/mergeDeep.js";
5
- let {
6
- // snippets
7
- header,
8
- footer,
9
- overlay,
10
- children,
11
- // props
12
- height = "auto",
13
- marginLeft = 30,
14
- marginRight = 10,
15
- marginTop = 20,
16
- marginBottom = 40,
17
- inset = null,
18
- grid = false,
19
- frame = false,
20
- maxWidth = null,
21
- title = "",
22
- subtitle = "",
23
- caption = "",
24
- // scales
25
- radius = null,
26
- color = null,
27
- symbol = null,
28
- x = null,
29
- y = null,
30
- onmousemove = null
31
- } = $props();
32
- let width = $state(400);
33
- const plot = new Plot(600, height || defaultPlotHeight, {
34
- marginTop,
35
- marginLeft,
36
- marginRight,
37
- marginBottom,
38
- // scales
39
- symbol,
40
- radius,
41
- x,
42
- y,
43
- color,
44
- // other
45
- title,
46
- subtitle,
47
- caption
48
- });
49
- setContext("svelteplot", plot);
50
- $effect(() => {
51
- plot.width = width;
52
- plot._height = height;
53
- plot.options = mergeDeep({}, DEFAULT_PLOT_OPTIONS, {
54
- marginBottom,
55
- marginLeft,
56
- marginRight,
57
- marginTop,
58
- inset,
59
- symbol,
60
- radius,
61
- color,
62
- x,
63
- y,
64
- title,
65
- subtitle,
66
- caption
67
- });
68
- });
69
- function onMouseMove(evt) {
70
- if (onmousemove)
71
- onmousemove({ ...evt, plot });
72
- }
73
- $inspect(plot.x.domain);
74
- let hasLegend = $derived(color?.legend || symbol?.legend);
1
+ <!--
2
+ @component
3
+ The Plot component is the container for plots. It collects the marks with
4
+ their data and channels and computes the shared scales.
5
+
6
+ The Plot component is split into two parts. This is the outer Plot which
7
+ provides convenient defaults and automatically adds axes etc to the grapihcs.
8
+ The downside is that it adds a bunch of imports that you may not be using.
9
+ To help with this you can use the core/Plot component directly for a more
10
+ low-level Plot wrapper.
11
+ -->
12
+ <script lang="ts">
13
+ import Plot from './core/Plot.svelte';
14
+
15
+ import type { PlotOptions } from './types.js';
16
+
17
+ // implicit marks
18
+ import AxisX from './marks/AxisX.svelte';
19
+ import AxisY from './marks/AxisY.svelte';
20
+ import ColorLegend from './marks/ColorLegend.svelte';
21
+ import Frame from './marks/Frame.svelte';
22
+ import GridX from './marks/GridX.svelte';
23
+ import GridY from './marks/GridY.svelte';
24
+ import SymbolLegend from './marks/SymbolLegend.svelte';
25
+ import FacetAxes from './core/FacetAxes.svelte';
26
+
27
+ // automatic scales
28
+ import { autoScale, autoScaleColor } from './helpers/autoScales.js';
29
+ import { namedProjection } from './helpers/autoProjection.js';
30
+ import { isObject } from './helpers/index.js';
31
+
32
+ let {
33
+ header: userHeader,
34
+ footer: userFooter,
35
+ overlay,
36
+ underlay,
37
+ children: parentChildren,
38
+ testid,
39
+ facet,
40
+ projection,
41
+ ...restOptions
42
+ }: Partial<PlotOptions> = $props();
43
+
44
+ const projectionOpts = $derived.by(() => {
45
+ if (
46
+ projection &&
47
+ typeof projection !== 'function' &&
48
+ typeof projection?.type !== 'function'
49
+ ) {
50
+ const { type: projFactory, aspectRatio } = namedProjection(
51
+ isObject(projection) ? projection.type : projection
52
+ );
53
+ return {
54
+ ...(isObject(projection) ? projection : {}),
55
+ type: projFactory,
56
+ aspectRatio
57
+ };
58
+ }
59
+ return projection;
60
+ });
61
+
62
+ const scales = $derived(
63
+ Object.fromEntries(
64
+ ['x', 'y', 'r', 'color', 'opacity', 'symbol', 'length', 'fx', 'fy'].map((scale) => {
65
+ const scaleOpts = restOptions[scale] || {};
66
+ const scaleFn = scaleOpts.scale || (scale === 'color' ? autoScaleColor : autoScale);
67
+ return [scale, { ...scaleOpts, scale: scaleFn }];
68
+ })
69
+ )
70
+ );
75
71
  </script>
76
72
 
77
- <figure class="svelteplot" bind:clientWidth={width} style:max-width={maxWidth}>
78
- <svg
79
- role="document"
80
- {width}
81
- height={plot.height}
82
- onmousemove={onmousemove ? onMouseMove : null}
83
- >
84
- <!-- automatic grids -->
85
- {#if grid || x?.grid}<GridX automatic />{/if}
86
- {#if grid || y?.grid}<GridY automatic />{/if}
73
+ {#snippet header()}
74
+ {#if restOptions.title}<h2>{restOptions.title}</h2>{/if}
75
+ {#if restOptions.subtitle}<h3>{restOptions.subtitle}</h3>{/if}
76
+ <!-- also pass on user header -->
77
+ {#if userHeader}{@render userHeader()}{/if}
78
+ {#if restOptions.color?.legend}
79
+ <ColorLegend />
80
+ {/if}
81
+ {#if restOptions.symbol?.legend}
82
+ <SymbolLegend />
83
+ {/if}
84
+ {/snippet}
85
+
86
+ {#snippet footer()}
87
+ {#if restOptions.caption}<div>{restOptions.caption}</div>{/if}
88
+ {#if userFooter}{@render userFooter()}{/if}
89
+ {/snippet}
87
90
 
88
- {#if !plot.hasAxisXMark && plot.hasChannelX}
89
- <!-- automatic x axis -->
90
- {#if plot.options.x.axis === 'bottom' || plot.options.x.axis === 'both'}
91
- <AxisX anchor="bottom" automatic />
92
- {/if}
93
- {#if plot.options.x.axis === 'top' || plot.options.x.axis === 'both'}
91
+ <!-- There's a bug triggering RangeError: Maximum call stack size exceeded
92
+ when using SveltePlot in ssr, so for now, we're disabling it -->
93
+
94
+ <Plot
95
+ {overlay}
96
+ {underlay}
97
+ {...restOptions}
98
+ header={userHeader ||
99
+ restOptions.title ||
100
+ restOptions.subtitle ||
101
+ restOptions.color?.legend ||
102
+ restOptions.symbol?.legend
103
+ ? header
104
+ : null}
105
+ footer={userFooter || restOptions?.caption ? footer : null}
106
+ projection={projectionOpts}
107
+ implicitScales
108
+ {...scales}>
109
+ {#snippet children({
110
+ hasProjection,
111
+ hasExplicitAxisX,
112
+ hasExplicitAxisY,
113
+ hasExplicitGridX,
114
+ hasExplicitGridY,
115
+ options,
116
+ scales,
117
+ ...restProps
118
+ })}
119
+ <!-- implicit axes -->
120
+ {#if !hasProjection && !hasExplicitAxisX}
121
+ {#if options.axes && (options.x.axis === 'top' || options.x.axis === 'both')}
94
122
  <AxisX anchor="top" automatic />
95
123
  {/if}
124
+ {#if options.axes && (options.x.axis === 'bottom' || options.x.axis === 'both')}
125
+ <AxisX anchor="bottom" automatic />
126
+ {/if}
96
127
  {/if}
97
- {#if !plot.hasAxisYMark && plot.hasChannelY}
98
- <!-- automatic y axis -->
99
- {#if plot.options.y.axis === 'left' || plot.options.y.axis === 'both'}
128
+ {#if !hasProjection && !hasExplicitAxisY}
129
+ {#if options.axes && (options.y.axis === 'left' || options.y.axis === 'both')}
100
130
  <AxisY anchor="left" automatic />
101
131
  {/if}
102
- {#if plot.options.y.axis === 'right' || plot.options.y.axis === 'both'}
132
+ {#if options.axes && (options.y.axis === 'right' || options.y.axis === 'both')}
103
133
  <AxisY anchor="right" automatic />
104
134
  {/if}
105
135
  {/if}
106
- <!-- automatic frame -->
107
- {#if frame}<Frame />{/if}
108
- {#if children}{@render children(plot)}{/if}
109
- </svg>
110
-
111
- {#if plot.options.title || plot.options.subtitle || header || hasLegend}
112
- <div class="plot-header">
113
- {#if plot.options.title}
114
- <h2>{@html plot.options.title}</h2>
115
- {/if}
116
- {#if plot.options.subtitle}
117
- <h3>{@html plot.options.subtitle}</h3>
118
- {/if}
119
- {#if color?.legend}
120
- <ColorLegend />
121
- {/if}
122
- {#if symbol?.legend}
123
- <SymbolLegend />
124
- {/if}
125
- {#if header}{@render header(plot)}{/if}
126
- </div>
127
- {/if}
128
-
129
- {#if footer || plot.options.caption}
130
- <div class="plot-footer">
131
- {#if plot.options.caption}
132
- <figcaption>{@html plot.options.caption}</figcaption>
133
- {/if}
134
- {#if footer}{@render footer(plot)}{/if}
135
- </div>
136
- {/if}
137
-
138
- <div class="overlay">
139
- {#if overlay}{@render overlay(plot)}{/if}
140
- </div>
141
- </figure>
136
+ <!-- implicit grids -->
137
+ {#if !hasExplicitGridX && (options.grid || options.x.grid)}
138
+ <GridX automatic />
139
+ {/if}
140
+ {#if !hasExplicitGridY && (options.grid || options.y.grid)}
141
+ <GridY automatic />
142
+ {/if}
143
+ <!-- implicit frame -->
144
+ {#if options.frame}
145
+ <Frame automatic />
146
+ {/if}
147
+ {@render parentChildren?.({
148
+ options,
149
+ scales,
150
+ ...restProps
151
+ })}
152
+ {/snippet}
153
+ {#snippet facetAxes()}
154
+ <FacetAxes />
155
+ {/snippet}
156
+ </Plot>
142
157
 
143
158
  <style>
144
- figure {
145
- margin: 1em 0;
146
- position: relative;
147
- display: flex;
148
- flex-direction: column;
149
- }
150
- figure .plot-header {
151
- order: 1;
152
- }
153
- svg {
154
- order: 2;
155
- overflow: visible;
156
- }
157
- figure .plot-footer {
158
- order: 3;
159
- }
160
- .overlay {
161
- position: absolute;
162
- pointer-events: none;
163
- top: 0;
164
- left: 0;
165
- bottom: 0;
166
- right: 0;
159
+ :root {
160
+ --plot-bg: white;
161
+ --plot-fg: currentColor;
167
162
  }
168
163
  </style>