@rokkit/chart 1.0.0-next.98 → 1.0.1

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 (137) hide show
  1. package/README.md +150 -46
  2. package/package.json +40 -51
  3. package/src/AnimatedPlot.svelte +383 -0
  4. package/src/Chart.svelte +87 -59
  5. package/src/ChartExporter.svelte +125 -0
  6. package/src/ChartLegend.svelte +148 -0
  7. package/src/ChartProvider.svelte +10 -0
  8. package/src/FacetPlot/Panel.svelte +37 -0
  9. package/src/FacetPlot.svelte +114 -0
  10. package/src/Plot/Arc.svelte +29 -0
  11. package/src/Plot/Area.svelte +32 -0
  12. package/src/Plot/Axis.svelte +164 -0
  13. package/src/Plot/Bar.svelte +54 -0
  14. package/src/Plot/Grid.svelte +34 -0
  15. package/src/Plot/Legend.svelte +268 -0
  16. package/src/Plot/Line.svelte +37 -0
  17. package/src/Plot/Point.svelte +40 -0
  18. package/src/Plot/Root.svelte +62 -0
  19. package/src/Plot/Timeline.svelte +95 -0
  20. package/src/Plot/Tooltip.svelte +87 -0
  21. package/src/Plot/index.js +9 -0
  22. package/src/Plot.svelte +313 -0
  23. package/src/PlotState.svelte.js +375 -0
  24. package/src/Sparkline.svelte +108 -0
  25. package/src/Symbol.svelte +15 -11
  26. package/src/Texture.svelte +9 -7
  27. package/src/charts/AreaChart.svelte +30 -0
  28. package/src/charts/BarChart.svelte +31 -0
  29. package/src/charts/BoxPlot.svelte +24 -0
  30. package/src/charts/BubbleChart.svelte +28 -0
  31. package/src/charts/LineChart.svelte +31 -0
  32. package/src/charts/PieChart.svelte +35 -0
  33. package/src/charts/ScatterPlot.svelte +31 -0
  34. package/src/charts/ViolinPlot.svelte +24 -0
  35. package/src/crossfilter/CrossFilter.svelte +42 -0
  36. package/src/crossfilter/FilterBar.svelte +24 -0
  37. package/src/crossfilter/FilterHistogram.svelte +290 -0
  38. package/src/crossfilter/FilterSlider.svelte +83 -0
  39. package/src/crossfilter/createCrossFilter.svelte.js +124 -0
  40. package/src/elements/Bar.svelte +20 -16
  41. package/src/elements/ColorRamp.svelte +19 -19
  42. package/src/elements/ContinuousLegend.svelte +41 -19
  43. package/src/elements/DefinePatterns.svelte +9 -7
  44. package/src/elements/DiscreteLegend.svelte +13 -11
  45. package/src/elements/Label.svelte +4 -9
  46. package/src/elements/SymbolGrid.svelte +8 -9
  47. package/src/examples/BarChartExample.svelte +81 -0
  48. package/src/geoms/Arc.svelte +126 -0
  49. package/src/geoms/Area.svelte +78 -0
  50. package/src/geoms/Bar.svelte +200 -0
  51. package/src/geoms/Box.svelte +113 -0
  52. package/src/geoms/Candlestick.svelte +110 -0
  53. package/src/geoms/Heatmap.svelte +92 -0
  54. package/src/geoms/Hexbin.svelte +114 -0
  55. package/src/geoms/LabelPill.svelte +17 -0
  56. package/src/geoms/Line.svelte +123 -0
  57. package/src/geoms/Point.svelte +145 -0
  58. package/src/geoms/Ribbon.svelte +161 -0
  59. package/src/geoms/Violin.svelte +56 -0
  60. package/src/geoms/Waterfall.svelte +114 -0
  61. package/src/geoms/lib/areas.js +154 -0
  62. package/src/geoms/lib/bars.js +223 -0
  63. package/src/index.js +81 -2
  64. package/src/lib/brewing/BoxBrewer.svelte.js +14 -0
  65. package/src/lib/brewing/CartesianBrewer.svelte.js +21 -0
  66. package/src/lib/brewing/PieBrewer.svelte.js +14 -0
  67. package/src/lib/brewing/QuartileBrewer.svelte.js +51 -0
  68. package/src/lib/brewing/ViolinBrewer.svelte.js +14 -0
  69. package/src/lib/brewing/axes.svelte.js +270 -0
  70. package/src/lib/brewing/bars.svelte.js +201 -0
  71. package/src/lib/brewing/brewer.svelte.js +277 -0
  72. package/src/lib/brewing/colors.js +157 -0
  73. package/src/lib/brewing/dimensions.svelte.js +56 -0
  74. package/src/lib/brewing/index.svelte.js +205 -0
  75. package/src/lib/brewing/legends.svelte.js +137 -0
  76. package/src/lib/brewing/marks/arcs.js +43 -0
  77. package/src/lib/brewing/marks/areas.js +72 -0
  78. package/src/lib/brewing/marks/bars.js +49 -0
  79. package/src/lib/brewing/marks/boxes.js +75 -0
  80. package/src/lib/brewing/marks/lines.js +55 -0
  81. package/src/lib/brewing/marks/points.js +105 -0
  82. package/src/lib/brewing/marks/violins.js +90 -0
  83. package/src/lib/brewing/patterns.js +45 -0
  84. package/src/lib/brewing/scales.js +51 -0
  85. package/src/lib/brewing/scales.svelte.js +82 -0
  86. package/src/lib/brewing/stats.js +74 -0
  87. package/src/lib/brewing/symbols.js +10 -0
  88. package/src/lib/brewing/types.js +73 -0
  89. package/src/lib/chart.js +16 -8
  90. package/src/lib/context.js +131 -0
  91. package/src/lib/keyboard-nav.js +37 -0
  92. package/src/lib/plot/chartProps.js +76 -0
  93. package/src/lib/plot/crossfilter.js +16 -0
  94. package/src/lib/plot/facet.js +58 -0
  95. package/src/lib/plot/frames.js +81 -0
  96. package/src/lib/plot/helpers.js +14 -0
  97. package/src/lib/plot/preset.js +67 -0
  98. package/src/lib/plot/scales.js +81 -0
  99. package/src/lib/plot/stat.js +92 -0
  100. package/src/lib/plot/types.js +65 -0
  101. package/src/lib/preset.js +41 -0
  102. package/src/lib/scales.svelte.js +151 -0
  103. package/src/lib/swatch.js +4 -7
  104. package/src/lib/utils.js +142 -0
  105. package/src/lib/xscale.js +31 -0
  106. package/src/patterns/DefinePatterns.svelte +32 -0
  107. package/src/patterns/PatternDef.svelte +27 -0
  108. package/src/patterns/index.js +4 -14
  109. package/src/patterns/patterns.js +360 -0
  110. package/src/patterns/scale.js +116 -0
  111. package/src/spec/chart-spec.js +72 -0
  112. package/src/symbols/RoundedSquare.svelte +18 -12
  113. package/src/symbols/Shape.svelte +22 -16
  114. package/src/symbols/constants/index.js +1 -1
  115. package/LICENSE +0 -21
  116. package/src/lib/index.js +0 -4
  117. package/src/lib/plots.js +0 -27
  118. package/src/patterns/Brick.svelte +0 -17
  119. package/src/patterns/Circles.svelte +0 -18
  120. package/src/patterns/CrossHatch.svelte +0 -14
  121. package/src/patterns/CurvedWave.svelte +0 -9
  122. package/src/patterns/Dots.svelte +0 -19
  123. package/src/patterns/OutlineCircles.svelte +0 -15
  124. package/src/patterns/Tile.svelte +0 -17
  125. package/src/patterns/Triangles.svelte +0 -15
  126. package/src/patterns/Waves.svelte +0 -13
  127. package/src/patterns/paths/NamedPattern.svelte +0 -12
  128. package/src/patterns/paths/constants.js +0 -4
  129. package/src/plots/Plot.svelte +0 -40
  130. package/src/plots/index.js +0 -1
  131. package/src/template/Texture.svelte +0 -16
  132. package/src/template/constants.js +0 -43
  133. package/src/template/shapes/Circles.svelte +0 -16
  134. package/src/template/shapes/Lines.svelte +0 -17
  135. package/src/template/shapes/Path.svelte +0 -12
  136. package/src/template/shapes/Polygons.svelte +0 -18
  137. package/src/template/shapes/index.js +0 -4
