@rokkit/chart 1.0.0-next.146 → 1.0.0-next.148

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 (157) hide show
  1. package/dist/Plot/index.d.ts +4 -0
  2. package/dist/PlotState.svelte.d.ts +47 -0
  3. package/dist/crossfilter/createCrossFilter.svelte.d.ts +15 -0
  4. package/dist/geoms/lib/areas.d.ts +52 -0
  5. package/dist/geoms/lib/bars.d.ts +3 -0
  6. package/dist/index.d.ts +38 -1
  7. package/dist/lib/brewing/BoxBrewer.svelte.d.ts +10 -0
  8. package/dist/lib/brewing/CartesianBrewer.svelte.d.ts +8 -0
  9. package/dist/lib/brewing/PieBrewer.svelte.d.ts +8 -0
  10. package/dist/lib/brewing/ViolinBrewer.svelte.d.ts +9 -0
  11. package/dist/lib/brewing/brewer.svelte.d.ts +145 -0
  12. package/dist/lib/brewing/colors.d.ts +17 -0
  13. package/dist/lib/brewing/marks/arcs.d.ts +17 -0
  14. package/dist/lib/brewing/marks/areas.d.ts +31 -0
  15. package/dist/lib/brewing/marks/bars.d.ts +1 -0
  16. package/dist/lib/brewing/marks/boxes.d.ts +24 -0
  17. package/dist/lib/brewing/marks/lines.d.ts +24 -0
  18. package/dist/lib/brewing/marks/points.d.ts +40 -0
  19. package/dist/lib/brewing/marks/violins.d.ts +20 -0
  20. package/dist/lib/brewing/patterns.d.ts +14 -0
  21. package/dist/lib/brewing/scales.d.ts +28 -0
  22. package/dist/lib/brewing/stats.d.ts +31 -0
  23. package/dist/lib/brewing/symbols.d.ts +7 -0
  24. package/dist/lib/plot/chartProps.d.ts +177 -0
  25. package/dist/lib/plot/crossfilter.d.ts +13 -0
  26. package/dist/lib/plot/facet.d.ts +24 -0
  27. package/dist/lib/plot/frames.d.ts +45 -0
  28. package/dist/lib/plot/helpers.d.ts +3 -0
  29. package/dist/lib/plot/preset.d.ts +29 -0
  30. package/dist/lib/plot/scales.d.ts +5 -0
  31. package/dist/lib/plot/stat.d.ts +32 -0
  32. package/dist/lib/plot/types.d.ts +89 -0
  33. package/dist/lib/scales.svelte.d.ts +1 -1
  34. package/dist/lib/swatch.d.ts +12 -0
  35. package/dist/lib/utils.d.ts +1 -0
  36. package/dist/lib/xscale.d.ts +11 -0
  37. package/dist/patterns/index.d.ts +4 -9
  38. package/dist/patterns/patterns.d.ts +72 -0
  39. package/dist/patterns/scale.d.ts +30 -0
  40. package/package.json +9 -3
  41. package/src/AnimatedPlot.svelte +194 -0
  42. package/src/Chart.svelte +101 -0
  43. package/src/FacetPlot/Panel.svelte +23 -0
  44. package/src/FacetPlot.svelte +90 -0
  45. package/src/Plot/Arc.svelte +29 -0
  46. package/src/Plot/Area.svelte +25 -0
  47. package/src/Plot/Axis.svelte +62 -84
  48. package/src/Plot/Grid.svelte +20 -58
  49. package/src/Plot/Legend.svelte +160 -120
  50. package/src/Plot/Line.svelte +27 -0
  51. package/src/Plot/Point.svelte +27 -0
  52. package/src/Plot/Timeline.svelte +95 -0
  53. package/src/Plot/Tooltip.svelte +81 -0
  54. package/src/Plot/index.js +4 -0
  55. package/src/Plot.svelte +189 -0
  56. package/src/PlotState.svelte.js +278 -0
  57. package/src/Sparkline.svelte +69 -0
  58. package/src/charts/AreaChart.svelte +25 -0
  59. package/src/charts/BarChart.svelte +26 -0
  60. package/src/charts/BoxPlot.svelte +21 -0
  61. package/src/charts/BubbleChart.svelte +23 -0
  62. package/src/charts/LineChart.svelte +26 -0
  63. package/src/charts/PieChart.svelte +25 -0
  64. package/src/charts/ScatterPlot.svelte +25 -0
  65. package/src/charts/ViolinPlot.svelte +21 -0
  66. package/src/crossfilter/CrossFilter.svelte +38 -0
  67. package/src/crossfilter/FilterBar.svelte +32 -0
  68. package/src/crossfilter/FilterSlider.svelte +79 -0
  69. package/src/crossfilter/createCrossFilter.svelte.js +113 -0
  70. package/src/elements/SymbolGrid.svelte +6 -7
  71. package/src/geoms/Arc.svelte +81 -0
  72. package/src/geoms/Area.svelte +50 -0
  73. package/src/geoms/Bar.svelte +142 -0
  74. package/src/geoms/Box.svelte +101 -0
  75. package/src/geoms/LabelPill.svelte +17 -0
  76. package/src/geoms/Line.svelte +100 -0
  77. package/src/geoms/Point.svelte +100 -0
  78. package/src/geoms/Violin.svelte +44 -0
  79. package/src/geoms/lib/areas.js +131 -0
  80. package/src/geoms/lib/bars.js +172 -0
  81. package/src/index.js +52 -3
  82. package/src/lib/brewing/BoxBrewer.svelte.js +56 -0
  83. package/src/lib/brewing/CartesianBrewer.svelte.js +16 -0
  84. package/src/lib/brewing/PieBrewer.svelte.js +14 -0
  85. package/src/lib/brewing/ViolinBrewer.svelte.js +55 -0
  86. package/src/lib/brewing/brewer.svelte.js +229 -0
  87. package/src/lib/brewing/colors.js +22 -0
  88. package/src/lib/brewing/marks/arcs.js +43 -0
  89. package/src/lib/brewing/marks/areas.js +59 -0
  90. package/src/lib/brewing/marks/bars.js +49 -0
  91. package/src/lib/brewing/marks/boxes.js +75 -0
  92. package/src/lib/brewing/marks/lines.js +48 -0
  93. package/src/lib/brewing/marks/points.js +57 -0
  94. package/src/lib/brewing/marks/violins.js +90 -0
  95. package/src/lib/brewing/patterns.js +31 -0
  96. package/src/lib/brewing/scales.js +51 -0
  97. package/src/lib/brewing/scales.svelte.js +2 -26
  98. package/src/lib/brewing/stats.js +62 -0
  99. package/src/lib/brewing/symbols.js +10 -0
  100. package/src/lib/plot/chartProps.js +76 -0
  101. package/src/lib/plot/crossfilter.js +16 -0
  102. package/src/lib/plot/facet.js +58 -0
  103. package/src/lib/plot/frames.js +90 -0
  104. package/src/lib/plot/helpers.js +14 -0
  105. package/src/lib/plot/preset.js +53 -0
  106. package/src/lib/plot/scales.js +56 -0
  107. package/src/lib/plot/stat.js +92 -0
  108. package/src/lib/plot/types.js +65 -0
  109. package/src/lib/scales.svelte.js +2 -26
  110. package/src/lib/swatch.js +13 -0
  111. package/src/lib/utils.js +9 -0
  112. package/src/lib/xscale.js +31 -0
  113. package/src/patterns/DefinePatterns.svelte +32 -0
  114. package/src/patterns/PatternDef.svelte +27 -0
  115. package/src/patterns/index.js +4 -14
  116. package/src/patterns/patterns.js +208 -0
  117. package/src/patterns/scale.js +87 -0
  118. package/src/spec/chart-spec.js +29 -0
  119. package/src/symbols/Shape.svelte +1 -1
  120. package/src/symbols/constants/index.js +1 -1
  121. package/dist/old_lib/index.d.ts +0 -4
  122. package/dist/old_lib/plots.d.ts +0 -3
  123. package/dist/old_lib/swatch.d.ts +0 -285
  124. package/dist/old_lib/utils.d.ts +0 -1
  125. package/dist/patterns/paths/constants.d.ts +0 -1
  126. package/dist/template/constants.d.ts +0 -43
  127. package/dist/template/shapes/index.d.ts +0 -4
  128. package/src/old_lib/index.js +0 -4
  129. package/src/old_lib/plots.js +0 -27
  130. package/src/old_lib/swatch.js +0 -16
  131. package/src/old_lib/utils.js +0 -8
  132. package/src/patterns/Brick.svelte +0 -15
  133. package/src/patterns/Circles.svelte +0 -18
  134. package/src/patterns/CrossHatch.svelte +0 -12
  135. package/src/patterns/CurvedWave.svelte +0 -7
  136. package/src/patterns/Dots.svelte +0 -20
  137. package/src/patterns/OutlineCircles.svelte +0 -13
  138. package/src/patterns/Tile.svelte +0 -16
  139. package/src/patterns/Triangles.svelte +0 -13
  140. package/src/patterns/Waves.svelte +0 -9
  141. package/src/patterns/paths/NamedPattern.svelte +0 -9
  142. package/src/patterns/paths/constants.js +0 -4
  143. package/src/template/Texture.svelte +0 -13
  144. package/src/template/constants.js +0 -43
  145. package/src/template/shapes/Circles.svelte +0 -15
  146. package/src/template/shapes/Lines.svelte +0 -16
  147. package/src/template/shapes/Path.svelte +0 -9
  148. package/src/template/shapes/Polygons.svelte +0 -15
  149. package/src/template/shapes/index.js +0 -4
  150. /package/dist/{old_lib → lib}/brewer.d.ts +0 -0
  151. /package/dist/{old_lib → lib}/chart.d.ts +0 -0
  152. /package/dist/{old_lib → lib}/grid.d.ts +0 -0
  153. /package/dist/{old_lib → lib}/ticks.d.ts +0 -0
  154. /package/src/{old_lib → lib}/brewer.js +0 -0
  155. /package/src/{old_lib → lib}/chart.js +0 -0
  156. /package/src/{old_lib → lib}/grid.js +0 -0
  157. /package/src/{old_lib → lib}/ticks.js +0 -0
