@rokkit/chart 1.0.0-next.15 → 1.0.0-next.151

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 (222) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +150 -46
  3. package/dist/Plot/index.d.ts +9 -0
  4. package/dist/PlotState.svelte.d.ts +49 -0
  5. package/dist/crossfilter/createCrossFilter.svelte.d.ts +13 -0
  6. package/dist/elements/index.d.ts +6 -0
  7. package/dist/geoms/lib/areas.d.ts +52 -0
  8. package/dist/geoms/lib/bars.d.ts +3 -0
  9. package/dist/index.d.ts +51 -0
  10. package/dist/lib/brewer.d.ts +9 -0
  11. package/dist/lib/brewing/BoxBrewer.svelte.d.ts +10 -0
  12. package/dist/lib/brewing/CartesianBrewer.svelte.d.ts +8 -0
  13. package/dist/lib/brewing/PieBrewer.svelte.d.ts +8 -0
  14. package/dist/lib/brewing/ViolinBrewer.svelte.d.ts +9 -0
  15. package/dist/lib/brewing/axes.svelte.d.ts +66 -0
  16. package/dist/lib/brewing/bars.svelte.d.ts +56 -0
  17. package/dist/lib/brewing/brewer.svelte.d.ts +114 -0
  18. package/dist/lib/brewing/colors.d.ts +17 -0
  19. package/dist/lib/brewing/dimensions.svelte.d.ts +35 -0
  20. package/dist/lib/brewing/index.svelte.d.ts +118 -0
  21. package/dist/lib/brewing/legends.svelte.d.ts +48 -0
  22. package/dist/lib/brewing/marks/arcs.d.ts +17 -0
  23. package/dist/lib/brewing/marks/areas.d.ts +31 -0
  24. package/dist/lib/brewing/marks/bars.d.ts +1 -0
  25. package/dist/lib/brewing/marks/boxes.d.ts +24 -0
  26. package/dist/lib/brewing/marks/lines.d.ts +24 -0
  27. package/dist/lib/brewing/marks/points.d.ts +40 -0
  28. package/dist/lib/brewing/marks/violins.d.ts +20 -0
  29. package/dist/lib/brewing/patterns.d.ts +14 -0
  30. package/dist/lib/brewing/scales.d.ts +28 -0
  31. package/dist/lib/brewing/scales.svelte.d.ts +24 -0
  32. package/dist/lib/brewing/stats.d.ts +23 -0
  33. package/dist/lib/brewing/symbols.d.ts +7 -0
  34. package/dist/lib/brewing/types.d.ts +162 -0
  35. package/dist/lib/chart.d.ts +38 -0
  36. package/dist/lib/context.d.ts +13 -0
  37. package/dist/lib/grid.d.ts +72 -0
  38. package/dist/lib/plot/chartProps.d.ts +177 -0
  39. package/dist/lib/plot/crossfilter.d.ts +13 -0
  40. package/dist/lib/plot/facet.d.ts +24 -0
  41. package/dist/lib/plot/frames.d.ts +47 -0
  42. package/dist/lib/plot/helpers.d.ts +3 -0
  43. package/dist/lib/plot/preset.d.ts +29 -0
  44. package/dist/lib/plot/scales.d.ts +5 -0
  45. package/dist/lib/plot/stat.d.ts +32 -0
  46. package/dist/lib/plot/types.d.ts +89 -0
  47. package/dist/lib/scales.svelte.d.ts +35 -0
  48. package/dist/lib/swatch.d.ts +12 -0
  49. package/dist/lib/ticks.d.ts +36 -0
  50. package/dist/lib/utils.d.ts +61 -0
  51. package/dist/lib/xscale.d.ts +11 -0
  52. package/dist/patterns/index.d.ts +4 -0
  53. package/dist/patterns/patterns.d.ts +72 -0
  54. package/dist/patterns/scale.d.ts +30 -0
  55. package/dist/symbols/constants/index.d.ts +1 -0
  56. package/dist/symbols/index.d.ts +5 -0
  57. package/package.json +41 -45
  58. package/src/AnimatedPlot.svelte +215 -0
  59. package/src/Chart.svelte +98 -0
  60. package/src/FacetPlot/Panel.svelte +23 -0
  61. package/src/FacetPlot.svelte +90 -0
  62. package/src/Plot/Arc.svelte +29 -0
  63. package/src/Plot/Area.svelte +25 -0
  64. package/src/Plot/Axis.svelte +73 -0
  65. package/src/Plot/Bar.svelte +96 -0
  66. package/src/Plot/Grid.svelte +30 -0
  67. package/src/Plot/Legend.svelte +167 -0
  68. package/src/Plot/Line.svelte +27 -0
  69. package/src/Plot/Point.svelte +27 -0
  70. package/src/Plot/Root.svelte +107 -0
  71. package/src/Plot/Timeline.svelte +95 -0
  72. package/src/Plot/Tooltip.svelte +81 -0
  73. package/src/Plot/index.js +9 -0
  74. package/src/Plot.svelte +181 -0
  75. package/src/PlotState.svelte.js +277 -0
  76. package/src/Sparkline.svelte +69 -0
  77. package/src/Symbol.svelte +21 -0
  78. package/src/Texture.svelte +18 -0
  79. package/src/charts/AreaChart.svelte +25 -0
  80. package/src/charts/BarChart.svelte +26 -0
  81. package/src/charts/BoxPlot.svelte +21 -0
  82. package/src/charts/BubbleChart.svelte +23 -0
  83. package/src/charts/LineChart.svelte +26 -0
  84. package/src/charts/PieChart.svelte +25 -0
  85. package/src/charts/ScatterPlot.svelte +25 -0
  86. package/src/charts/ViolinPlot.svelte +21 -0
  87. package/src/crossfilter/CrossFilter.svelte +38 -0
  88. package/src/crossfilter/FilterBar.svelte +32 -0
  89. package/src/crossfilter/FilterSlider.svelte +79 -0
  90. package/src/crossfilter/createCrossFilter.svelte.js +120 -0
  91. package/src/elements/Bar.svelte +22 -24
  92. package/src/elements/ColorRamp.svelte +20 -22
  93. package/src/elements/ContinuousLegend.svelte +20 -17
  94. package/src/elements/DefinePatterns.svelte +24 -0
  95. package/src/elements/DiscreteLegend.svelte +15 -15
  96. package/src/elements/Label.svelte +4 -8
  97. package/src/elements/SymbolGrid.svelte +22 -0
  98. package/src/elements/index.js +6 -0
  99. package/src/examples/BarChartExample.svelte +81 -0
  100. package/src/geoms/Arc.svelte +81 -0
  101. package/src/geoms/Area.svelte +50 -0
  102. package/src/geoms/Bar.svelte +142 -0
  103. package/src/geoms/Box.svelte +103 -0
  104. package/src/geoms/LabelPill.svelte +17 -0
  105. package/src/geoms/Line.svelte +99 -0
  106. package/src/geoms/Point.svelte +105 -0
  107. package/src/geoms/Violin.svelte +46 -0
  108. package/src/geoms/lib/areas.js +131 -0
  109. package/src/geoms/lib/bars.js +172 -0
  110. package/src/index.js +67 -16
  111. package/src/lib/brewer.js +25 -0
  112. package/src/lib/brewing/BoxBrewer.svelte.js +56 -0
  113. package/src/lib/brewing/CartesianBrewer.svelte.js +17 -0
  114. package/src/lib/brewing/PieBrewer.svelte.js +14 -0
  115. package/src/lib/brewing/ViolinBrewer.svelte.js +55 -0
  116. package/src/lib/brewing/axes.svelte.js +270 -0
  117. package/src/lib/brewing/bars.svelte.js +201 -0
  118. package/src/lib/brewing/brewer.svelte.js +230 -0
  119. package/src/lib/brewing/colors.js +22 -0
  120. package/src/lib/brewing/dimensions.svelte.js +56 -0
  121. package/src/lib/brewing/index.svelte.js +205 -0
  122. package/src/lib/brewing/legends.svelte.js +137 -0
  123. package/src/lib/brewing/marks/arcs.js +43 -0
  124. package/src/lib/brewing/marks/areas.js +59 -0
  125. package/src/lib/brewing/marks/bars.js +49 -0
  126. package/src/lib/brewing/marks/boxes.js +75 -0
  127. package/src/lib/brewing/marks/lines.js +48 -0
  128. package/src/lib/brewing/marks/points.js +57 -0
  129. package/src/lib/brewing/marks/violins.js +90 -0
  130. package/src/lib/brewing/patterns.js +31 -0
  131. package/src/lib/brewing/scales.js +51 -0
  132. package/src/lib/brewing/scales.svelte.js +82 -0
  133. package/src/lib/brewing/stats.js +66 -0
  134. package/src/lib/brewing/symbols.js +10 -0
  135. package/src/lib/brewing/types.js +73 -0
  136. package/src/lib/chart.js +220 -0
  137. package/src/lib/context.js +131 -0
  138. package/src/lib/grid.js +85 -0
  139. package/src/lib/plot/chartProps.js +76 -0
  140. package/src/lib/plot/crossfilter.js +16 -0
  141. package/src/lib/plot/facet.js +58 -0
  142. package/src/lib/plot/frames.js +80 -0
  143. package/src/lib/plot/helpers.js +14 -0
  144. package/src/lib/plot/preset.js +53 -0
  145. package/src/lib/plot/scales.js +56 -0
  146. package/src/lib/plot/stat.js +92 -0
  147. package/src/lib/plot/types.js +65 -0
  148. package/src/lib/scales.svelte.js +151 -0
  149. package/src/lib/swatch.js +13 -0
  150. package/src/lib/ticks.js +46 -0
  151. package/src/lib/utils.js +111 -118
  152. package/src/lib/xscale.js +31 -0
  153. package/src/patterns/DefinePatterns.svelte +32 -0
  154. package/src/patterns/PatternDef.svelte +27 -0
  155. package/src/patterns/index.js +4 -0
  156. package/src/patterns/patterns.js +208 -0
  157. package/src/patterns/scale.js +87 -0
  158. package/src/spec/chart-spec.js +29 -0
  159. package/src/symbols/RoundedSquare.svelte +33 -0
  160. package/src/symbols/Shape.svelte +37 -0
  161. package/src/symbols/constants/index.js +4 -0
  162. package/src/symbols/index.js +9 -0
  163. package/src/symbols/outline.svelte +60 -0
  164. package/src/symbols/solid.svelte +60 -0
  165. package/src/chart/FacetGrid.svelte +0 -51
  166. package/src/chart/Grid.svelte +0 -34
  167. package/src/chart/Legend.svelte +0 -16
  168. package/src/chart/PatternDefs.svelte +0 -13
  169. package/src/chart/Swatch.svelte +0 -93
  170. package/src/chart/SwatchButton.svelte +0 -29
  171. package/src/chart/SwatchGrid.svelte +0 -55
  172. package/src/chart/Symbol.svelte +0 -37
  173. package/src/chart/Texture.svelte +0 -16
  174. package/src/chart/TexturedShape.svelte +0 -27
  175. package/src/chart/TimelapseChart.svelte +0 -97
  176. package/src/chart/Timer.svelte +0 -27
  177. package/src/chart.js +0 -9
  178. package/src/components/charts/Axis.svelte +0 -66
  179. package/src/components/charts/Chart.svelte +0 -35
  180. package/src/components/index.js +0 -23
  181. package/src/components/lib/axis.js +0 -0
  182. package/src/components/lib/chart.js +0 -187
  183. package/src/components/lib/color.js +0 -327
  184. package/src/components/lib/funnel.js +0 -204
  185. package/src/components/lib/index.js +0 -19
  186. package/src/components/lib/pattern.js +0 -190
  187. package/src/components/lib/rollup.js +0 -55
  188. package/src/components/lib/shape.js +0 -199
  189. package/src/components/lib/summary.js +0 -145
  190. package/src/components/lib/theme.js +0 -23
  191. package/src/components/lib/timer.js +0 -41
  192. package/src/components/lib/utils.js +0 -165
  193. package/src/components/plots/BarPlot.svelte +0 -36
  194. package/src/components/plots/BoxPlot.svelte +0 -54
  195. package/src/components/plots/ScatterPlot.svelte +0 -30
  196. package/src/components/store.js +0 -70
  197. package/src/constants.js +0 -66
  198. package/src/elements/PatternDefs.svelte +0 -13
  199. package/src/elements/PatternMask.svelte +0 -20
  200. package/src/elements/Symbol.svelte +0 -38
  201. package/src/elements/Tooltip.svelte +0 -23
  202. package/src/funnel.svelte +0 -35
  203. package/src/geom.js +0 -105
  204. package/src/lib/axis.js +0 -75
  205. package/src/lib/colors.js +0 -32
  206. package/src/lib/geom.js +0 -4
  207. package/src/lib/shapes.js +0 -144
  208. package/src/lib/timer.js +0 -44
  209. package/src/lookup.js +0 -29
  210. package/src/plots/BarPlot.svelte +0 -55
  211. package/src/plots/BoxPlot.svelte +0 -0
  212. package/src/plots/FunnelPlot.svelte +0 -33
  213. package/src/plots/HeatMap.svelte +0 -5
  214. package/src/plots/HeatMapCalendar.svelte +0 -129
  215. package/src/plots/LinePlot.svelte +0 -55
  216. package/src/plots/Plot.svelte +0 -25
  217. package/src/plots/RankBarPlot.svelte +0 -38
  218. package/src/plots/ScatterPlot.svelte +0 -20
  219. package/src/plots/ViolinPlot.svelte +0 -11
  220. package/src/plots/heatmap.js +0 -70
  221. package/src/plots/index.js +0 -10
  222. package/src/swatch.js +0 -11
