svelteplot 0.0.1-alpha.9 → 0.1.3-next.12

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 -35
  3. package/dist/Mark.svelte +292 -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 +15 -0
  8. package/dist/constants.js +110 -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 +568 -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 +55 -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 +21 -0
  63. package/dist/helpers/resolve.js +156 -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 +311 -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 +93 -0
  87. package/dist/marks/BarX.svelte.d.ts +4 -0
  88. package/dist/marks/BarY.svelte +103 -0
  89. package/dist/marks/BarY.svelte.d.ts +25 -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 +39 -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 +180 -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 +189 -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 +219 -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 +141 -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 +74 -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 +1 -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 +15 -0
  209. package/dist/transforms/recordize.js +78 -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 +66 -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 +24 -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 +81 -42
  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,219 @@
1
+ <script lang="ts" module>
2
+ import type {
3
+ PlotContext,
4
+ DataRecord,
5
+ BaseMarkProps,
6
+ ConstantAccessor,
7
+ ChannelAccessor,
8
+ FacetContext
9
+ } from '../types.js';
10
+
11
+ type D3Path = ReturnType<typeof import('d3-path').path>;
12
+
13
+ export type ShapeRenderer = {
14
+ draw(context: D3Path, l: number, r: number): void;
15
+ };
16
+
17
+ export type VectorMarkProps = BaseMarkProps & {
18
+ data: DataRecord[];
19
+ x: ChannelAccessor;
20
+ y: ChannelAccessor;
21
+ r?: number;
22
+ length?: ChannelAccessor;
23
+ rotate?: ChannelAccessor;
24
+ fill?: ChannelAccessor;
25
+ stroke?: ChannelAccessor;
26
+ /**
27
+ * Controls where the vector is anchored in relation to the x, y position.
28
+ * If set to 'start', the arrow will start at the x, y position. If set to
29
+ * 'middle', the arrow will be centered at the x, y position. If set to
30
+ * 'end', the arrow will end at the x, y position.
31
+ */
32
+ anchor: 'start' | 'middle' | 'end';
33
+ shape?: 'arrow' | 'spike' | 'arrow-filled' | ShapeRenderer;
34
+ children?: Snippet;
35
+ dx?: ConstantAccessor<number>;
36
+ dy?: ConstantAccessor<number>;
37
+ canvas?: boolean;
38
+ };
39
+ </script>
40
+
41
+ <script lang="ts">
42
+ import { getContext, type Snippet } from 'svelte';
43
+ import { pathRound as path } from 'd3-path';
44
+
45
+ import { resolveChannel, resolveProp, resolveStyles } from '../helpers/resolve.js';
46
+ import { projectXY } from '../helpers/scales.js';
47
+ import { sort } from '../index.js';
48
+ import Mark from '../Mark.svelte';
49
+ //import DotCanvas from './helpers/DotCanvas.svelte';
50
+ import { maybeData, testFilter, isValid } from '../helpers/index.js';
51
+ import { addEventHandlers } from './helpers/events.js';
52
+
53
+ const defaultRadius = 3.5;
54
+
55
+ // The size of the arrowhead is proportional to its length, but we still allow
56
+ // the relative size of the head to be controlled via the mark’s width option;
57
+ // doubling the default radius will produce an arrowhead that is twice as big.
58
+ // That said, we’ll probably want a arrow with a fixed head size, too.
59
+ const wingRatio = defaultRadius * 5;
60
+
61
+ let {
62
+ data = [{}],
63
+ canvas,
64
+ shape = 'arrow',
65
+ anchor = 'middle',
66
+ r = defaultRadius,
67
+ ...options
68
+ }: VectorMarkProps = $props();
69
+
70
+ const { getPlotState } = getContext<PlotContext>('svelteplot');
71
+ const plot = $derived(getPlotState());
72
+
73
+ const { getTestFacet } = getContext<FacetContext>('svelteplot/facet');
74
+ const testFacet = $derived(getTestFacet());
75
+
76
+ const shapeArrow: ShapeRenderer = {
77
+ draw(context: D3Path, l: number, r: number) {
78
+ const wing = (l * r) / wingRatio;
79
+ context.moveTo(0, 0);
80
+ context.lineTo(0, -l);
81
+ context.moveTo(-wing, wing - l);
82
+ context.lineTo(0, -l);
83
+ context.lineTo(wing, wing - l);
84
+ }
85
+ };
86
+
87
+ const shapeSpike: ShapeRenderer = {
88
+ draw(context: D3Path, l: number, r: number) {
89
+ context.moveTo(-r, 0);
90
+ context.lineTo(0, -l);
91
+ context.lineTo(r, 0);
92
+ }
93
+ };
94
+
95
+ const shapeArrowFilled: ShapeRenderer = {
96
+ draw(context: D3Path, l: number, r: number) {
97
+ // const wing = (l * r) / wingRatio;
98
+ const headLength = Math.max(3, l * 0.3);
99
+ const headSpike = headLength * 0.2;
100
+ const headWidth = Math.max(2, l * 0.3);
101
+ const tailWidth = Math.max(2, l * 0.3) * 0.3;
102
+
103
+ context.moveTo(0, 0);
104
+
105
+ context.lineTo(tailWidth * 0.5, -l + headLength - headSpike);
106
+ context.lineTo(headWidth * 0.5, -l + headLength);
107
+ context.lineTo(0, -l);
108
+ context.lineTo(-headWidth * 0.5, -l + headLength);
109
+ context.lineTo(-tailWidth * 0.5, -l + headLength - headSpike);
110
+
111
+ context.closePath();
112
+ }
113
+ };
114
+
115
+ const shapes = new Map([
116
+ ['arrow', shapeArrow],
117
+ ['arrow-filled', shapeArrowFilled],
118
+ ['spike', shapeSpike]
119
+ ]);
120
+
121
+ function isShapeObject(value: any): value is ShapeRenderer {
122
+ return value && typeof value.draw === 'function';
123
+ }
124
+
125
+ function maybeShape(shape: 'arrow' | 'spike' | 'arrow-filled' | ShapeRenderer) {
126
+ if (isShapeObject(shape)) return shape;
127
+ const value = shapes.get(`${shape}`.toLowerCase());
128
+ if (value) return value;
129
+ throw new Error(`invalid shape: ${shape}`);
130
+ }
131
+
132
+ function shapePath(
133
+ shape: 'arrow' | 'spike' | 'arrow-filled' | ShapeRenderer,
134
+ l: number,
135
+ r: number
136
+ ) {
137
+ const context = path();
138
+ maybeShape(shape).draw(context, l, r);
139
+ return context.toString();
140
+ }
141
+
142
+ const args = $derived(
143
+ sort({
144
+ data: maybeData(data),
145
+ // sort by descending radius by default
146
+ ...options
147
+ })
148
+ );
149
+ </script>
150
+
151
+ <Mark
152
+ type="vector"
153
+ required={['x', 'y']}
154
+ channels={[
155
+ 'x',
156
+ 'y',
157
+ 'r',
158
+ 'length',
159
+ 'symbol',
160
+ 'fill',
161
+ 'opacity',
162
+ 'stroke',
163
+ 'fillOpacity',
164
+ 'strokeOpacity'
165
+ ]}
166
+ {...args}>
167
+ {#snippet children({ mark, scaledData, usedScales })}
168
+ <g class="vector" data-l={usedScales.length}>
169
+ {#if canvas}
170
+ <text x="30" y="30" style="color:red"
171
+ >implement canvas rendering for vector mark</text>
172
+ {:else}
173
+ {#each scaledData as d}
174
+ {@const r = resolveChannel('r', d.datum, { r: 3, ...args })}
175
+ {#if d.valid && isValid(r)}
176
+ {@const dx = +resolveProp(args.dx, d.datum, 0)}
177
+ {@const dy = +resolveProp(args.dx, d.datum, 0)}
178
+ {@const [style, styleClass] = resolveStyles(
179
+ plot,
180
+ d,
181
+ {
182
+ strokeWidth: 1.5,
183
+ strokeLinejoin: 'round',
184
+ strokeLinecap: 'round',
185
+ ...args
186
+ },
187
+ shape === 'arrow-filled' ? 'fill' : 'stroke',
188
+ usedScales
189
+ )}
190
+ <path
191
+ d={shapePath(shape, d.length, r)}
192
+ transform="translate({d.x + dx}, {d.y + dy}) rotate({resolveProp(
193
+ args.rotate,
194
+ d.datum,
195
+ 0
196
+ )}) {anchor === 'start'
197
+ ? ''
198
+ : anchor === 'end'
199
+ ? `translate(0, ${d.length})`
200
+ : `translate(0, ${d.length / 2})`}"
201
+ {style}
202
+ use:addEventHandlers={{
203
+ getPlotState,
204
+ options: args,
205
+ datum: d.datum
206
+ }}
207
+ class={[styleClass]} />
208
+ {/if}
209
+ {/each}
210
+ {/if}
211
+ </g>
212
+ {/snippet}
213
+ </Mark>
214
+
215
+ <style>
216
+ path {
217
+ stroke-width: 1.5px;
218
+ }
219
+ </style>
@@ -0,0 +1,31 @@
1
+ import type { DataRecord, BaseMarkProps, ConstantAccessor, ChannelAccessor } from '../types.js';
2
+ type D3Path = ReturnType<typeof import('d3-path').path>;
3
+ export type ShapeRenderer = {
4
+ draw(context: D3Path, l: number, r: number): void;
5
+ };
6
+ export type VectorMarkProps = BaseMarkProps & {
7
+ data: DataRecord[];
8
+ x: ChannelAccessor;
9
+ y: ChannelAccessor;
10
+ r?: number;
11
+ length?: ChannelAccessor;
12
+ rotate?: ChannelAccessor;
13
+ fill?: ChannelAccessor;
14
+ stroke?: ChannelAccessor;
15
+ /**
16
+ * Controls where the vector is anchored in relation to the x, y position.
17
+ * If set to 'start', the arrow will start at the x, y position. If set to
18
+ * 'middle', the arrow will be centered at the x, y position. If set to
19
+ * 'end', the arrow will end at the x, y position.
20
+ */
21
+ anchor: 'start' | 'middle' | 'end';
22
+ shape?: 'arrow' | 'spike' | 'arrow-filled' | ShapeRenderer;
23
+ children?: Snippet;
24
+ dx?: ConstantAccessor<number>;
25
+ dy?: ConstantAccessor<number>;
26
+ canvas?: boolean;
27
+ };
28
+ import { type Snippet } from 'svelte';
29
+ declare const Vector: import("svelte").Component<VectorMarkProps, {}, "">;
30
+ type Vector = ReturnType<typeof Vector>;
31
+ export default Vector;
@@ -0,0 +1,210 @@
1
+ <script lang="ts">
2
+ // this component only takes care of rendering the x axis so we can re-use it
3
+ // for the facet labels
4
+ import { getContext, untrack } from 'svelte';
5
+ import removeIdenticalLines from '../../helpers/removeIdenticalLines.js';
6
+ import type {
7
+ AutoMarginStores,
8
+ ChannelAccessor,
9
+ ConstantAccessor,
10
+ PlotState,
11
+ RawValue,
12
+ ScaleType
13
+ } from '../../types.js';
14
+ import { resolveScaledStyles, resolveProp } from '../../helpers/resolve.js';
15
+ import { max } from 'd3-array';
16
+ import { randomId, testFilter } from '../../helpers/index.js';
17
+
18
+ type BaseAxisXProps = {
19
+ scaleFn: (d: RawValue) => number;
20
+ scaleType: ScaleType;
21
+ ticks: RawValue[];
22
+ tickFormat: (d: RawValue, i: number) => string | string[];
23
+ anchor: 'top' | 'bottom';
24
+ tickSize: number;
25
+ tickPadding: number;
26
+ tickFontSize: ConstantAccessor<number>;
27
+ tickClass: ConstantAccessor<string>;
28
+ marginTop: number;
29
+ height: number;
30
+ title: string;
31
+ options: {
32
+ dx: ConstantAccessor<number>;
33
+ dy: ConstantAccessor<number>;
34
+ filter: ChannelAccessor;
35
+ };
36
+ plot: PlotState;
37
+ };
38
+
39
+ let {
40
+ scaleFn,
41
+ scaleType,
42
+ ticks,
43
+ tickFormat,
44
+ anchor,
45
+ tickSize,
46
+ tickPadding,
47
+ tickFontSize,
48
+ tickClass,
49
+ marginTop,
50
+ height,
51
+ options,
52
+ plot,
53
+ title
54
+ }: BaseAxisXProps = $props();
55
+
56
+ function splitTick(tick: string | string[]) {
57
+ return Array.isArray(tick) ? tick : [tick];
58
+ }
59
+
60
+ let tickRotate = $derived(plot.options.x.tickRotate || 0);
61
+
62
+ let tickY = $derived(anchor === 'bottom' ? marginTop + height : marginTop);
63
+
64
+ let isQuantitative = $derived(scaleType !== 'point' && scaleType !== 'band');
65
+
66
+ // generate id used for registering margins
67
+ const id = randomId();
68
+
69
+ const { autoMarginTop, autoMarginBottom } =
70
+ getContext<AutoMarginStores>('svelteplot/autoMargins');
71
+
72
+ let tickTextElements = $state([] as SVGTextElement[]);
73
+
74
+ const positionedTicks = $derived.by(() => {
75
+ let tickObjects = removeIdenticalLines(
76
+ ticks.map((tick, i) => {
77
+ return {
78
+ value: tick,
79
+ hidden: false,
80
+ dx: +resolveProp(options.dx, tick, 0),
81
+ dy: +resolveProp(options.dy, tick, 0),
82
+ x: scaleFn(tick) + (scaleType === 'band' ? scaleFn.bandwidth() * 0.5 : 0),
83
+ text: splitTick(tickFormat(tick, i)),
84
+ element: null as SVGTextElement | null
85
+ };
86
+ })
87
+ );
88
+ const T = tickObjects.length;
89
+ for (let i = 0; i < T; i++) {
90
+ let j = i;
91
+ // find the preceeding tick that was not hidden
92
+ do {
93
+ j--;
94
+ } while (j >= 0 && tickObjects[j].hidden);
95
+ if (j >= 0) {
96
+ const tickLabelSpace = Math.abs(tickObjects[i].x - tickObjects[j].x);
97
+ tickObjects[i].hidden = tickLabelSpace < 15;
98
+ }
99
+ }
100
+ return tickObjects;
101
+ });
102
+
103
+ $effect(() => {
104
+ untrack(() => [$autoMarginTop, $autoMarginBottom]);
105
+ const outsideTextAnchor = anchor === 'top' ? 'end' : 'start';
106
+ // measure tick label heights
107
+ const maxLabelHeight =
108
+ Math.ceil(
109
+ max(
110
+ positionedTicks.map((tick, i) => {
111
+ if (
112
+ resolveProp(options.anchor, tick.value, outsideTextAnchor) !==
113
+ outsideTextAnchor
114
+ )
115
+ return 0;
116
+ if (tick.hidden || !testFilter(tick.value, options)) return 0;
117
+ if (tickTextElements[i])
118
+ return tickTextElements[i].getBoundingClientRect().height;
119
+ return 0;
120
+ }) as number[]
121
+ )
122
+ ) +
123
+ Math.max(0, tickPadding + tickSize) +
124
+ (title ? 15 : 0);
125
+
126
+ if (!isNaN(maxLabelHeight)) {
127
+ if (anchor === 'top' && $autoMarginTop.get(id) !== maxLabelHeight) {
128
+ $autoMarginTop.set(id, maxLabelHeight);
129
+ } else if (anchor === 'bottom' && $autoMarginBottom.get(id) !== maxLabelHeight) {
130
+ $autoMarginBottom.set(id, maxLabelHeight);
131
+ }
132
+ }
133
+ });
134
+
135
+ $effect(() => {
136
+ // clear margins on destroy
137
+ return () => {
138
+ if ($autoMarginBottom.has(id)) $autoMarginBottom.delete(id);
139
+ if ($autoMarginTop.has(id)) $autoMarginTop.delete(id);
140
+ };
141
+ });
142
+ </script>
143
+
144
+ <g class="axis-x">
145
+ {#each positionedTicks as tick, t}
146
+ {#if testFilter(tick.value, options) && !tick.hidden}
147
+ {@const textLines = tick.text}
148
+ {@const prevTextLines = t && positionedTicks[t - 1].text}
149
+ {@const fontSize = resolveProp(tickFontSize, tick)}
150
+ {@const tickClass_ = resolveProp(tickClass, tick.value)}
151
+ {@const estLabelWidth = max(textLines.map((t) => t.length)) * fontSize * 0.2}
152
+ {@const moveDown =
153
+ (tickSize + tickPadding + (tickRotate !== 0 ? tickFontSize * 0.35 : 0)) *
154
+ (anchor === 'bottom' ? 1 : -1)}
155
+ <g
156
+ class="tick {tickClass_ || ''}"
157
+ transform="translate({tick.x + tick.dx}, {tickY + tick.dy})"
158
+ text-anchor={tickRotate < 0 ? 'end' : tickRotate > 0 ? 'start' : 'middle'}>
159
+ {#if tickSize}
160
+ <line
161
+ style={resolveScaledStyles(tick, options, {}, plot, 'stroke')}
162
+ y2={anchor === 'bottom' ? tickSize : -tickSize} />
163
+ {/if}
164
+
165
+ <text
166
+ bind:this={tickTextElements[t]}
167
+ transform="translate(0, {moveDown}) rotate({tickRotate})"
168
+ style:font-variant={isQuantitative ? 'tabular-nums' : 'normal'}
169
+ style={resolveScaledStyles(
170
+ tick,
171
+ { ...options, fontSize: tickFontSize, stroke: null },
172
+ {},
173
+ plot,
174
+ 'fill'
175
+ )}
176
+ x={0}
177
+ y={0}
178
+ dominant-baseline={tickRotate !== 0
179
+ ? 'central'
180
+ : anchor === 'bottom'
181
+ ? 'hanging'
182
+ : 'auto'}>
183
+ {#if ticks.length > 0 || t === 0 || t === ticks.length - 1}
184
+ {#if textLines.length === 1}
185
+ {textLines[0]}
186
+ {:else}
187
+ {#each textLines as line, i}
188
+ <tspan x="0" dy={i ? 12 : 0}
189
+ >{!prevTextLines || prevTextLines[i] !== line
190
+ ? line
191
+ : ''}</tspan>
192
+ {/each}
193
+ {/if}
194
+ {/if}
195
+ </text>
196
+ </g>
197
+ {/if}
198
+ {/each}
199
+ </g>
200
+
201
+ <style>
202
+ line {
203
+ stroke: currentColor;
204
+ }
205
+ text {
206
+ font-size: 11px;
207
+ opacity: 0.8;
208
+ fill: currentColor;
209
+ }
210
+ </style>
@@ -0,0 +1,24 @@
1
+ import type { ChannelAccessor, ConstantAccessor, PlotState, RawValue, ScaleType } from '../../types.js';
2
+ type BaseAxisXProps = {
3
+ scaleFn: (d: RawValue) => number;
4
+ scaleType: ScaleType;
5
+ ticks: RawValue[];
6
+ tickFormat: (d: RawValue, i: number) => string | string[];
7
+ anchor: 'top' | 'bottom';
8
+ tickSize: number;
9
+ tickPadding: number;
10
+ tickFontSize: ConstantAccessor<number>;
11
+ tickClass: ConstantAccessor<string>;
12
+ marginTop: number;
13
+ height: number;
14
+ title: string;
15
+ options: {
16
+ dx: ConstantAccessor<number>;
17
+ dy: ConstantAccessor<number>;
18
+ filter: ChannelAccessor;
19
+ };
20
+ plot: PlotState;
21
+ };
22
+ declare const BaseAxisX: import("svelte").Component<BaseAxisXProps, {}, "">;
23
+ type BaseAxisX = ReturnType<typeof BaseAxisX>;
24
+ export default BaseAxisX;
@@ -0,0 +1,187 @@
1
+ <script lang="ts">
2
+ import { getContext, untrack } from 'svelte';
3
+ import { randomId, testFilter } from '../../helpers/index.js';
4
+ import { resolveProp, resolveScaledStyles } from '../../helpers/resolve.js';
5
+ import { max } from 'd3-array';
6
+ import type {
7
+ AutoMarginStores,
8
+ ConstantAccessor,
9
+ PlotState,
10
+ RawValue,
11
+ ScaleType
12
+ } from '../../types.js';
13
+
14
+ type BaseAxisYProps = {
15
+ scaleFn: (d: RawValue) => number;
16
+ scaleType: ScaleType;
17
+ ticks: RawValue[];
18
+ tickFormat: (d: RawValue) => string | string[];
19
+ anchor: 'left' | 'right';
20
+ lineAnchor: 'top' | 'center' | 'bottom';
21
+ tickSize: number;
22
+ tickPadding: number;
23
+ tickFontSize: ConstantAccessor<number>;
24
+ marginLeft: number;
25
+ width: number;
26
+ title: string | null;
27
+ options: {
28
+ dx: ConstantAccessor<number>;
29
+ dy: ConstantAccessor<number>;
30
+ };
31
+ plot: PlotState;
32
+ };
33
+
34
+ let {
35
+ scaleFn,
36
+ scaleType,
37
+ ticks,
38
+ tickFormat,
39
+ anchor,
40
+ lineAnchor,
41
+ tickSize,
42
+ tickPadding,
43
+ tickFontSize,
44
+ tickClass,
45
+ marginLeft,
46
+ width,
47
+ title,
48
+ plot,
49
+ options
50
+ }: BaseAxisYProps = $props();
51
+
52
+ const LINE_ANCHOR = {
53
+ top: 'hanging',
54
+ center: 'middle',
55
+ bottom: 'auto'
56
+ };
57
+
58
+ const positionedTicks = $derived.by(() => {
59
+ let tickObjects = ticks.map((tick, i) => {
60
+ return {
61
+ value: tick,
62
+ hidden: false,
63
+ dx: +resolveProp(options.dx, tick, 0),
64
+ dy: +resolveProp(options.dy, tick, 0),
65
+ y: scaleFn(tick) + (scaleType === 'band' ? scaleFn.bandwidth() * 0.5 : 0),
66
+ text: tickFormat(tick),
67
+ element: null as SVGTextElement | null
68
+ };
69
+ });
70
+ const T = tickObjects.length;
71
+ for (let i = 0; i < T; i++) {
72
+ let j = i;
73
+ // find the preceeding tick that was not hidden
74
+ do {
75
+ j--;
76
+ } while (j >= 0 && tickObjects[j].hidden);
77
+ if (j >= 0) {
78
+ const tickLabelSpace = Math.abs(tickObjects[i].y - tickObjects[j].y);
79
+ tickObjects[i].hidden = tickLabelSpace < 15;
80
+ }
81
+ }
82
+ return tickObjects;
83
+ });
84
+
85
+ let tickTexts = $state([] as SVGTextElement[]);
86
+
87
+ // generate id used for registering margins
88
+ const id = randomId();
89
+
90
+ const { autoMarginLeft, autoMarginRight, autoMarginTop } =
91
+ getContext<AutoMarginStores>('svelteplot/autoMargins');
92
+
93
+ $effect(() => {
94
+ untrack(() => [$autoMarginLeft, $autoMarginRight]);
95
+ const outsideTextAnchor = anchor === 'left' ? 'end' : 'start';
96
+ // measure tick label widths
97
+ const maxLabelWidth =
98
+ Math.ceil(
99
+ max(
100
+ positionedTicks.map((tick, i) => {
101
+ if (
102
+ resolveProp(options.textAnchor, tick.value, outsideTextAnchor) !==
103
+ outsideTextAnchor
104
+ )
105
+ return 0;
106
+ if (tick.hidden || !testFilter(tick.value, options)) return 0;
107
+ if (tickTexts[i]) return tickTexts[i].getBoundingClientRect().width;
108
+ return 0;
109
+ }) as number[]
110
+ )
111
+ ) + Math.max(0, tickPadding + tickSize);
112
+
113
+ if (!isNaN(maxLabelWidth)) {
114
+ if (anchor === 'left' && $autoMarginLeft.get(id) !== maxLabelWidth) {
115
+ $autoMarginLeft.set(id, maxLabelWidth);
116
+ } else if (anchor === 'right' && $autoMarginRight.get(id) !== maxLabelWidth) {
117
+ $autoMarginRight.set(id, maxLabelWidth);
118
+ }
119
+ }
120
+ });
121
+
122
+ $effect(() => {
123
+ untrack(() => [$autoMarginTop]);
124
+ if (title) {
125
+ // add margin top to make some space for title
126
+ $autoMarginTop.set(id, 20);
127
+ } else {
128
+ // no need for extra margin top
129
+ $autoMarginTop.delete(id);
130
+ }
131
+ });
132
+
133
+ $effect(() => {
134
+ // clear margins on destroy
135
+ return () => {
136
+ if ($autoMarginLeft.has(id)) $autoMarginLeft.delete(id);
137
+ if ($autoMarginRight.has(id)) $autoMarginRight.delete(id);
138
+ if ($autoMarginTop.has(id)) $autoMarginTop.delete(id);
139
+ };
140
+ });
141
+ </script>
142
+
143
+ <g class="axis-y">
144
+ {#each positionedTicks as tick, t}
145
+ {#if testFilter(tick.value, options) && !tick.hidden}
146
+ {@const tickClass_ = resolveProp(tickClass, tick.value)}
147
+ <g
148
+ class="tick {tickClass_ || ''}"
149
+ transform="translate({tick.dx +
150
+ marginLeft +
151
+ (anchor === 'left' ? 0 : width)},{tick.y + tick.dy})">
152
+ {#if tickSize}
153
+ <line
154
+ style={resolveScaledStyles(tick.value, options, {}, plot, 'stroke')}
155
+ x2={anchor === 'left' ? -tickSize : tickSize} />
156
+ {/if}
157
+ <text
158
+ bind:this={tickTexts[t]}
159
+ class={{ 'is-left': anchor === 'left' }}
160
+ style={resolveScaledStyles(
161
+ tick.value,
162
+ { ...options, fontSize: tickFontSize, stroke: null },
163
+ {},
164
+ plot,
165
+ 'fill'
166
+ )}
167
+ x={(tickSize + tickPadding) * (anchor === 'left' ? -1 : 1)}
168
+ dominant-baseline={LINE_ANCHOR[lineAnchor]}
169
+ >{Array.isArray(tick.text) ? tick.text.join(' ') : tick.text}</text>
170
+ </g>
171
+ {/if}
172
+ {/each}
173
+ </g>
174
+
175
+ <style>
176
+ line {
177
+ stroke: currentColor;
178
+ }
179
+ text {
180
+ font-size: 11px;
181
+ opacity: 0.8;
182
+ fill: currentColor;
183
+ }
184
+ text.is-left {
185
+ text-anchor: end;
186
+ }
187
+ </style>
@@ -0,0 +1,23 @@
1
+ import type { ConstantAccessor, PlotState, RawValue, ScaleType } from '../../types.js';
2
+ type BaseAxisYProps = {
3
+ scaleFn: (d: RawValue) => number;
4
+ scaleType: ScaleType;
5
+ ticks: RawValue[];
6
+ tickFormat: (d: RawValue) => string | string[];
7
+ anchor: 'left' | 'right';
8
+ lineAnchor: 'top' | 'center' | 'bottom';
9
+ tickSize: number;
10
+ tickPadding: number;
11
+ tickFontSize: ConstantAccessor<number>;
12
+ marginLeft: number;
13
+ width: number;
14
+ title: string | null;
15
+ options: {
16
+ dx: ConstantAccessor<number>;
17
+ dy: ConstantAccessor<number>;
18
+ };
19
+ plot: PlotState;
20
+ };
21
+ declare const BaseAxisY: import("svelte").Component<BaseAxisYProps, {}, "">;
22
+ type BaseAxisY = ReturnType<typeof BaseAxisY>;
23
+ export default BaseAxisY;