@@ -0,0 +1,194 @@
1
+ <script>
2
+ import { onMount, onDestroy } from 'svelte'
3
+ import { extractFrames, normalizeFrame, computeStaticDomains } from './lib/plot/frames.js'
4
+ import Timeline from './Plot/Timeline.svelte'
5
+ import PlotChart from './Plot.svelte'
6
+
7
+ /**
8
+ * @type {{
9
+ * data: Object[],
10
+ * animate: { by: string, duration?: number, loop?: boolean },
11
+ * x?: string,
12
+ * y?: string,
13
+ * color?: string,
14
+ * geoms?: import('./lib/plot/types.js').GeomSpec[],
15
+ * helpers?: import('./lib/plot/types.js').PlotHelpers,
16
+ * width?: number,
17
+ * height?: number,
18
+ * mode?: 'light' | 'dark',
19
+ * grid?: boolean,
20
+ * legend?: boolean,
21
+ * children?: import('svelte').Snippet
22
+ * }}
23
+ */
24
+ let {
25
+ data = [],
26
+ animate,
27
+ x,
28
+ y,
29
+ color,
30
+ geoms = [],
31
+ helpers = {},
32
+ width = 600,
33
+ height = 400,
34
+ mode = 'light',
35
+ grid = true,
36
+ legend = false,
37
+ children
38
+ } = $props()
39
+
40
+ // Extract and normalize frames
41
+ const rawFrames = $derived(extractFrames(data, animate.by))
42
+ const frameKeys = $derived([...rawFrames.keys()])
43
+
44
+ const channels = $derived({ x, y, color })
45
+ const allXValues = $derived(x ? [...new Set(data.map((d) => d[x]))] : [])
46
+ const allColorValues = $derived(color ? [...new Set(data.map((d) => d[color]))] : null)
47
+
48
+ const staticDomains = $derived(
49
+ x && y ? computeStaticDomains(rawFrames, channels) : { xDomain: undefined, yDomain: undefined }
50
+ )
51
+
52
+ // Playback state
53
+ let currentIndex = $state(0)
54
+ let playing = $state(false)
55
+ let speed = $state(1)
56
+
57
+ // Current frame data (normalized — missing combos filled with 0)
58
+ const currentFrameData = $derived.by(() => {
59
+ const key = frameKeys[currentIndex]
60
+ const raw = rawFrames.get(key) ?? []
61
+ if (!x || !y) return raw
62
+ return normalizeFrame(raw, { x, y, color }, allXValues, allColorValues)
63
+ })
64
+
65
+ // Reduced motion preference
66
+ let prefersReducedMotion = $state(false)
67
+ onMount(() => {
68
+ if (typeof window.matchMedia !== 'function') return
69
+ const mq = window.matchMedia('(prefers-reduced-motion: reduce)')
70
+ prefersReducedMotion = mq.matches
71
+ const handler = (e) => { prefersReducedMotion = e.matches }
72
+ mq.addEventListener('change', handler)
73
+ return () => mq.removeEventListener('change', handler)
74
+ })
75
+
76
+ // Animation loop
77
+ const baseDuration = $derived(animate.duration ?? 800)
78
+ const msPerFrame = $derived(Math.round(baseDuration / speed))
79
+ let lastTime = 0
80
+ let rafId = 0
81
+
82
+ function tick(time) {
83
+ if (!playing) return
84
+ if (time - lastTime >= msPerFrame) {
85
+ lastTime = time
86
+ currentIndex = currentIndex + 1
87
+ if (currentIndex >= frameKeys.length) {
88
+ if (animate.loop ?? false) {
89
+ currentIndex = 0
90
+ } else {
91
+ playing = false
92
+ return
93
+ }
94
+ }
95
+ }
96
+ rafId = requestAnimationFrame(tick)
97
+ }
98
+
99
+ $effect(() => {
100
+ // Reading msPerFrame here makes it a tracked dependency — effect re-runs on speed changes,
101
+ // which resets lastTime=0 to re-anchor frame pacing.
102
+ const _ms = msPerFrame
103
+ if (playing && !prefersReducedMotion) {
104
+ lastTime = 0
105
+ rafId = requestAnimationFrame(tick)
106
+ } else {
107
+ cancelAnimationFrame(rafId)
108
+ }
109
+ return () => cancelAnimationFrame(rafId)
110
+ })
111
+
112
+ // Reduced motion: step frames on interval instead
113
+ let reducedInterval = 0
114
+ $effect(() => {
115
+ if (!playing || !prefersReducedMotion) {
116
+ clearInterval(reducedInterval)
117
+ return
118
+ }
119
+ reducedInterval = setInterval(() => {
120
+ currentIndex = currentIndex + 1
121
+ if (currentIndex >= frameKeys.length) {
122
+ if (animate.loop ?? false) currentIndex = 0
123
+ else { playing = false; clearInterval(reducedInterval) }
124
+ }
125
+ }, msPerFrame)
126
+ return () => clearInterval(reducedInterval)
127
+ })
128
+
129
+ onDestroy(() => {
130
+ cancelAnimationFrame(rafId)
131
+ clearInterval(reducedInterval)
132
+ })
133
+
134
+ $effect(() => {
135
+ const len = frameKeys.length
136
+ if (currentIndex >= len && len > 0) {
137
+ currentIndex = len - 1
138
+ playing = false
139
+ } else if (len === 0) {
140
+ currentIndex = 0
141
+ playing = false
142
+ }
143
+ })
144
+
145
+ function handlePlay() { playing = true }
146
+ function handlePause() { playing = false }
147
+ function handleScrub(index) {
148
+ playing = false
149
+ currentIndex = index
150
+ }
151
+ function handleSpeed(s) { speed = s }
152
+
153
+ // Build spec for the current frame, with static domain overrides
154
+ const frameSpec = $derived({
155
+ data: currentFrameData,
156
+ x, y, color,
157
+ geoms,
158
+ xDomain: staticDomains.xDomain,
159
+ yDomain: staticDomains.yDomain
160
+ })
161
+ </script>
162
+
163
+ <div data-plot-animated>
164
+ <PlotChart
165
+ spec={frameSpec}
166
+ {helpers}
167
+ {width}
168
+ {height}
169
+ {mode}
170
+ {grid}
171
+ {legend}
172
+ >
173
+ {@render children?.()}
174
+ </PlotChart>
175
+
176
+ <Timeline
177
+ {frameKeys}
178
+ {currentIndex}
179
+ {playing}
180
+ {speed}
181
+ onplay={handlePlay}
182
+ onpause={handlePause}
183
+ onscrub={handleScrub}
184
+ onspeed={handleSpeed}
185
+ />
186
+ </div>
187
+
188
+ <style>
189
+ [data-plot-animated] {
190
+ display: flex;
191
+ flex-direction: column;
192
+ width: 100%;
193
+ }
194
+ </style>
@@ -0,0 +1,101 @@
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
+ $effect(() => {
44
+ if (spec) {
45
+ brewer.update({
46
+ data: spec.data,
47
+ channels: spec.channels,
48
+ width,
49
+ height,
50
+ mode,
51
+ layers: spec.layers
52
+ })
53
+ } else {
54
+ const channels = {}
55
+ if (x) channels.x = x
56
+ if (y) channels.y = y
57
+ if (color) channels.color = color
58
+ if (pattern) channels.pattern = pattern
59
+ if (fill) channels.fill = fill
60
+ if (size) channels.size = size
61
+ if (label) channels.label = label
62
+ if (symbol) channels.symbol = symbol
63
+ brewer.update({ data, channels, width, height, mode })
64
+ }
65
+ })
66
+
67
+ </script>
68
+
69
+ <div class="chart-container" data-chart-root>
70
+ <svg
71
+ {width}
72
+ {height}
73
+ viewBox="0 0 {width} {height}"
74
+ role="img"
75
+ aria-label="Chart visualization"
76
+ >
77
+ <g
78
+ class="chart-area"
79
+ data-chart-canvas
80
+ >
81
+ {@render children?.()}
82
+ </g>
83
+ </svg>
84
+ </div>
85
+
86
+ <style>
87
+ .chart-container {
88
+ position: relative;
89
+ width: 100%;
90
+ height: auto;
91
+ }
92
+
93
+ svg {
94
+ display: block;
95
+ overflow: visible;
96
+ }
97
+
98
+ .chart-area {
99
+ pointer-events: all;
100
+ }
101
+ </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>
@@ -1,95 +1,73 @@
1
1
  <script>