@@ -0,0 +1,98 @@
1
+ <script>
2
+ import { setContext } from 'svelte'
3
+ import { ChartBrewer } from './lib/brewing/brewer.svelte.js'
4
+
5
+ /**
6
+ * @type {{
7
+ * spec?: import('./spec/chart-spec.js').ChartSpec,
8
+ * data?: Object[],
9
+ * x?: string,
10
+ * y?: string,
11
+ * color?: string,
12
+ * pattern?: string,
13
+ * fill?: string,
14
+ * size?: string,
15
+ * label?: string,
16
+ * symbol?: string,
17
+ * width?: number,
18
+ * height?: number,
19
+ * mode?: 'light' | 'dark',
20
+ * children?: import('svelte').Snippet
21
+ * }}
22
+ */
23
+ let {
24
+ spec = undefined,
25
+ data = [],
26
+ x = undefined,
27
+ y = undefined,
28
+ color = undefined,
29
+ pattern = undefined,
30
+ fill = undefined,
31
+ size = undefined,
32
+ label = undefined,
33
+ symbol = undefined,
34
+ width = 600,
35
+ height = 400,
36
+ mode = 'light',
37
+ children
38
+ } = $props()
39
+
40
+ const brewer = new ChartBrewer()
41
+ setContext('chart-brewer', brewer)
42
+
43
+ function buildChannels() {
44
+ const channels = {}
45
+ if (x) channels.x = x
46
+ if (y) channels.y = y
47
+ if (color) channels.color = color
48
+ if (pattern) channels.pattern = pattern
49
+ if (fill) channels.fill = fill
50
+ if (size) channels.size = size
51
+ if (label) channels.label = label
52
+ if (symbol) channels.symbol = symbol
53
+ return channels
54
+ }
55
+
56
+ $effect(() => {
57
+ if (spec) {
58
+ brewer.update({ data: spec.data, channels: spec.channels, width, height, mode, layers: spec.layers })
59
+ } else {
60
+ brewer.update({ data, channels: buildChannels(), width, height, mode })
61
+ }
62
+ })
63
+
64
+ </script>
65
+
66
+ <div class="chart-container" data-chart-root>
67
+ <svg
68
+ {width}
69
+ {height}
70
+ viewBox="0 0 {width} {height}"
71
+ role="img"
72
+ aria-label="Chart visualization"
73
+ >
74
+ <g
75
+ class="chart-area"
76
+ data-chart-canvas
77
+ >
78
+ {@render children?.()}
79
+ </g>
80
+ </svg>
81
+ </div>
82
+
83
+ <style>
84
+ .chart-container {
85
+ position: relative;
86
+ width: 100%;
87
+ height: auto;
88
+ }
89
+
90
+ svg {
91
+ display: block;
92
+ overflow: visible;
93
+ }
94
+
95
+ .chart-area {
96
+ pointer-events: all;
97
+ }
98
+ </style>
@@ -0,0 +1,23 @@
1
+ <script>
2
+ import PlotChart from '../Plot.svelte'
3
+
4
+ let {
5
+ data, x, y, color, geoms = [], helpers = {},
6
+ width, height, mode, grid, legend,
7
+ xDomain, yDomain, colorDomain,
8
+ children
9
+ } = $props()
10
+
11
+ // Build spec with domain overrides so PlotState uses them
12
+ const spec = $derived({
13
+ data, x, y, color,
14
+ geoms,
15
+ xDomain,
16
+ yDomain,
17
+ colorDomain
18
+ })
19
+ </script>
20
+
21
+ <PlotChart {spec} {helpers} {width} {height} {mode} {grid} {legend}>
22
+ {@render children?.()}
23
+ </PlotChart>
@@ -0,0 +1,90 @@
1
+ <script>
2
+ import { splitByField, getFacetDomains } from './lib/plot/facet.js'
3
+ import { distinct } from './lib/brewing/colors.js'
4
+ import PlotPanel from './FacetPlot/Panel.svelte'
5
+
6
+ /**
7
+ * @type {{
8
+ * data: Object[],
9
+ * facet: { by: string, cols?: number, scales?: 'fixed'|'free'|'free_x'|'free_y' },
10
+ * x?: string,
11
+ * y?: string,
12
+ * color?: string,
13
+ * geoms?: import('./lib/plot/types.js').GeomSpec[],
14
+ * helpers?: import('./lib/plot/types.js').PlotHelpers,
15
+ * panelWidth?: number,
16
+ * panelHeight?: number,
17
+ * width?: number,
18
+ * height?: number,
19
+ * mode?: 'light' | 'dark',
20
+ * grid?: boolean,
21
+ * legend?: boolean,
22
+ * children?: import('svelte').Snippet
23
+ * }}
24
+ */
25
+ let {
26
+ data = [],
27
+ facet,
28
+ x,
29
+ y,
30
+ fill,
31
+ color,
32
+ geoms = [],
33
+ helpers = {},
34
+ panelWidth,
35
+ panelHeight,
36
+ width = 900,
37
+ height = 300,
38
+ mode = 'light',
39
+ grid = true,
40
+ legend = false,
41
+ children
42
+ } = $props()
43
+
44
+ // `fill` is accepted as an alias for `color` (bar/area semantics vs line/point)
45
+ const colorChannel = $derived(fill ?? color)
46
+
47
+ const panels = $derived(splitByField(data, facet.by))
48
+ const scales = $derived(facet.scales ?? 'fixed')
49
+ const domains = $derived(
50
+ x && y ? getFacetDomains(panels, { x, y }, scales) : new Map()
51
+ )
52
+
53
+ // Global color domain ensures the same value maps to the same color in every panel.
54
+ const colorDomain = $derived(colorChannel ? distinct(data, colorChannel) : undefined)
55
+
56
+ const cols = $derived(facet.cols ?? Math.min(panels.size, 3))
57
+ const pw = $derived(panelWidth ?? Math.floor(width / cols))
58
+ const ph = $derived(panelHeight ?? height)
59
+ </script>
60
+
61
+ <div data-facet-grid style:--facet-cols={cols}>
62
+ {#each [...panels.entries()] as [facetValue, panelData] (`${facetValue}`)}
63
+ <div data-facet-panel data-facet-value={facetValue}>
64
+ <div data-facet-title>{facetValue}</div>
65
+ <PlotPanel
66
+ data={panelData}
67
+ {x} {y} color={colorChannel}
68
+ {geoms} {helpers}
69
+ width={pw}
70
+ height={ph}
71
+ {mode} {grid}
72
+ legend={false}
73
+ xDomain={domains.get(facetValue)?.xDomain}
74
+ yDomain={domains.get(facetValue)?.yDomain}
75
+ {colorDomain}
76
+ >
77
+ <!-- Render caller-supplied geoms inside every panel (each gets its own PlotState context) -->
78
+ {@render children?.()}
79
+ </PlotPanel>
80
+ </div>
81
+ {/each}
82
+ </div>
83
+
84
+ <!-- Single shared legend outside the grid -->
85
+ {#if legend}
86
+ <div data-facet-legend>
87
+ <!-- Legend content rendered by first panel; simplified for now -->
88
+ </div>
89
+ {/if}
90
+
@@ -0,0 +1,29 @@
1
+ <script>
2
+ import { getContext } from 'svelte'
3
+
4
+ const brewer = getContext('chart-brewer')
5
+ </script>
6
+
7
+ {#if brewer && brewer.arcs && brewer.arcs.length > 0}
8
+ <g
9
+ class="chart-arcs"
10
+ data-plot-type="arc"
11
+ transform="translate({brewer.width / 2}, {brewer.height / 2})"
12
+ >
13
+ {#each brewer.arcs as arc (arc.key)}
14
+ <path
15
+ d={arc.d}
16
+ fill={arc.fill}
17
+ stroke={arc.stroke}
18
+ stroke-width="1"
19
+ data-plot-element="arc"
20
+ />
21
+ {/each}
22
+ </g>
23
+ {/if}
24
+
25
+ <style>
26
+ .chart-arcs {
27
+ pointer-events: none;
28
+ }
29
+ </style>
@@ -0,0 +1,25 @@
1
+ <script>
2
+ import { getContext } from 'svelte'
3
+
4
+ const brewer = getContext('chart-brewer')
5
+ </script>
6
+
7
+ {#if brewer && brewer.areas && brewer.areas.length > 0}
8
+ <g class="chart-areas" data-plot-type="area">
9
+ {#each brewer.areas as seg (seg.key ?? seg.d)}
10
+ <path
11
+ d={seg.d}
12
+ fill={seg.fill}
13
+ stroke={seg.stroke}
14
+ opacity="0.7"
15
+ data-plot-element="area"
16
+ />
17
+ {/each}
18
+ </g>
19
+ {/if}
20
+
21
+ <style>
22
+ .chart-areas {
23
+ pointer-events: none;
24
+ }
25
+ </style>
@@ -0,0 +1,73 @@
1
+ <script>
2
+ import { getContext } from 'svelte'
3
+
4
+ /** @type {'x' | 'y'} */
5
+ let { type = 'x', label = '' } = $props()
6
+
7
+ const state = getContext('plot-state')
8
+
9
+ const xTicks = $derived.by(() => {
10
+ const s = state.xScale
11
+ if (!s) return []
12
+ if (typeof s.bandwidth === 'function') {
13
+ return s.domain().map((val) => ({
14
+ value: val,
15
+ pos: (s(val) ?? 0) + s.bandwidth() / 2
16
+ }))
17
+ }
18
+ return s.ticks(6).map((val) => ({ value: val, pos: s(val) }))
19
+ })
20
+
21
+ const yTicks = $derived.by(() => {
22
+ const s = state.yScale
23
+ if (!s) return []
24
+ if (typeof s.bandwidth === 'function') {
25
+ return s.domain().map((val) => ({
26
+ value: val,
27
+ pos: (s(val) ?? 0) + s.bandwidth() / 2
28
+ }))
29
+ }
30
+ return s.ticks(6).map((val) => ({ value: val, pos: s(val) }))
31
+ })
32
+
33
+ const xTransform = $derived(`translate(0, ${state.xAxisY ?? state.innerHeight})`)
34
+ const yTransform = $derived(`translate(${state.yAxisX ?? 0}, 0)`)
35
+ </script>
36
+
37
+ {#if type === 'x'}
38
+ <g class="axis x-axis" transform={xTransform} data-plot-axis="x">
39
+ <line x1="0" y1="0" x2={state.innerWidth} y2="0" data-plot-axis-line />
40
+ {#each xTicks as tick (tick.value)}
41
+ <g transform="translate({tick.pos}, 0)" data-plot-tick>
42
+ <line x1="0" y1="0" x2="0" y2="6" stroke="currentColor" />
43
+ <text x="0" y="9" text-anchor="middle" dominant-baseline="hanging" data-plot-tick-label>
44
+ {tick.value}
45
+ </text>
46
+ </g>
47
+ {/each}
48
+ {#if label}
49
+ <text x={state.innerWidth / 2} y="36" text-anchor="middle" class="axis-label" data-plot-axis-label>{label}</text>
50
+ {/if}
51
+ </g>
52
+ {:else}
53
+ <g class="axis y-axis" transform={yTransform} data-plot-axis="y">
54
+ <line x1="0" y1="0" x2="0" y2={state.innerHeight} data-plot-axis-line />
55
+ {#each yTicks as tick (tick.value)}
56
+ <g transform="translate(0, {tick.pos})" data-plot-tick>
57
+ <line x1="-6" y1="0" x2="0" y2="0" stroke="currentColor" />
58
+ <text x="-9" y="0" text-anchor="end" dominant-baseline="middle" data-plot-tick-label>
59
+ {tick.value}
60
+ </text>
61
+ </g>
62
+ {/each}
63
+ {#if label}
64
+ <text transform="rotate(-90)" x={-(state.innerHeight / 2)} y="-40" text-anchor="middle" class="axis-label" data-plot-axis-label>{label}</text>
65
+ {/if}
66
+ </g>
67
+ {/if}
68
+
69
+ <style>
70
+ .axis { font-size: 11px; fill: currentColor; stroke: currentColor; }
71
+ .axis-label { font-size: 13px; font-weight: 500; }
72
+ [data-plot-axis-line] { stroke: currentColor; }
73
+ </style>
@@ -0,0 +1,96 @@
1
+ <script>
2
+ import { getContext } from 'svelte'
3
+ import { ChartBrewer } from '../lib/brewing/index.svelte.js'
4
+
5
+ let {
6
+ x = null,
7
+ y = null,
8
+ fill = null,
9
+ color = '#4682b4',
10
+ opacity = 1,
11
+ animationDuration = 300,
12
+ onClick = null
13
+ } = $props()
14
+
15
+ // Get brewer from context
16
+ const brewer = getContext('chart-brewer')
17
+
18
+ // Set field mappings in the brewer
19
+ $effect(() => {
20
+ brewer.setFields({
21
+ x,
22
+ y,
23
+ color: fill
24
+ })
25
+
26
+ // Ensure scales are updated
27
+ brewer.createScales()
28
+ })
29
+
30
+ // Compute bars whenever data or fields change
31
+ let bars = $derived(brewer.createBars())
32
+
33
+ // Animation transition values
34
+ let initialY = $state(0)
35
+ let initialHeight = $state(0)
36
+
37
+ // Handle resetting animation state for new bars
38
+ $effect(() => {
39
+ if (bars && bars.length > 0) {
40
+ initialY = brewer.getDimensions().innerHeight
41
+ initialHeight = 0
42
+
43
+ // Reset to actual positions after a delay
44
+ setTimeout(() => {
45
+ initialY = 0
46
+ initialHeight = 0
47
+ }, 10)
48
+ }
49
+ })
50
+
51
+ // Handle bar click
52
+ function handleClick(event, bar) {
53
+ if (onClick) onClick(bar.data, event)
54
+ }
55
+ </script>
56
+
57
+ {#if bars && bars.length > 0}
58
+ <g class="chart-bars" data-plot-type="bar">
59
+ {#each bars as bar, i (bar.data[x])}
60
+ {@const barY = initialY > 0 ? brewer.getDimensions().innerHeight : bar.y}
61
+ {@const barHeight = initialHeight > 0 ? 0 : bar.height}
62
+
63
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions, a11y_click_events_have_key_events -->
64
+ <rect
65
+ class="bar"
66
+ x={bar.x}
67
+ y={barY}
68
+ width={bar.width}
69
+ height={barHeight}
70
+ fill={bar.color}
71
+ {opacity}
72
+ onclick={(event) => handleClick(event, bar)}
73
+ onmouseenter={(event) => {
74
+ event.target.setAttribute('opacity', Math.min(opacity + 0.2, 1))
75
+ }}
76
+ onmouseleave={(event) => {
77
+ event.target.setAttribute('opacity', opacity)
78
+ }}
79
+ style="transition: y {animationDuration}ms ease, height {animationDuration}ms ease;"
80
+ role="graphics-symbol"
81
+ aria-label="Bar representing {bar.data[x]} with value {bar.data[y]}"
82
+ data-plot-element="bar"
83
+ data-plot-value={bar.data[y]}
84
+ data-plot-category={bar.data[x]}
85
+ >
86
+ <title>{bar.data[x]}: {bar.data[y]}</title>
87
+ </rect>
88
+ {/each}
89
+ </g>
90
+ {/if}
91
+
92
+ <style>
93
+ .chart-bars .bar {
94
+ cursor: pointer;
95
+ }
96
+ </style>
@@ -0,0 +1,30 @@
1
+ <script>
2
+ import { getContext } from 'svelte'
3
+
4
+ const state = getContext('plot-state')
5
+
6
+ const xGridLines = $derived.by(() => {
7
+ const s = state.xScale
8
+ if (!s || typeof s.bandwidth !== 'function') return []
9
+ return s.domain().map((val) => ({ pos: (s(val) ?? 0) + s.bandwidth() / 2 }))
10
+ })
11
+
12
+ const yGridLines = $derived.by(() => {
13
+ const s = state.yScale
14
+ if (!s || typeof s.ticks !== 'function') return []
15
+ return s.ticks(6).map((val) => ({ pos: s(val) }))
16
+ })
17
+ </script>
18
+
19
+ <g class="grid" data-plot-grid>
20
+ {#each yGridLines as line (line.pos)}
21
+ <line x1="0" y1={line.pos} x2={state.innerWidth} y2={line.pos} data-plot-grid-line />
22
+ {/each}
23
+ {#each xGridLines as line (line.pos)}
24
+ <line x1={line.pos} y1="0" x2={line.pos} y2={state.innerHeight} data-plot-grid-line="x" />
25
+ {/each}
26
+ </g>
27
+
28
+ <style>
29
+ [data-plot-grid-line] { stroke: var(--chart-grid-color, currentColor); opacity: 0.15; stroke-dasharray: 2 4; }
30
+ </style>
@@ -0,0 +1,167 @@
1
+ <script>
2
+ import { getContext } from 'svelte'
3
+ import { toPatternId } from '../lib/brewing/patterns.js'
4
+ import { buildSymbolPath } from '../lib/brewing/marks/points.js'
5
+
6
+ /** @type {Record<string, string>} */
7
+ let { labels = {} } = $props()
8
+
9
+ const state = getContext('plot-state')
10
+
11
+ const isCategorical = $derived(state.colorScaleType === 'categorical')
12
+ const isLineGeom = $derived(state.geomTypes?.has('line') ?? false)
13
+ const isPointGeom = $derived(state.geomTypes?.has('point') ?? false)
14
+ const hasSymbols = $derived((state.symbols?.size ?? 0) > 0)
15
+
16
+ // Split conditions
17
+ const splitPattern = $derived(
18
+ !!state.colorField && !!state.patternField && state.colorField !== state.patternField
19
+ )
20
+ const splitSymbol = $derived(
21
+ hasSymbols && !!state.colorField && !!state.symbolField && state.colorField !== state.symbolField
22
+ )
23
+ const symbolOnly = $derived(hasSymbols && !state.colorField)
24
+
25
+ // Color section items — combined with same-field pattern/symbol overlays
26
+ const colorItems = $derived(
27
+ [...(state.colors?.entries() ?? [])].map(([key, entry]) => ({
28
+ key,
29
+ label: labels[String(key)] ?? String(key),
30
+ fill: entry.fill,
31
+ stroke: entry.stroke,
32
+ patternId: !splitPattern && state.patterns?.has(key) ? toPatternId(String(key)) : null,
33
+ symbolShape: !splitSymbol && state.symbols?.get(key) ? state.symbols.get(key) : null
34
+ }))
35
+ )
36
+
37
+ // Pattern section — only when pattern encodes a different field than color
38
+ const patternItems = $derived(
39
+ splitPattern
40
+ ? [...(state.patterns?.entries() ?? [])].map(([key]) => ({
41
+ key,
42
+ label: labels[String(key)] ?? String(key),
43
+ patternId: toPatternId(String(key))
44
+ }))
45
+ : []
46
+ )
47
+
48
+ // Symbol section — only when symbol encodes a different field than color, or symbol-only
49
+ const symbolItems = $derived(
50
+ splitSymbol || symbolOnly
51
+ ? [...(state.symbols?.entries() ?? [])].map(([key, shape]) => ({
52
+ key,
53
+ label: labels[String(key)] ?? String(key),
54
+ shape,
55
+ fill: state.colors?.get(key)?.fill ?? '#888',
56
+ stroke: state.colors?.get(key)?.stroke ?? '#888'
57
+ }))
58
+ : []
59
+ )
60
+
61
+ const gradientStyle = $derived.by(() => {
62
+ if (isCategorical) return ''
63
+ return `background: linear-gradient(to right, #cfe2f3, #084594)`
64
+ })
65
+ </script>
66
+
67
+ {#if isCategorical}
68
+ <div class="legend-root" data-plot-legend>
69
+
70
+ <!-- Symbol-only: no color field, just symbol shapes -->
71
+ {#if symbolOnly}
72
+ <div class="legend categorical">
73
+ {#each symbolItems as item (item.key)}
74
+ <div class="legend-item" data-plot-legend-item>
75
+ <svg width="14" height="14" data-plot-legend-swatch>
76
+ <path transform="translate(7,7)" d={buildSymbolPath(item.shape, 5)} fill={item.fill} />
77
+ </svg>
78
+ <span class="label" data-plot-legend-label>{item.label}</span>
79
+ </div>
80
+ {/each}
81
+ </div>
82
+
83
+ <!-- Color section (line, point, or fill swatches) -->
84
+ {:else if colorItems.length > 0}
85
+ <div class="legend categorical">
86
+ {#each colorItems as item (item.key)}
87
+ <div class="legend-item" data-plot-legend-item>
88
+ {#if isLineGeom}
89
+ <!-- Line swatch with optional combined symbol -->
90
+ <svg width="24" height="14" data-plot-legend-swatch>
91
+ <line x1="2" y1="7" x2="22" y2="7" stroke={item.stroke} stroke-width="2" stroke-linecap="round" />
92
+ {#if item.symbolShape}
93
+ <path transform="translate(12,7)" d={buildSymbolPath(item.symbolShape, 4)} fill={item.stroke} />
94
+ {/if}
95
+ </svg>
96
+ {:else if isPointGeom && item.symbolShape}
97
+ <!-- Symbol shape swatch for scatter -->
98
+ <svg width="14" height="14" data-plot-legend-swatch>
99
+ <path transform="translate(7,7)" d={buildSymbolPath(item.symbolShape, 5)} fill={item.fill} />
100
+ </svg>
101
+ {:else if item.patternId}
102
+ <!-- Fill + pattern overlay -->
103
+ <svg width="14" height="14" data-plot-legend-swatch>
104
+ <rect width="14" height="14" fill={item.fill} />
105
+ <rect width="14" height="14" fill="url(#{item.patternId})" />
106
+ </svg>
107
+ {:else}
108
+ <!-- Plain fill swatch -->
109
+ <span class="swatch" style:background-color={item.fill} data-plot-legend-swatch></span>
110
+ {/if}
111
+ <span class="label" data-plot-legend-label>{item.label}</span>
112
+ </div>
113
+ {/each}
114
+ </div>
115
+ {/if}
116
+
117
+ <!-- Pattern section (different field from color) -->
118
+ {#if patternItems.length > 0}
119
+ <div class="legend categorical legend-section">
120
+ {#each patternItems as item (item.key)}
121
+ <div class="legend-item" data-plot-legend-item>
122
+ <svg width="14" height="14" data-plot-legend-swatch>
123
+ <rect width="14" height="14" fill="var(--color-surface-z2, #ccc)" />
124
+ <rect width="14" height="14" fill="url(#{item.patternId})" />
125
+ </svg>
126
+ <span class="label" data-plot-legend-label>{item.label}</span>
127
+ </div>
128
+ {/each}
129
+ </div>
130
+ {/if}
131
+
132
+ <!-- Symbol section (different field from color) -->
133
+ {#if symbolItems.length > 0 && !symbolOnly}
134
+ <div class="legend categorical legend-section">
135
+ {#each symbolItems as item (item.key)}
136
+ <div class="legend-item" data-plot-legend-item>
137
+ {#if isLineGeom}
138
+ <svg width="24" height="14" data-plot-legend-swatch>
139
+ <line x1="2" y1="7" x2="22" y2="7" stroke={item.stroke ?? item.fill} stroke-width="2" stroke-linecap="round" stroke-dasharray="4 2" />
140
+ <path transform="translate(12,7)" d={buildSymbolPath(item.shape, 4)} fill={item.stroke ?? item.fill} />
141
+ </svg>
142
+ {:else}
143
+ <svg width="14" height="14" data-plot-legend-swatch>
144
+ <path transform="translate(7,7)" d={buildSymbolPath(item.shape, 5)} fill={item.fill} />
145
+ </svg>
146
+ {/if}
147
+ <span class="label" data-plot-legend-label>{item.label}</span>
148
+ </div>
149
+ {/each}
150
+ </div>
151
+ {/if}
152
+
153
+ </div>
154
+ {:else}
155
+ <div class="legend gradient" data-plot-legend>
156
+ <div class="gradient-bar" style={gradientStyle} data-plot-legend-gradient></div>
157
+ </div>
158
+ {/if}
159
+
160
+ <style>
161
+ .legend-root { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
162
+ .legend { display: flex; flex-wrap: wrap; gap: 8px; font-size: 12px; }
163
+ .legend-section { border-top: 1px solid var(--color-surface-z3, #e0e0e0); padding-top: 6px; }
164
+ .legend-item { display: flex; align-items: center; gap: 4px; }
165
+ .swatch { display: inline-block; width: 14px; height: 14px; border-radius: 2px; flex-shrink: 0; }
166
+ .gradient-bar { width: 180px; height: 14px; border-radius: 2px; }
167
+ </style>
@@ -0,0 +1,27 @@
1
+ <script>
2
+ import { getContext } from 'svelte'
3
+
4
+ const brewer = getContext('chart-brewer')
5
+ </script>
6
+
7
+ {#if brewer && brewer.lines && brewer.lines.length > 0}
8
+ <g class="chart-lines" data-plot-type="line">
9
+ {#each brewer.lines as seg (seg.key ?? seg.d)}
10
+ <path
11
+ d={seg.d}
12
+ fill={seg.fill}
13
+ stroke={seg.stroke}
14
+ stroke-width="2"
15
+ stroke-linejoin="round"
16
+ stroke-linecap="round"
17
+ data-plot-element="line"
18
+ />
19
+ {/each}
20
+ </g>
21
+ {/if}
22
+
23
+ <style>
24
+ .chart-lines {
25
+ pointer-events: none;
26
+ }
27
+ </style>
@@ -0,0 +1,27 @@
1
+ <script>
2
+ import { getContext } from 'svelte'
3
+
4
+ const brewer = getContext('chart-brewer')
5
+ </script>
6
+
7
+ {#if brewer && brewer.points && brewer.points.length > 0}
8
+ <g class="chart-points" data-plot-type="point">
9
+ {#each brewer.points as pt, i (i)}
10
+ <circle
11
+ cx={pt.cx}
12
+ cy={pt.cy}
13
+ r={pt.r}
14
+ fill={pt.fill}
15
+ stroke={pt.stroke}
16
+ stroke-width="1"
17
+ data-plot-element="point"
18
+ />
19
+ {/each}
20
+ </g>
21
+ {/if}
22
+
23
+ <style>
24
+ .chart-points {
25
+ pointer-events: none;
26
+ }
27
+ </style>