@rokkit/chart 1.0.0-next.14 → 1.0.0-next.140

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 (150) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +150 -46
  3. package/dist/Plot/index.d.ts +5 -0
  4. package/dist/elements/index.d.ts +6 -0
  5. package/dist/index.d.ts +14 -0
  6. package/dist/lib/brewing/axes.svelte.d.ts +66 -0
  7. package/dist/lib/brewing/bars.svelte.d.ts +56 -0
  8. package/dist/lib/brewing/dimensions.svelte.d.ts +35 -0
  9. package/dist/lib/brewing/index.svelte.d.ts +118 -0
  10. package/dist/lib/brewing/legends.svelte.d.ts +48 -0
  11. package/dist/lib/brewing/scales.svelte.d.ts +24 -0
  12. package/dist/lib/brewing/types.d.ts +162 -0
  13. package/dist/lib/context.d.ts +13 -0
  14. package/dist/lib/scales.svelte.d.ts +35 -0
  15. package/dist/lib/utils.d.ts +60 -0
  16. package/dist/old_lib/brewer.d.ts +9 -0
  17. package/dist/old_lib/chart.d.ts +40 -0
  18. package/dist/old_lib/grid.d.ts +72 -0
  19. package/dist/old_lib/index.d.ts +4 -0
  20. package/dist/old_lib/plots.d.ts +3 -0
  21. package/dist/old_lib/swatch.d.ts +285 -0
  22. package/dist/old_lib/ticks.d.ts +36 -0
  23. package/dist/old_lib/utils.d.ts +1 -0
  24. package/dist/patterns/index.d.ts +9 -0
  25. package/dist/patterns/paths/constants.d.ts +1 -0
  26. package/dist/symbols/constants/index.d.ts +1 -0
  27. package/dist/symbols/index.d.ts +5 -0
  28. package/dist/template/constants.d.ts +43 -0
  29. package/dist/template/shapes/index.d.ts +4 -0
  30. package/package.json +34 -44
  31. package/src/Plot/Axis.svelte +95 -0
  32. package/src/Plot/Bar.svelte +96 -0
  33. package/src/Plot/Grid.svelte +68 -0
  34. package/src/Plot/Legend.svelte +127 -0
  35. package/src/Plot/Root.svelte +107 -0
  36. package/src/Plot/index.js +5 -0
  37. package/src/Symbol.svelte +21 -0
  38. package/src/Texture.svelte +18 -0
  39. package/src/elements/Bar.svelte +22 -24
  40. package/src/elements/ColorRamp.svelte +20 -22
  41. package/src/elements/ContinuousLegend.svelte +20 -17
  42. package/src/elements/DefinePatterns.svelte +24 -0
  43. package/src/elements/DiscreteLegend.svelte +15 -15
  44. package/src/elements/Label.svelte +4 -8
  45. package/src/elements/SymbolGrid.svelte +23 -0
  46. package/src/elements/index.js +6 -0
  47. package/src/examples/BarChartExample.svelte +81 -0
  48. package/src/index.js +18 -16
  49. package/src/lib/brewing/axes.svelte.js +265 -0
  50. package/src/lib/brewing/bars.svelte.js +177 -0
  51. package/src/lib/brewing/dimensions.svelte.js +56 -0
  52. package/src/lib/brewing/index.svelte.js +205 -0
  53. package/src/lib/brewing/legends.svelte.js +137 -0
  54. package/src/lib/brewing/scales.svelte.js +106 -0
  55. package/src/lib/brewing/types.js +73 -0
  56. package/src/lib/context.js +133 -0
  57. package/src/lib/scales.svelte.js +172 -0
  58. package/src/lib/utils.js +107 -120
  59. package/src/old_lib/brewer.js +25 -0
  60. package/src/old_lib/chart.js +213 -0
  61. package/src/old_lib/grid.js +85 -0
  62. package/src/old_lib/index.js +4 -0
  63. package/src/old_lib/plots.js +27 -0
  64. package/src/old_lib/swatch.js +16 -0
  65. package/src/old_lib/ticks.js +46 -0
  66. package/src/old_lib/utils.js +8 -0
  67. package/src/patterns/Brick.svelte +15 -0
  68. package/src/patterns/Circles.svelte +18 -0
  69. package/src/patterns/CrossHatch.svelte +12 -0
  70. package/src/patterns/CurvedWave.svelte +7 -0
  71. package/src/patterns/Dots.svelte +20 -0
  72. package/src/patterns/OutlineCircles.svelte +13 -0
  73. package/src/patterns/README.md +3 -0
  74. package/src/patterns/Tile.svelte +16 -0
  75. package/src/patterns/Triangles.svelte +13 -0
  76. package/src/patterns/Waves.svelte +9 -0
  77. package/src/patterns/index.js +14 -0
  78. package/src/patterns/paths/NamedPattern.svelte +9 -0
  79. package/src/patterns/paths/constants.js +4 -0
  80. package/src/symbols/RoundedSquare.svelte +33 -0
  81. package/src/symbols/Shape.svelte +37 -0
  82. package/src/symbols/constants/index.js +4 -0
  83. package/src/symbols/index.js +9 -0
  84. package/src/symbols/outline.svelte +60 -0
  85. package/src/symbols/solid.svelte +60 -0
  86. package/src/template/Texture.svelte +13 -0
  87. package/src/template/constants.js +43 -0
  88. package/src/template/shapes/Circles.svelte +15 -0
  89. package/src/template/shapes/Lines.svelte +16 -0
  90. package/src/template/shapes/Path.svelte +9 -0
  91. package/src/template/shapes/Polygons.svelte +15 -0
  92. package/src/template/shapes/index.js +4 -0
  93. package/src/chart/FacetGrid.svelte +0 -51
  94. package/src/chart/Grid.svelte +0 -34
  95. package/src/chart/Legend.svelte +0 -16
  96. package/src/chart/PatternDefs.svelte +0 -13
  97. package/src/chart/Swatch.svelte +0 -93
  98. package/src/chart/SwatchButton.svelte +0 -29
  99. package/src/chart/SwatchGrid.svelte +0 -55
  100. package/src/chart/Symbol.svelte +0 -37
  101. package/src/chart/Texture.svelte +0 -16
  102. package/src/chart/TexturedShape.svelte +0 -27
  103. package/src/chart/TimelapseChart.svelte +0 -97
  104. package/src/chart/Timer.svelte +0 -27
  105. package/src/chart.js +0 -9
  106. package/src/components/charts/Axis.svelte +0 -66
  107. package/src/components/charts/Chart.svelte +0 -35
  108. package/src/components/index.js +0 -23
  109. package/src/components/lib/axis.js +0 -0
  110. package/src/components/lib/chart.js +0 -187
  111. package/src/components/lib/color.js +0 -327
  112. package/src/components/lib/funnel.js +0 -204
  113. package/src/components/lib/index.js +0 -19
  114. package/src/components/lib/pattern.js +0 -190
  115. package/src/components/lib/rollup.js +0 -55
  116. package/src/components/lib/shape.js +0 -199
  117. package/src/components/lib/summary.js +0 -145
  118. package/src/components/lib/theme.js +0 -23
  119. package/src/components/lib/timer.js +0 -41
  120. package/src/components/lib/utils.js +0 -165
  121. package/src/components/plots/BarPlot.svelte +0 -36
  122. package/src/components/plots/BoxPlot.svelte +0 -54
  123. package/src/components/plots/ScatterPlot.svelte +0 -30
  124. package/src/components/store.js +0 -70
  125. package/src/constants.js +0 -66
  126. package/src/elements/PatternDefs.svelte +0 -13
  127. package/src/elements/PatternMask.svelte +0 -20
  128. package/src/elements/Symbol.svelte +0 -38
  129. package/src/elements/Tooltip.svelte +0 -23
  130. package/src/funnel.svelte +0 -35
  131. package/src/geom.js +0 -105
  132. package/src/lib/axis.js +0 -75
  133. package/src/lib/colors.js +0 -32
  134. package/src/lib/geom.js +0 -4
  135. package/src/lib/shapes.js +0 -144
  136. package/src/lib/timer.js +0 -44
  137. package/src/lookup.js +0 -29
  138. package/src/plots/BarPlot.svelte +0 -55
  139. package/src/plots/BoxPlot.svelte +0 -0
  140. package/src/plots/FunnelPlot.svelte +0 -33
  141. package/src/plots/HeatMap.svelte +0 -5
  142. package/src/plots/HeatMapCalendar.svelte +0 -129
  143. package/src/plots/LinePlot.svelte +0 -55
  144. package/src/plots/Plot.svelte +0 -25
  145. package/src/plots/RankBarPlot.svelte +0 -38
  146. package/src/plots/ScatterPlot.svelte +0 -20
  147. package/src/plots/ViolinPlot.svelte +0 -11
  148. package/src/plots/heatmap.js +0 -70
  149. package/src/plots/index.js +0 -10
  150. package/src/swatch.js +0 -11