2
- import { getContext } from 'svelte'
3
- import { createXAxis, createYAxis, createTickAttributes } from '../lib/brewing/axes.svelte.js'
2
+ import { getContext } from 'svelte'
4
3
 
5
- let {
6
- type = 'x',
7
- field = null,
8
- label = '',
9
- ticks = null,
10
- tickFormat = null,
11
- grid = false
12
- } = $props()
4
+ /** @type {'x' | 'y'} */
5
+ let { type = 'x', label = '' } = $props()
13
6
 
14
- // Get brewer from context
15
- const brewer = getContext('chart-brewer')
7
+ const state = getContext('plot-state')
16
8
 
17
- // Set field mappings in the brewer
18
- $effect(() => {
19
- if (field) {
20
- brewer.setFields({
21
- [type]: field
22
- })
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
+ })
23
20
 
24
- // Ensure scales are updated
25
- brewer.createScales()
26
- }
27
- })
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
+ })
28
32
 
29
- // Compute axis data whenever scales change
30
- let axisData = $derived(
31
- type === 'x'
32
- ? brewer.createXAxis({ tickCount: ticks, tickFormat, label })
33
- : brewer.createYAxis({ tickCount: ticks, tickFormat, label })
34
- )
33
+ const xTransform = $derived(`translate(0, ${state.xAxisY ?? state.innerHeight})`)
34
+ const yTransform = $derived(`translate(${state.yAxisX ?? 0}, 0)`)
35
35
  </script>