@@ -0,0 +1,360 @@
1
+ /**
2
+ * Unified pattern library.
3
+ *
4
+ * Each entry is an array of mark descriptors. All geometry is in normalized
5
+ * 0–1 coordinates; the renderer scales by `size` at paint time.
6
+ *
7
+ * Mark types
8
+ * ----------
9
+ * circle { cx, cy, r }
10
+ * line { x1, y1, x2, y2 } coordinates may exceed 0–1 for seamless tiling
11
+ * polygon { points: [x,y][] }
12
+ * rect { x, y, w, h }
13
+ * path { d: [cmd, ...args][] } SVG path commands as nested arrays (H/V take one arg)
14
+ *
15
+ * Appearance
16
+ * ----------
17
+ * fill: true → element uses fill color (stroke: none)
18
+ * fill: false → element uses stroke color (fill: none) ← default
19
+ * fillOpacity: n → applied as fill-opacity on filled marks
20
+ * opacity: n → applied as opacity on any mark
21
+ */
22
+
23
+ /** @typedef {{ type: 'circle', cx: number, cy: number, r: number, fill?: boolean, fillOpacity?: number, opacity?: number }} CircleMark */
24
+ /** @typedef {{ type: 'line', x1: number, y1: number, x2: number, y2: number, fill?: false, strokeWidth?: number }} LineMark */
25
+ /** @typedef {{ type: 'polygon', points: [number,number][], fill?: boolean, fillOpacity?: number, opacity?: number }} PolygonMark */
26
+ /** @typedef {{ type: 'rect', x: number, y: number, w: number, h: number, fill?: boolean, fillOpacity?: number, opacity?: number }} RectMark */
27
+ /** @typedef {{ type: 'path', d: (string|number)[][], fill?: boolean, fillOpacity?: number, opacity?: number }} PathMark */
28
+ /** @typedef {CircleMark | LineMark | PolygonMark | RectMark | PathMark} PatternMark */
29
+
30
+ /** @type {Record<string, PatternMark[]>} */
31
+ export const PATTERNS = {
32
+ // ── Line-based ──────────────────────────────────────────────────────────────
33
+
34
+ brick: [
35
+ { type: 'line', x1: 0, y1: 0.25, x2: 0.5, y2: 0.25 },
36
+ { type: 'line', x1: 0.5, y1: 0.75, x2: 1, y2: 0.75 },
37
+ { type: 'line', x1: 0, y1: 0, x2: 0, y2: 1 },
38
+ { type: 'line', x1: 1, y1: 0, x2: 1, y2: 1 },
39
+ { type: 'line', x1: 0.5, y1: 0, x2: 0.5, y2: 1 }
40
+ ],
41
+
42
+ hatch: [
43
+ { type: 'line', x1: 0, y1: 0.25, x2: 1, y2: 0.25 },
44
+ { type: 'line', x1: 0, y1: 0.5, x2: 1, y2: 0.5 },
45
+ { type: 'line', x1: 0, y1: 0.75, x2: 1, y2: 0.75 },
46
+ { type: 'line', x1: 0.25, y1: 0, x2: 0.25, y2: 1 },
47
+ { type: 'line', x1: 0.5, y1: 0, x2: 0.5, y2: 1 },
48
+ { type: 'line', x1: 0.75, y1: 0, x2: 0.75, y2: 1 }
49
+ ],
50
+
51
+ // Coordinates intentionally exceed 0–1 so lines tile seamlessly at tile edges
52
+ diagonal: [
53
+ { type: 'line', x1: -0.5, y1: 0.5, x2: 0.5, y2: -0.5 },
54
+ { type: 'line', x1: 0, y1: 1, x2: 1, y2: 0 },
55
+ { type: 'line', x1: 0.5, y1: 1.5, x2: 1.5, y2: 0.5 }
56
+ ],
57
+
58
+ // Two sets of diagonals crossing to form a diamond lattice
59
+ diamonds: [
60
+ { type: 'line', x1: 0, y1: 1, x2: 1, y2: 0 },
61
+ { type: 'line', x1: -0.5, y1: 0.5, x2: 0.5, y2: -0.5 },
62
+ { type: 'line', x1: 0.5, y1: 1.5, x2: 1.5, y2: 0.5 },
63
+ { type: 'line', x1: 0, y1: 0, x2: 1, y2: 1 },
64
+ { type: 'line', x1: -0.5, y1: 0.5, x2: 0.5, y2: 1.5 },
65
+ { type: 'line', x1: 0.5, y1: -0.5, x2: 1.5, y2: 0.5 }
66
+ ],
67
+
68
+ tile: [
69
+ { type: 'line', x1: 0, y1: 0.5, x2: 1, y2: 0.5 },
70
+ { type: 'line', x1: 0.5, y1: 0, x2: 0.5, y2: 1 }
71
+ ],
72
+
73
+ // ── Path-based ──────────────────────────────────────────────────────────────
74
+
75
+ swell: [
76
+ {
77
+ type: 'path',
78
+ d: [
79
+ ['M', 0, 0.3],
80
+ ['A', 0.6, 0.5, 0, 0, 0, 1, 0.3]
81
+ ]
82
+ }
83
+ ],
84
+
85
+ waves: [
86
+ {
87
+ type: 'path',
88
+ d: [
89
+ ['M', 0, 0.5],
90
+ ['L', 0.25, 0],
91
+ ['L', 0.75, 1],
92
+ ['L', 1, 0.5]
93
+ ]
94
+ }
95
+ ],
96
+
97
+ // ── Circle-based ────────────────────────────────────────────────────────────
98
+
99
+ // Small filled dots in an X pattern across the tile
100
+ dots: [
101
+ { type: 'circle', cx: 0.2, cy: 0.2, r: 0.08, fill: true },
102
+ { type: 'circle', cx: 0.4, cy: 0.4, r: 0.08, fill: true },
103
+ { type: 'circle', cx: 0.6, cy: 0.6, r: 0.08, fill: true },
104
+ { type: 'circle', cx: 0.8, cy: 0.8, r: 0.08, fill: true },
105
+ { type: 'circle', cx: 0.8, cy: 0.2, r: 0.08, fill: true },
106
+ { type: 'circle', cx: 0.6, cy: 0.4, r: 0.08, fill: true },
107
+ { type: 'circle', cx: 0.4, cy: 0.6, r: 0.08, fill: true },
108
+ { type: 'circle', cx: 0.2, cy: 0.8, r: 0.08, fill: true }
109
+ ],
110
+
111
+ // Outlined diamond (rotated square)
112
+ lattice: [
113
+ {
114
+ type: 'polygon',
115
+ points: [
116
+ [0.5, 0.22],
117
+ [0.78, 0.5],
118
+ [0.5, 0.78],
119
+ [0.22, 0.5]
120
+ ]
121
+ }
122
+ ],
123
+
124
+ // Fish-scale geometry as stroke only — same circles as petals, no fill
125
+ scales: [
126
+ {
127
+ type: 'path',
128
+ d: [
129
+ ['M', -0.7071, 0],
130
+ ['a', 0.7071, 0.7071, 0, 1, 0, 1.4142, 0],
131
+ ['a', 0.7071, 0.7071, 0, 1, 0, -1.4142, 0],
132
+ ['M', 0.2929, 0],
133
+ ['a', 0.7071, 0.7071, 0, 1, 0, 1.4142, 0],
134
+ ['a', 0.7071, 0.7071, 0, 1, 0, -1.4142, 0],
135
+ ['M', -0.2071, 0.5],
136
+ ['a', 0.7071, 0.7071, 0, 1, 0, 1.4142, 0],
137
+ ['a', 0.7071, 0.7071, 0, 1, 0, -1.4142, 0],
138
+ ['M', -0.7071, 1],
139
+ ['a', 0.7071, 0.7071, 0, 1, 0, 1.4142, 0],
140
+ ['a', 0.7071, 0.7071, 0, 1, 0, -1.4142, 0],
141
+ ['M', 0.2929, 1],
142
+ ['a', 0.7071, 0.7071, 0, 1, 0, 1.4142, 0],
143
+ ['a', 0.7071, 0.7071, 0, 1, 0, -1.4142, 0]
144
+ ]
145
+ }
146
+ ],
147
+
148
+ // Filled circles at tile corners + center (5-point dice pattern)
149
+ circles: [
150
+ { type: 'circle', cx: 0.5, cy: 0.5, r: 0.2, fill: true },
151
+ { type: 'circle', cx: 0, cy: 0, r: 0.2, fill: true },
152
+ { type: 'circle', cx: 1, cy: 0, r: 0.2, fill: true },
153
+ { type: 'circle', cx: 0, cy: 1, r: 0.2, fill: true },
154
+ { type: 'circle', cx: 1, cy: 1, r: 0.2, fill: true }
155
+ ],
156
+
157
+ // Diagonal cross strokes with a filled circle at the center
158
+ pip: [
159
+ { type: 'line', x1: 0, y1: 0, x2: 1, y2: 1 },
160
+ { type: 'line', x1: 0, y1: 1, x2: 1, y2: 0 },
161
+ { type: 'circle', cx: 0.5, cy: 0.5, r: 0.22, fill: true }
162
+ ],
163
+
164
+ // Two outlined circles + two filled circles in a 2×2 grid
165
+ rings: [
166
+ { type: 'circle', cx: 0.25, cy: 0.25, r: 0.2 },
167
+ { type: 'circle', cx: 0.75, cy: 0.75, r: 0.2 },
168
+ { type: 'circle', cx: 0.25, cy: 0.75, r: 0.2, fill: true },
169
+ { type: 'circle', cx: 0.75, cy: 0.25, r: 0.2, fill: true }
170
+ ],
171
+
172
+ // Fish-scale: circles centered at corners + tile center, r = 0.5√2 so each
173
+ // circle passes through its adjacent neighbors' centers. Even-odd fill produces
174
+ // the scallop/petal pattern where overlapping regions alternate filled/clear.
175
+ petals: [
176
+ {
177
+ type: 'path',
178
+ fill: true,
179
+ fillRule: 'evenodd',
180
+ d: [
181
+ // center (0, 0) r=0.7071 leftmost = (-0.7071, 0)
182
+ ['M', -0.7071, 0],
183
+ ['a', 0.7071, 0.7071, 0, 1, 0, 1.4142, 0],
184
+ ['a', 0.7071, 0.7071, 0, 1, 0, -1.4142, 0],
185
+ // center (1, 0) leftmost = (0.2929, 0)
186
+ ['M', 0.2929, 0],
187
+ ['a', 0.7071, 0.7071, 0, 1, 0, 1.4142, 0],
188
+ ['a', 0.7071, 0.7071, 0, 1, 0, -1.4142, 0],
189
+ // center (0.5, 0.5) leftmost = (-0.2071, 0.5)
190
+ ['M', -0.2071, 0.5],
191
+ ['a', 0.7071, 0.7071, 0, 1, 0, 1.4142, 0],
192
+ ['a', 0.7071, 0.7071, 0, 1, 0, -1.4142, 0],
193
+ // center (0, 1) leftmost = (-0.7071, 1)
194
+ ['M', -0.7071, 1],
195
+ ['a', 0.7071, 0.7071, 0, 1, 0, 1.4142, 0],
196
+ ['a', 0.7071, 0.7071, 0, 1, 0, -1.4142, 0],
197
+ // center (1, 1) leftmost = (0.2929, 1)
198
+ ['M', 0.2929, 1],
199
+ ['a', 0.7071, 0.7071, 0, 1, 0, 1.4142, 0],
200
+ ['a', 0.7071, 0.7071, 0, 1, 0, -1.4142, 0]
201
+ ]
202
+ }
203
+ ],
204
+
205
+ // ── Polygon-based ───────────────────────────────────────────────────────────
206
+
207
+ // Three filled triangles tiling the corners + center of the tile
208
+ triangles: [
209
+ {
210
+ type: 'polygon',
211
+ points: [
212
+ [0, 0.5],
213
+ [0.5, 1],
214
+ [0, 1]
215
+ ],
216
+ fill: true
217
+ },
218
+ {
219
+ type: 'polygon',
220
+ points: [
221
+ [0.5, 0],
222
+ [0, 0],
223
+ [0, 0.5]
224
+ ],
225
+ fill: true
226
+ },
227
+ {
228
+ type: 'polygon',
229
+ points: [
230
+ [1, 0],
231
+ [0.5, 0.5],
232
+ [1, 1]
233
+ ],
234
+ fill: true
235
+ }
236
+ ],
237
+
238
+ // Two filled triangles scattered at opposite corners
239
+ shards: [
240
+ {
241
+ type: 'polygon',
242
+ points: [
243
+ [0.25, 0.017],
244
+ [0, 0.417],
245
+ [0.5, 0.417]
246
+ ],
247
+ fill: true
248
+ },
249
+ {
250
+ type: 'polygon',
251
+ points: [
252
+ [0.5, 0.583],
253
+ [1, 0.583],
254
+ [0.75, 0.983]
255
+ ],
256
+ fill: true
257
+ }
258
+ ],
259
+
260
+ // Diagonal corner split — two-tone polygon division
261
+ wedge: [
262
+ {
263
+ type: 'polygon',
264
+ points: [
265
+ [1, 1],
266
+ [0.45, 1],
267
+ [0, 0.55],
268
+ [0, 0]
269
+ ],
270
+ fill: true
271
+ },
272
+ {
273
+ type: 'polygon',
274
+ points: [
275
+ [1, 0],
276
+ [0, 0],
277
+ [1, 1]
278
+ ],
279
+ fill: true,
280
+ fillOpacity: 0.55
281
+ }
282
+ ],
283
+
284
+ // Two triangles pointing inward from top and bottom edges
285
+ argyle: [
286
+ {
287
+ type: 'polygon',
288
+ points: [
289
+ [1, 0],
290
+ [0.5, 0.5],
291
+ [0, 0]
292
+ ],
293
+ fill: true
294
+ },
295
+ {
296
+ type: 'polygon',
297
+ points: [
298
+ [1, 1],
299
+ [0.5, 0.5],
300
+ [0, 1]
301
+ ],
302
+ fill: true
303
+ }
304
+ ],
305
+
306
+ // triangles pattern rotated 90° clockwise: (x,y) → (1−y, x)
307
+ chevrons: [
308
+ {
309
+ type: 'polygon',
310
+ points: [
311
+ [0, 0],
312
+ [0.5, 0],
313
+ [0, 0.5]
314
+ ],
315
+ fill: true
316
+ },
317
+ {
318
+ type: 'polygon',
319
+ points: [
320
+ [0.5, 0],
321
+ [1, 0],
322
+ [1, 0.5]
323
+ ],
324
+ fill: true
325
+ },
326
+ {
327
+ type: 'polygon',
328
+ points: [
329
+ [0, 1],
330
+ [1, 1],
331
+ [0.5, 0.5]
332
+ ],
333
+ fill: true
334
+ }
335
+ ],
336
+
337
+ // ── Rect-based ──────────────────────────────────────────────────────────────
338
+
339
+ checkerboard: [
340
+ { type: 'rect', x: 0, y: 0, w: 0.5, h: 0.5, fill: true },
341
+ { type: 'rect', x: 0.5, y: 0.5, w: 0.5, h: 0.5, fill: true }
342
+ ],
343
+
344
+ // Quarter-circle filled shape (bottom-left corner) with two accent dots
345
+ shell: [
346
+ {
347
+ type: 'path',
348
+ d: [
349
+ ['M', 0, 1],
350
+ ['V', 0],
351
+ ['H', 0.6],
352
+ ['V', 0.4],
353
+ ['A', 0.6, 0.6, 0, 0, 1, 0, 1]
354
+ ],
355
+ fill: true
356
+ },
357
+ { type: 'circle', cx: 0.798, cy: 0.298, r: 0.081, fill: true },
358
+ { type: 'circle', cx: 0.798, cy: 0.702, r: 0.081, fill: true }
359
+ ]
360
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Mark geometry scaling and attribute resolution utilities.
3
+ *
4
+ * All pattern coordinates are stored normalized 0–1. `scaleMark` multiplies
5
+ * every geometric value by `size`. `resolveMarkAttrs` then folds in the
6
+ * runtime fill/stroke colors and produces a plain object whose keys are
7
+ * valid SVG attribute names, ready for spread (`{...attrs}`).
8
+ *
9
+ * Path `A` (arc) commands: indices 2, 3, 4 are xRotation, largeArcFlag, and
10
+ * sweepFlag — NOT coordinates — and are never scaled.
11
+ *
12
+ * Source → SVG name mappings applied by scaleMark:
13
+ * rect w → width, h → height
14
+ * polygon points [[x,y]…] → SVG points string
15
+ * path d [[cmd,…]…] → SVG d string
16
+ */
17
+
18
+ /** @import { PatternMark } from './patterns.js' */
19
+
20
+ /**
21
+ * Scale a single SVG path command by `size`.
22
+ * @param {(string|number)[]} cmd
23
+ * @param {number} size
24
+ * @returns {(string|number)[]}
25
+ */
26
+ function scalePathCmd(cmd, size) {
27
+ const [op, ...args] = cmd
28
+ if (op === 'A' || op === 'a') {
29
+ return [
30
+ op,
31
+ args[0] * size,
32
+ args[1] * size,
33
+ args[2],
34
+ args[3],
35
+ args[4],
36
+ args[5] * size,
37
+ args[6] * size
38
+ ]
39
+ }
40
+ return [op, ...args.map((v) => (typeof v === 'number' ? v * size : v))]
41
+ }
42
+
43
+ /**
44
+ * Scale all geometric coordinates in a mark by `size`.
45
+ * Renames rect `w`/`h` to `width`/`height` and converts polygon/path to strings.
46
+ * @param {PatternMark} mark
47
+ * @param {number} size
48
+ * @returns {object}
49
+ */
50
+ export function scaleMark(mark, size) {
51
+ switch (mark.type) {
52
+ case 'line':
53
+ return {
54
+ ...mark,
55
+ x1: mark.x1 * size,
56
+ y1: mark.y1 * size,
57
+ x2: mark.x2 * size,
58
+ y2: mark.y2 * size
59
+ }
60
+ case 'circle':
61
+ return { ...mark, cx: mark.cx * size, cy: mark.cy * size, r: mark.r * size }
62
+ case 'rect': {
63
+ const { w, h, ...rest } = mark
64
+ return { ...rest, x: mark.x * size, y: mark.y * size, width: w * size, height: h * size }
65
+ }
66
+ case 'polygon':
67
+ return { ...mark, points: mark.points.map(([x, y]) => `${x * size},${y * size}`).join(' ') }
68
+ case 'path':
69
+ return {
70
+ ...mark,
71
+ d: mark.d
72
+ .map((cmd) => scalePathCmd(cmd, size))
73
+ .map((cmd) => cmd.join(' '))
74
+ .join(' ')
75
+ }
76
+ default:
77
+ return mark
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Resolve a scaled mark into SVG-ready `{ type, attrs }`.
83
+ * `attrs` can be spread directly onto the SVG element.
84
+ *
85
+ * Per-mark overrides respected:
86
+ * mark.strokeWidth → `stroke-width` (takes precedence over `thickness`)
87
+ * mark.fillOpacity → `fill-opacity`
88
+ * mark.opacity → `opacity`
89
+ *
90
+ * @param {object} scaledMark Output of scaleMark
91
+ * @param {{ fill: string, stroke: string, thickness: number }} appearance
92
+ * @returns {{ type: string, attrs: object }}
93
+ */
94
+ export function resolveMarkAttrs(scaledMark, { fill, stroke, thickness }) {
95
+ const {
96
+ type,
97
+ fill: isFill,
98
+ strokeWidth,
99
+ fillOpacity,
100
+ opacity,
101
+ fillRule,
102
+ ...geometry
103
+ } = scaledMark
104
+
105
+ const attrs = {
106
+ ...geometry,
107
+ fill: isFill ? fill : 'none',
108
+ stroke: isFill ? 'none' : stroke,
109
+ 'stroke-width': isFill ? 0 : (strokeWidth ?? thickness)
110
+ }
111
+ if (fillOpacity !== null && fillOpacity !== undefined) attrs['fill-opacity'] = fillOpacity
112
+ if (opacity !== null && opacity !== undefined) attrs.opacity = opacity
113
+ if (fillRule !== null && fillRule !== undefined) attrs['fill-rule'] = fillRule
114
+
115
+ return { type, attrs }
116
+ }
@@ -0,0 +1,72 @@
1
+ export class ChartSpec {
2
+ constructor(data) {
3
+ this.data = data ?? []
4
+ this.channels = {}
5
+ this.layers = []
6
+ this.options = {}
7
+ }
8
+
9
+ x(f) {
10
+ this.channels.x = f
11
+ return this
12
+ }
13
+ y(f) {
14
+ this.channels.y = f
15
+ return this
16
+ }
17
+ color(f) {
18
+ this.channels.color = f
19
+ return this
20
+ }
21
+ pattern(f) {
22
+ this.channels.pattern = f
23
+ return this
24
+ }
25
+ aes(ch) {
26
+ Object.assign(this.channels, ch)
27
+ return this
28
+ }
29
+
30
+ bar(opts = {}) {
31
+ this.layers.push({ type: 'bar', ...opts })
32
+ return this
33
+ }
34
+ line(opts = {}) {
35
+ this.layers.push({ type: 'line', ...opts })
36
+ return this
37
+ }
38
+ area(opts = {}) {
39
+ this.layers.push({ type: 'area', ...opts })
40
+ return this
41
+ }
42
+ arc(opts = {}) {
43
+ this.layers.push({ type: 'arc', ...opts })
44
+ return this
45
+ }
46
+ point(opts = {}) {
47
+ this.layers.push({ type: 'point', ...opts })
48
+ return this
49
+ }
50
+
51
+ grid(opts = {}) {
52
+ this.options.grid = opts
53
+ return this
54
+ }
55
+ legend(opts = {}) {
56
+ this.options.legend = opts
57
+ return this
58
+ }
59
+ axis(type, opts = {}) {
60
+ this.options[`axis_${type}`] = opts
61
+ return this
62
+ }
63
+ size(w, h) {
64
+ this.options.width = w
65
+ this.options.height = h
66
+ return this
67
+ }
68
+ }
69
+
70
+ export function chart(data, channels = {}) {
71
+ return new ChartSpec(data).aes(channels)
72
+ }
@@ -1,15 +1,21 @@
1
1
  <script>
2
- export let x = 0
3
- export let y = 0
4
- export let size = 1
5
- export let fill = 'currentColor'
6
- export let stroke = 'currentColor'
2
+ let {
3
+ x = 0,
4
+ y = 0,
5
+ size = 1,
6
+ fill = 'currentColor',
7
+ stroke = 'currentColor',
8
+ onclick,
9
+ onmouseover,
10
+ onmouseleave,
11
+ onfocus,
12
+ ...restProps
13
+ } = $props()
7
14
 
8
- $: r = size * 3.534
9
- $: props = { rx: r * 0.1, ry: r * 0.1, ...$$restProps }
15
+ let r = $derived(size * 3.534)
16
+ let props = $derived({ rx: r * 0.1, ry: r * 0.1, ...restProps })
10
17
  </script>
11
18
 
12
- <!-- svelte-ignore a11y-click-events-have-key-events -->
13
19
  <rect
14
20
  x={x - r}
15
21
  y={y - r}
@@ -19,9 +25,9 @@
19
25
  {stroke}
20
26
  {...props}
21
27
  role="button"
22
- on:click
23
- on:mouseover
24
- on:mouseleave
25
- on:focus
28
+ {onclick}
29
+ {onmouseover}
30
+ {onmouseleave}
31
+ {onfocus}
26
32
  tabindex="0"
27
33
  />
@@ -1,31 +1,37 @@
1
1
  <script>
2
2
  import { namedShapes } from './constants'
3
3
 
4
- export let x = 0
5
- export let y = 0
6
- export let size = 1
7
- export let fill = 'none'
8
- export let stroke = 'currentColor'
9
- export let thickness = 1
4
+ let {
5
+ x = 0,
6
+ y = 0,
7
+ size = 1,
8
+ fill = 'none',
9
+ stroke = 'currentColor',
10
+ thickness = 1,
11
+ name = 'circle',
12
+ onclick,
13
+ onmouseover,
14
+ onmouseleave,
15
+ onfocus,
16
+ onblur
17
+ } = $props()
10
18
 
11
- export let name = 'circle'
12
-
13
- $: d = name in namedShapes ? namedShapes[name](size) : namedShapes['circle'](size)
19
+ let d = $derived(name in namedShapes ? namedShapes[name](size) : namedShapes['circle'](size))
14
20
  </script>
15
21
 
16
- <!-- svelte-ignore a11y-click-events-have-key-events -->
22
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
17
23
  <path
18
24
  {d}
19
25
  {fill}
20
26
  {stroke}
21
27
  transform="translate({x},{y})"
22
28
  stroke-width={thickness}
23
- fill-rule="evenodd"
29
+ fill-rule="nonzero"
24
30
  role="button"
25
- on:click
26
- on:mouseover
27
- on:mouseleave
28
- on:focus
29
- on:blur
31
+ {onclick}
32
+ {onmouseover}
33
+ {onmouseleave}
34
+ {onfocus}
35
+ {onblur}
30
36
  tabindex="0"
31
37
  />
@@ -1,4 +1,4 @@
1
1
  import { scaledPathCollection } from '../../lib/utils'
2
- import paths from './shapes.json'
2
+ import paths from './shapes.json' with { type: 'json' }
3
3
 
4
4
  export const namedShapes = scaledPathCollection(paths)
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2022 Jerry Thomas
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/src/lib/index.js DELETED
@@ -1,4 +0,0 @@
1
- export { swatch } from './swatch'
2
- export { swatchGrid } from './grid'
3
- export * from './chart'
4
- export * from './brewer'
package/src/lib/plots.js DELETED
@@ -1,27 +0,0 @@
1
- // skipcq: JS-C1003 - Importing all plots from observablehq/plot
2
- import * as Plot from '@observablehq/plot'
3
-
4
- export function plotter(node, options) {
5
- let { data, aes, type, opts } = options
6
- // let chart = Plot.lineY(options.data).plot({ grid: true })
7
-
8
- function render() {
9
- const chart = Plot[type](data, aes).plot({
10
- color: { scheme: 'turbo', range: [0.1, 0.9] },
11
- ...opts
12
- })
13
- node.firstChild?.remove() // remove old chart, if any
14
- node.append(chart)
15
- }
16
- render()
17
-
18
- return {
19
- update: (input) => {
20
- data = input.data
21
- aes = input.aes
22
- type = input.type
23
- opts = input.opts
24
- render()
25
- }
26
- }
27
- }