@@ -0,0 +1,265 @@
1
+ // import { scaleBand } from 'd3-scale'
2
+
3
+ import {} from './types.js'
4
+
5
+ /**
6
+ * @typedef {import('./types').TickData} TickData
7
+ * @typedef {import('./types').AxisData} AxisData
8
+ * @typedef {import('./types').ChartScales} ChartScales
9
+ * @typedef {import('./types').ChartDimensions} ChartDimensions
10
+ */
11
+
12
+ const IDENTITY = (v) => v
13
+
14
+ /**
15
+ * @param {Object} xScale
16
+ * @param {number|null} tickCount
17
+ * @returns {Array}
18
+ */
19
+ function xTicksFromContinuous(xScale, tickCount) {
20
+ return tickCount !== null ? xScale.ticks(tickCount) : xScale.ticks()
21
+ }
22
+
23
+ /**
24
+ * @param {Array} domain
25
+ * @param {number} tickCount
26
+ * @returns {Array}
27
+ */
28
+ function downsampleDomain(domain, tickCount) {
29
+ const step = Math.max(1, Math.floor(domain.length / tickCount))
30
+ return domain.filter((_, i) => i % step === 0)
31
+ }
32
+
33
+ /**
34
+ * @param {Object} xScale
35
+ * @param {number|null} tickCount
36
+ * @returns {Array}
37
+ */
38
+ function xTicksFromBand(xScale, tickCount) {
39
+ const domain = xScale.domain()
40
+ if (tickCount === null || tickCount >= domain.length) return domain
41
+ return downsampleDomain(domain, tickCount)
42
+ }
43
+
44
+ /**
45
+ * @param {Object} xScale
46
+ * @param {number|null} tickCount
47
+ * @returns {Array}
48
+ */
49
+ function resolveXTicks(xScale, tickCount) {
50
+ return xScale.ticks ? xTicksFromContinuous(xScale, tickCount) : xTicksFromBand(xScale, tickCount)
51
+ }
52
+
53
+ /**
54
+ * @param {Object} xScale
55
+ * @param {unknown} value
56
+ * @returns {number}
57
+ */
58
+ function xPosition(xScale, value) {
59
+ return xScale.bandwidth ? xScale(value) + xScale.bandwidth() / 2 : xScale(value)
60
+ }
61
+
62
+ /**
63
+ * @param {Object} scale
64
+ * @param {Array} values
65
+ * @param {Function} formatter
66
+ * @param {(scale: Object, v: unknown) => number} getPosition
67
+ * @returns {Array}
68
+ */
69
+ function mapTicks(scale, values, formatter, getPosition) {
70
+ return values.map((value) => ({
71
+ value,
72
+ position: getPosition(scale, value),
73
+ formattedValue: formatter(value)
74
+ }))
75
+ }
76
+
77
+ /**
78
+ * @param {Object} options
79
+ * @returns {{ tickCount: number|null, formatter: Function, label: string }}
80
+ */
81
+ function parseAxisOptions(options) {
82
+ const opts = options || {}
83
+ return {
84
+ tickCount: opts.tickCount !== undefined ? opts.tickCount : null,
85
+ formatter: opts.tickFormat ? opts.tickFormat : IDENTITY,
86
+ label: opts.label ? opts.label : ''
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Creates x-axis tick data for rendering
92
+ *
93
+ * @param {Object} scales - Chart scales
94
+ * @param {Function} scales.x - X-axis scale
95
+ * @param {Object} dimensions - Chart dimensions
96
+ * @param {Object} options - Axis options
97
+ * @param {number} [options.tickCount] - Number of ticks to show
98
+ * @param {Function} [options.tickFormat] - Tick formatting function
99
+ * @param {string} [options.label] - Axis label
100
+ * @returns {AxisData} Axis rendering data
101
+ */
102
+ export function createXAxis(scales, dimensions, options) {
103
+ if (!scales.x) return { ticks: [] }
104
+ const { tickCount, formatter, label } = parseAxisOptions(options)
105
+ const ticks = mapTicks(scales.x, resolveXTicks(scales.x, tickCount), formatter, xPosition)
106
+ return {
107
+ ticks,
108
+ label,
109
+ transform: `translate(0, ${dimensions.innerHeight})`,
110
+ labelTransform: `translate(${dimensions.innerWidth / 2}, 35)`
111
+ }
112
+ }
113
+
114
+ /**
115
+ * @param {Object} yScale
116
+ * @param {number|null} tickCount
117
+ * @returns {Array}
118
+ */
119
+ function yTicksFromDomain(yScale, tickCount) {
120
+ const domain = yScale.domain()
121
+ if (tickCount === null || domain.length <= tickCount) return domain
122
+ return downsampleDomain(domain, tickCount)
123
+ }
124
+
125
+ /**
126
+ * @param {Object} yScale
127
+ * @param {number|null} tickCount
128
+ * @returns {Array}
129
+ */
130
+ function resolveYTicks(yScale, tickCount) {
131
+ if (yScale.ticks) return tickCount !== null ? yScale.ticks(tickCount) : yScale.ticks()
132
+ if (yScale.domain) return yTicksFromDomain(yScale, tickCount)
133
+ return []
134
+ }
135
+
136
+ /**
137
+ * @param {Object} scale
138
+ * @param {unknown} value
139
+ * @returns {unknown}
140
+ */
141
+ function yPositionPassthrough(scale, value) {
142
+ return scale(value)
143
+ }
144
+
145
+ /**
146
+ * Creates y-axis tick data for rendering
147
+ *
148
+ * @param {Object} scales - Chart scales
149
+ * @param {Function} scales.y - Y-axis scale
150
+ * @param {Object} dimensions - Chart dimensions
151
+ * @param {Object} options - Axis options
152
+ * @param {number} [options.tickCount] - Number of ticks to show
153
+ * @param {Function} [options.tickFormat] - Tick formatting function
154
+ * @param {string} [options.label] - Axis label
155
+ * @returns {AxisData} Axis rendering data
156
+ */
157
+ export function createYAxis(scales, dimensions, options) {
158
+ if (!scales.y) return { ticks: [] }
159
+ const { tickCount, formatter, label } = parseAxisOptions(options)
160
+ const ticks = mapTicks(scales.y, resolveYTicks(scales.y, tickCount), formatter, yPositionPassthrough)
161
+ return {
162
+ ticks,
163
+ label,
164
+ transform: 'translate(0, 0)',
165
+ labelTransform: `translate(-40, ${dimensions.innerHeight / 2}) rotate(-90)`
166
+ }
167
+ }
168
+
169
+ /**
170
+ * @param {Object} scales
171
+ * @param {Object} dimensions
172
+ * @param {number|null} xTickCount
173
+ * @returns {Array}
174
+ */
175
+ function buildXLines(scales, dimensions, xTickCount) {
176
+ return createXAxis(scales, dimensions, { tickCount: xTickCount }).ticks.map((tick) => ({
177
+ x1: tick.position,
178
+ y1: 0,
179
+ x2: tick.position,
180
+ y2: dimensions.innerHeight
181
+ }))
182
+ }
183
+
184
+ /**
185
+ * @param {Object} scales
186
+ * @param {Object} dimensions
187
+ * @param {number|null} yTickCount
188
+ * @returns {Array}
189
+ */
190
+ function buildYLines(scales, dimensions, yTickCount) {
191
+ return createYAxis(scales, dimensions, { tickCount: yTickCount }).ticks.map((tick) => ({
192
+ x1: 0,
193
+ y1: tick.position,
194
+ x2: dimensions.innerWidth,
195
+ y2: tick.position
196
+ }))
197
+ }
198
+
199
+ /**
200
+ * @param {Object} opts
201
+ * @returns {{ direction: string, xTickCount: number|null, yTickCount: number|null }}
202
+ */
203
+ function parseGridOptions(opts) {
204
+ const o = opts || {}
205
+ return {
206
+ direction: o.direction !== undefined ? o.direction : 'both',
207
+ xTickCount: o.xTickCount !== undefined ? o.xTickCount : null,
208
+ yTickCount: o.yTickCount !== undefined ? o.yTickCount : null
209
+ }
210
+ }
211
+
212
+ /**
213
+ * @param {string} direction
214
+ * @returns {{ showX: boolean, showY: boolean }}
215
+ */
216
+ function gridDirections(direction) {
217
+ return {
218
+ showX: direction === 'x' || direction === 'both',
219
+ showY: direction === 'y' || direction === 'both'
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Creates grid line data for rendering
225
+ *
226
+ * @param {Object} scales - Chart scales
227
+ * @param {Object} dimensions - Chart dimensions
228
+ * @param {Object} options - Grid options
229
+ * @param {string} [options.direction='both'] - Grid direction ('x', 'y', or 'both')
230
+ * @param {number} [options.xTickCount] - Number of x-axis ticks
231
+ * @param {number} [options.yTickCount] - Number of y-axis ticks
232
+ * @returns {Object} Grid rendering data
233
+ */
234
+ export function createGrid(scales, dimensions, options) {
235
+ const { direction, xTickCount, yTickCount } = parseGridOptions(options)
236
+ const { showX, showY } = gridDirections(direction)
237
+ return {
238
+ xLines: showX && scales.x ? buildXLines(scales, dimensions, xTickCount) : [],
239
+ yLines: showY && scales.y ? buildYLines(scales, dimensions, yTickCount) : []
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Creates DOM attributes for a tick element
245
+ *
246
+ * @param {TickData} tick - Tick data
247
+ * @param {string} axis - Axis type ('x' or 'y')
248
+ * @returns {Object} Attributes for the tick
249
+ */
250
+ export function createTickAttributes(tick, axis) {
251
+ if (axis === 'x') {
252
+ return {
253
+ 'data-plot-tick': 'major',
254
+ transform: `translate(${tick.position}, 0)`,
255
+ 'text-anchor': 'middle'
256
+ }
257
+ } else {
258
+ return {
259
+ 'data-plot-tick': 'major',
260
+ transform: `translate(0, ${tick.position})`,
261
+ 'text-anchor': 'end',
262
+ 'dominant-baseline': 'middle'
263
+ }
264
+ }
265
+ }
@@ -0,0 +1,177 @@
1
+ import { SvelteSet } from 'svelte/reactivity'
2
+ import {} from './types.js'
3
+
4
+ /**
5
+ * @typedef {import('./types').BarData} BarData
6
+ * @typedef {import('./types').ScaleFields} ScaleFields
7
+ * @typedef {import('./types').ChartScales} ChartScales
8
+ * @typedef {import('./types').ChartDimensions} ChartDimensions
9
+ */
10
+
11
+ const DEFAULT_COLOR = '#4682b4'
12
+
13
+ /**
14
+ * @param {Object} d - data item
15
+ * @param {{ fields: Object, scales: Object, dimensions: Object, defaultColor: string }} ctx
16
+ * @returns {BarData}
17
+ */
18
+ function buildBar(d, ctx) {
19
+ const { fields, scales, dimensions, defaultColor } = ctx
20
+ const { x: xField, y: yField, color: colorField } = fields
21
+ const barWidth = scales.x.bandwidth ? scales.x.bandwidth() : 10
22
+ const barX = scales.x.bandwidth ? scales.x(d[xField]) : scales.x(d[xField]) - barWidth / 2
23
+ return {
24
+ data: d,
25
+ x: barX,
26
+ y: scales.y(d[yField]),
27
+ width: barWidth,
28
+ height: dimensions.innerHeight - scales.y(d[yField]),
29
+ color: colorField && scales.color ? scales.color(d[colorField]) : defaultColor
30
+ }
31
+ }
32
+
33
+ /**
34
+ * @param {Object} options
35
+ * @param {Object} fields
36
+ * @param {Object} scales
37
+ * @returns {{ ctx: Object }|null} null if input is invalid
38
+ */
39
+ function parseBarsInput(options, fields, scales) {
40
+ if (!scales.x || !scales.y) return null
41
+ const opts = options || {}
42
+ return {
43
+ fields,
44
+ scales,
45
+ dimensions: opts.dimensions,
46
+ defaultColor: opts.defaultColor !== undefined ? opts.defaultColor : DEFAULT_COLOR
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Creates bar data for rendering
52
+ *
53
+ * @param {Array} data - Chart data
54
+ * @param {Object} fields - Field mappings
55
+ * @param {string} fields.x - X-axis field
56
+ * @param {string} fields.y - Y-axis field
57
+ * @param {string} fields.color - Color field (optional)
58
+ * @param {Object} scales - Chart scales
59
+ * @param {Function} scales.x - X-axis scale
60
+ * @param {Function} scales.y - Y-axis scale
61
+ * @param {Function} scales.color - Color scale
62
+ * @param {Object} options - Options including dimensions and defaultColor
63
+ * @param {Object} options.dimensions - Chart dimensions
64
+ * @param {string} [options.defaultColor='#4682b4'] - Default color if no color scale
65
+ * @returns {BarData[]} Bar data for rendering
66
+ */
67
+ export function createBars(data, fields, scales, options) {
68
+ if (!data || !data.length) return []
69
+ const ctx = parseBarsInput(options, fields, scales)
70
+ if (!ctx) return []
71
+ return data.map((d) => buildBar(d, ctx))
72
+ }
73
+
74
+ /**
75
+ * Filter bars based on a selection criteria
76
+ *
77
+ * @param {BarData[]} bars - Bar data array
78
+ * @param {Object} selection - Selection criteria
79
+ * @returns {BarData[]} Filtered bars
80
+ */
81
+ export function filterBars(bars, selection) {
82
+ if (!selection) return bars
83
+
84
+ return bars.filter((bar) => {
85
+ for (const [key, value] of Object.entries(selection)) {
86
+ if (bar.data[key] !== value) return false
87
+ }
88
+ return true
89
+ })
90
+ }
91
+
92
+ /**
93
+ * @param {Object} item - data item
94
+ * @param {Object} scales - chart scales
95
+ * @param {{ yField: string, colorField: string, group: unknown, barX: number, barWidth: number, dimensions: Object }} ctx
96
+ * @returns {BarData}
97
+ */
98
+ function buildGroupBar(item, scales, ctx) {
99
+ const { yField, colorField, group, barX, barWidth, dimensions } = ctx
100
+ return {
101
+ data: item,
102
+ group,
103
+ x: barX,
104
+ y: scales.y(item[yField]),
105
+ width: barWidth,
106
+ height: dimensions.innerHeight - scales.y(item[yField]),
107
+ color: scales.color ? scales.color(item[colorField]) : DEFAULT_COLOR
108
+ }
109
+ }
110
+
111
+ /**
112
+ * @param {Object} scales
113
+ * @param {{ groupItems: Array, groupField: string, group: unknown, i: number, yField: string, colorField: string, barWidth: number, padding: number, dimensions: Object, xPos: number }} ctx
114
+ * @returns {BarData|null}
115
+ */
116
+ function buildOneGroupBar(scales, ctx) {
117
+ const { groupItems, groupField, group, i, yField, colorField, barWidth, padding, dimensions, xPos } = ctx
118
+ const item = groupItems.find((d) => d[groupField] === group)
119
+ if (!item) return null
120
+ const barX = xPos + i * (barWidth + padding)
121
+ return buildGroupBar(item, scales, { yField, colorField, group, barX, barWidth, dimensions })
122
+ }
123
+
124
+ /**
125
+ * @param {Object} fields
126
+ * @param {Object} options
127
+ * @returns {{ xField: string, yField: string, groupField: string, colorField: string, dimensions: Object, padding: number }}
128
+ */
129
+ function parseGroupedBarsConfig(fields, options) {
130
+ const opts = options || {}
131
+ return {
132
+ xField: fields.x,
133
+ yField: fields.y,
134
+ groupField: fields.group,
135
+ colorField: fields.color !== undefined ? fields.color : fields.group,
136
+ dimensions: opts.dimensions,
137
+ padding: opts.padding !== undefined ? opts.padding : 0.1
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Creates a grouped bars layout
143
+ *
144
+ * @param {Array} data - Chart data
145
+ * @param {Object} fields - Field mappings
146
+ * @param {Object} scales - Chart scales
147
+ * @param {Object} options - Options including dimensions and padding
148
+ * @param {Object} options.dimensions - Chart dimensions
149
+ * @param {number} [options.padding=0.1] - Padding between bars in a group
150
+ * @returns {Object} Grouped bar data
151
+ */
152
+ export function createGroupedBars(data, fields, scales, options) {
153
+ if (!data || !data.length || !fields.group) return { groups: [], bars: [] }
154
+
155
+ const { xField, yField, groupField, colorField, dimensions, padding } =
156
+ parseGroupedBarsConfig(fields, options)
157
+
158
+ const groups = [...new SvelteSet(data.map((d) => d[groupField]))]
159
+ const xValues = [...new SvelteSet(data.map((d) => d[xField]))]
160
+
161
+ const xScale = scales.x
162
+ const groupWidth = xScale.bandwidth ? xScale.bandwidth() : 20
163
+ const barWidth = (groupWidth - padding * (groups.length - 1)) / groups.length
164
+
165
+ const bars = []
166
+ xValues.forEach((xValue) => {
167
+ const groupItems = data.filter((d) => d[xField] === xValue)
168
+ const xPos = xScale(xValue)
169
+ groups.forEach((group, i) => {
170
+ const ctx = { groupItems, groupField, group, i, yField, colorField, barWidth, padding, dimensions, xPos }
171
+ const bar = buildOneGroupBar(scales, ctx)
172
+ if (bar) bars.push(bar)
173
+ })
174
+ })
175
+
176
+ return { groups, bars }
177
+ }
@@ -0,0 +1,56 @@
1
+ import {} from './types.js'
2
+
3
+ /**
4
+ * @typedef {import('./types').ChartMargin} ChartMargin
5
+ * @typedef {import('./types').ChartDimensions} ChartDimensions
6
+ */
7
+
8
+ /**
9
+ * Default chart margin
10
+ * @type {ChartMargin}
11
+ */
12
+ export const DEFAULT_MARGIN = { top: 20, right: 30, bottom: 40, left: 50 }
13
+
14
+ /**
15
+ * Creates chart dimensions based on width, height and margins
16
+ *
17
+ * @param {number} width - Total chart width
18
+ * @param {number} height - Total chart height
19
+ * @param {ChartMargin} margin - Chart margins
20
+ * @returns {ChartDimensions} Chart dimensions
21
+ */
22
+ export function createDimensions(width = 600, height = 400, margin = DEFAULT_MARGIN) {
23
+ return {
24
+ width,
25
+ height,
26
+ margin: { ...margin },
27
+ innerWidth: width - margin.left - margin.right,
28
+ innerHeight: height - margin.top - margin.bottom
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Updates existing dimensions with new values
34
+ *
35
+ * @param {ChartDimensions} dimensions - Current dimensions
36
+ * @param {Object} updates - Values to update
37
+ * @param {number} [updates.width] - New width
38
+ * @param {number} [updates.height] - New height
39
+ * @param {ChartMargin} [updates.margin] - New margin
40
+ * @returns {ChartDimensions} Updated dimensions
41
+ */
42
+ export function updateDimensions(dimensions, updates = {}) {
43
+ const newDimensions = { ...dimensions }
44
+
45
+ if (updates.width !== undefined) newDimensions.width = updates.width
46
+ if (updates.height !== undefined) newDimensions.height = updates.height
47
+ if (updates.margin !== undefined) newDimensions.margin = { ...updates.margin }
48
+
49
+ // Recalculate inner dimensions
50
+ newDimensions.innerWidth =
51
+ newDimensions.width - newDimensions.margin.left - newDimensions.margin.right
52
+ newDimensions.innerHeight =
53
+ newDimensions.height - newDimensions.margin.top - newDimensions.margin.bottom
54
+
55
+ return newDimensions
56
+ }
@@ -0,0 +1,205 @@
1
+ import { createDimensions, updateDimensions } from './dimensions.svelte.js'
2
+ import { createScales, getOrigin } from './scales.svelte.js'
3
+ import { createBars, filterBars, createGroupedBars } from './bars.svelte.js'
4
+ import { createXAxis, createYAxis, createGrid, createTickAttributes } from './axes.svelte.js'
5
+ import { createLegend, filterByLegend, createLegendItemAttributes } from './legends.svelte.js'
6
+
7
+ /**
8
+ * Chart Brewing - A collection of pure functions for chart data preparation
9
+ */
10
+ export {
11
+ // Dimensions
12
+ createDimensions,
13
+ updateDimensions,
14
+
15
+ // Scales
16
+ createScales,
17
+ getOrigin,
18
+
19
+ // Bars
20
+ createBars,
21
+ filterBars,
22
+ createGroupedBars,
23
+
24
+ // Axes
25
+ createXAxis,
26
+ createYAxis,
27
+ createGrid,
28
+ createTickAttributes,
29
+
30
+ // Legends
31
+ createLegend,
32
+ filterByLegend,
33
+ createLegendItemAttributes
34
+ }
35
+
36
+ /**
37
+ * Main class that manages chart state and provides access to all brewing functions
38
+ */
39
+ export class ChartBrewer {
40
+ #data = []
41
+ #dimensions = createDimensions()
42
+ #scales = { x: null, y: null, color: null }
43
+ #fields = { x: null, y: null, color: null }
44
+ #options = {
45
+ padding: 0.2,
46
+ animationDuration: 300
47
+ }
48
+
49
+ /**
50
+ * Creates a new ChartBrewer instance
51
+ *
52
+ * @param {Object} options Configuration options
53
+ */
54
+ constructor(options = {}) {
55
+ this.#dimensions = createDimensions(options.width, options.height, options.margin)
56
+
57
+ if (options.padding !== undefined) this.#options.padding = options.padding
58
+ if (options.animationDuration !== undefined)
59
+ this.#options.animationDuration = options.animationDuration
60
+ }
61
+
62
+ /**
63
+ * Sets the data for the chart
64
+ *
65
+ * @param {Array} data Data array
66
+ * @returns {ChartBrewer} this for method chaining
67
+ */
68
+ setData(data) {
69
+ this.#data = Array.isArray(data) ? data : []
70
+ return this
71
+ }
72
+
73
+ /**
74
+ * Sets the field mappings for axes and color
75
+ *
76
+ * @param {Object} fields Field mappings
77
+ * @returns {ChartBrewer} this for method chaining
78
+ */
79
+ setFields({ x, y, color }) {
80
+ if (x !== undefined) this.#fields.x = x
81
+ if (y !== undefined) this.#fields.y = y
82
+ if (color !== undefined) this.#fields.color = color
83
+ return this
84
+ }
85
+
86
+ /**
87
+ * Sets the dimensions of the chart
88
+ *
89
+ * @param {Object} dimensions Chart dimensions
90
+ * @returns {ChartBrewer} this for method chaining
91
+ */
92
+ setDimensions({ width, height, margin }) {
93
+ this.#dimensions = updateDimensions(this.#dimensions, { width, height, margin })
94
+ return this
95
+ }
96
+
97
+ /**
98
+ * Creates scales based on data and dimensions
99
+ *
100
+ * @returns {ChartBrewer} this for method chaining
101
+ */
102
+ createScales() {
103
+ this.#scales = createScales(this.#data, this.#fields, this.#dimensions, {
104
+ padding: this.#options.padding
105
+ })
106
+ return this
107
+ }
108
+
109
+ /**
110
+ * Creates bar data for rendering
111
+ *
112
+ * @returns {Array} Data for rendering bars
113
+ */
114
+ createBars() {
115
+ return createBars(this.#data, this.#fields, this.#scales, { dimensions: this.#dimensions })
116
+ }
117
+
118
+ /**
119
+ * Creates x-axis tick data for rendering
120
+ *
121
+ * @param {Object} options Axis options
122
+ * @returns {Object} Axis rendering data
123
+ */
124
+ createXAxis(options = {}) {
125
+ return createXAxis(this.#scales, this.#dimensions, options)
126
+ }
127
+
128
+ /**
129
+ * Creates y-axis tick data for rendering
130
+ *
131
+ * @param {Object} options Axis options
132
+ * @returns {Object} Axis rendering data
133
+ */
134
+ createYAxis(options = {}) {
135
+ return createYAxis(this.#scales, this.#dimensions, options)
136
+ }
137
+
138
+ /**
139
+ * Creates grid line data for rendering
140
+ *
141
+ * @param {Object} options Grid options
142
+ * @returns {Object} Grid rendering data
143
+ */
144
+ createGrid(options = {}) {
145
+ return createGrid(this.#scales, this.#dimensions, options)
146
+ }
147
+
148
+ /**
149
+ * Creates legend data for rendering
150
+ *
151
+ * @param {Object} options Legend options
152
+ * @returns {Object} Legend rendering data
153
+ */
154
+ createLegend(options = {}) {
155
+ return createLegend(this.#data, this.#fields, this.#scales, {
156
+ ...options,
157
+ dimensions: this.#dimensions
158
+ })
159
+ }
160
+
161
+ /**
162
+ * Gets all chart dimensions
163
+ *
164
+ * @returns {Object} Chart dimensions
165
+ */
166
+ getDimensions() {
167
+ return { ...this.#dimensions }
168
+ }
169
+
170
+ /**
171
+ * Gets all chart scales
172
+ *
173
+ * @returns {Object} Chart scales
174
+ */
175
+ getScales() {
176
+ return { ...this.#scales }
177
+ }
178
+
179
+ /**
180
+ * Gets the animation duration
181
+ *
182
+ * @returns {number} Animation duration in ms
183
+ */
184
+ getAnimationDuration() {
185
+ return this.#options.animationDuration
186
+ }
187
+
188
+ /**
189
+ * Gets the data being used
190
+ *
191
+ * @returns {Array} Chart data
192
+ */
193
+ getData() {
194
+ return [...this.#data]
195
+ }
196
+
197
+ /**
198
+ * Gets the fields configuration
199
+ *
200
+ * @returns {Object} Fields configuration
201
+ */
202
+ getFields() {
203
+ return { ...this.#fields }
204
+ }
205
+ }