36
36
 
37
- <g class="axis {type}-axis" transform={axisData.transform} data-plot-axis={type}>
38
- {#if axisData.ticks.length > 0}
39
- <!-- Axis line -->
40
- <line
41
- data-plot-axis-line
42
- x1={type === 'x' ? 0 : 0}
43
- y1={type === 'x' ? 0 : 0}
44
- x2={type === 'x' ? brewer.getDimensions().innerWidth : 0}
45
- y2={type === 'x' ? 0 : brewer.getDimensions().innerHeight}
46
- stroke="currentColor"
47
- />
48
-
49
- <!-- Ticks -->
50
- {#each axisData.ticks as tick, index (index)}
51
- {@const attrs = createTickAttributes(tick, type)}
52
- <g {...attrs}>
53
- <line
54
- x1="0"
55
- y1="0"
56
- x2={type === 'x' ? 0 : -6}
57
- y2={type === 'x' ? 6 : 0}
58
- stroke="currentColor"
59
- />
60
- <text x={type === 'x' ? 0 : -9} y={type === 'x' ? 9 : 0} data-plot-tick-label>
61
- {tick.formattedValue}
62
- </text>
63
- </g>
64
- {/each}
65
-
66
- <!-- Axis label -->
67
- {#if label}
68
- <text
69
- class="axis-label {type}-axis-label"
70
- transform={axisData.labelTransform}
71
- text-anchor="middle"
72
- data-plot-axis-label
73
- >
74
- {label}
75
- </text>
76
- {/if}
77
- {/if}
78
- </g>
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}
79
68
 
80
69
  <style>
81
- .axis {
82
- font-size: 12px;
83
- }
84
-
85
- .axis-label {
86
- font-size: 14px;
87
- font-weight: 500;
88
- fill: currentColor;
89
- }
90
-
91
- [data-plot-tick-label] {
92
- font-size: 11px;
93
- fill: currentColor;
94
- }
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; }
95
73
  </style>