@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,65 @@
1
+ // packages/chart/src/lib/plot/types.js
2
+ // JSDoc typedefs for the Plot system. No runtime code.
3
+
4
+ /**
5
+ * @typedef {Object} GeomSpec
6
+ * @property {string} type - Geom type: 'bar'|'line'|'area'|'point'|'box'|'violin'|'arc' or custom
7
+ * @property {string} [x]
8
+ * @property {string} [y]
9
+ * @property {string} [color]
10
+ * @property {string} [fill]
11
+ * @property {string} [size]
12
+ * @property {string} [symbol]
13
+ * @property {string} [pattern]
14
+ * @property {string} [stat] - Built-in or helpers.stats key
15
+ * @property {Record<string, unknown>} [options]
16
+ */
17
+
18
+ /**
19
+ * @typedef {Object} PlotSpec
20
+ * @property {Record<string, unknown>[]} data
21
+ * @property {string} [x]
22
+ * @property {string} [y]
23
+ * @property {string} [color]
24
+ * @property {string} [fill]
25
+ * @property {string} [size]
26
+ * @property {string} [symbol]
27
+ * @property {string} [pattern]
28
+ * @property {string} [theta]
29
+ * @property {Record<string, string>} [labels]
30
+ * @property {unknown[]} [xDomain]
31
+ * @property {number[]} [yDomain]
32
+ * @property {string} [xLabel]
33
+ * @property {string} [yLabel]
34
+ * @property {[number, number]} [axisOrigin]
35
+ * @property {'categorical'|'sequential'|'diverging'} [colorScale]
36
+ * @property {string} [colorScheme]
37
+ * @property {number} [colorMidpoint]
38
+ * @property {unknown[]} [colorDomain]
39
+ * @property {GeomSpec[]} geoms
40
+ * @property {{ by: string, cols?: number, scales?: 'fixed'|'free'|'free_x'|'free_y' }} [facet]
41
+ * @property {{ by: string, duration?: number, loop?: boolean }} [animate]
42
+ * @property {boolean} [grid]
43
+ * @property {boolean} [legend]
44
+ * @property {boolean} [tooltip]
45
+ * @property {string} [title]
46
+ * @property {string} [preset]
47
+ * @property {number} [width]
48
+ * @property {number} [height]
49
+ * @property {'light'|'dark'} [mode]
50
+ */
51
+
52
+ /**
53
+ * @typedef {Object} PlotHelpers
54
+ * @property {Record<string, (values: unknown[]) => unknown>} [stats]
55
+ * @property {Record<string, (v: unknown) => string>} [format]
56
+ * @property {(d: Record<string, unknown>) => string} [tooltip]
57
+ * @property {Record<string, unknown>} [geoms] Svelte components keyed by type name
58
+ * @property {unknown} [colorScale] d3 scale override
59
+ * @property {{ colors: string[], patterns: string[], symbols: string[] }} [preset]
60
+ * @property {Record<string, { colors: string[], patterns: string[], symbols: string[] }>} [presets]
61
+ * @property {Record<string, unknown>} [patterns] custom SVG pattern components keyed by name
62
+ * @property {Record<string, unknown>} [symbols] custom symbol shape components keyed by name
63
+ */
64
+
65
+ export {}
@@ -0,0 +1,41 @@
1
+ // packages/chart/src/lib/preset.js
2
+
3
+ export const defaultPreset = {
4
+ colors: ['blue', 'emerald', 'rose', 'amber', 'violet', 'sky', 'pink', 'teal',
5
+ 'orange', 'indigo', 'lime', 'cyan', 'gold', 'lavender'],
6
+ shades: {
7
+ light: { fill: '300', stroke: '700' },
8
+ dark: { fill: '500', stroke: '200' }
9
+ },
10
+ opacity: {
11
+ area: 0.6,
12
+ box: 0.5,
13
+ violin: 0.5,
14
+ point: 0.8
15
+ },
16
+ patterns: ['diagonal', 'dots', 'triangles', 'hatch', 'lattice', 'swell',
17
+ 'checkerboard', 'waves', 'petals', 'brick', 'diamonds', 'tile',
18
+ 'scales', 'circles', 'pip', 'rings', 'chevrons', 'shards',
19
+ 'wedge', 'argyle', 'shell'],
20
+ symbols: ['circle', 'square', 'triangle', 'diamond', 'cross', 'star']
21
+ }
22
+
23
+ /**
24
+ * Creates a chart preset by deep-merging overrides with the default preset.
25
+ * All fields are optional. `opacity` is merged key-by-key so partial overrides work.
26
+ * @param {Partial<typeof defaultPreset>} [overrides]
27
+ * @returns {typeof defaultPreset}
28
+ */
29
+ export function createChartPreset(overrides = {}) {
30
+ return {
31
+ ...defaultPreset,
32
+ ...overrides,
33
+ shades: overrides.shades
34
+ ? {
35
+ light: { ...defaultPreset.shades.light, ...overrides.shades.light },
36
+ dark: { ...defaultPreset.shades.dark, ...overrides.shades.dark }
37
+ }
38
+ : defaultPreset.shades,
39
+ opacity: { ...defaultPreset.opacity, ...overrides.opacity }
40
+ }
41
+ }
@@ -0,0 +1,151 @@
1
+ import { SvelteSet } from 'svelte/reactivity'
2
+ import { scaleLinear, scaleOrdinal } from 'd3-scale'
3
+ import { schemeCategory10 } from 'd3-scale-chromatic'
4
+ import { min, max } from 'd3-array'
5
+ import { buildXScale } from './xscale.js'
6
+
7
+ /**
8
+ * Creates appropriate scales based on data and dimensions
9
+ *
10
+ * @param {Array} data The dataset
11
+ * @param {Object} dimensions Chart dimensions
12
+ * @param {Object} options Additional options
13
+ * @param {string} options.xKey Field to use for x-axis
14
+ * @param {string} options.yKey Field to use for y-axis
15
+ * @param {string} [options.colorKey] Field to use for color mapping
16
+ * @param {number} [options.padding=0.2] Padding for band scales
17
+ * @returns {Object} Object containing xScale, yScale, and colorScale
18
+ */
19
+ /**
20
+ * @param {Array} data
21
+ * @param {string} colorKey
22
+ * @returns {Object}
23
+ */
24
+ function buildColorScale(data, colorKey) {
25
+ const uniqueCategories = [...new SvelteSet(data.map((d) => d[colorKey]))]
26
+ return scaleOrdinal().domain(uniqueCategories).range(schemeCategory10)
27
+ }
28
+
29
+ /**
30
+ * @param {Object} options
31
+ * @returns {{ xKey: string, yKey: string, colorKey: string|undefined, padding: number }}
32
+ */
33
+ function parseScaleOptions(options) {
34
+ const opts = options || {}
35
+ return {
36
+ xKey: opts.xKey,
37
+ yKey: opts.yKey,
38
+ colorKey: opts.colorKey,
39
+ padding: opts.padding !== undefined ? opts.padding : 0.2
40
+ }
41
+ }
42
+
43
+ export function createScales(data, dimensions, options) {
44
+ if (!data || !data.length) return {}
45
+
46
+ const { xKey, yKey, colorKey, padding } = parseScaleOptions(options)
47
+ const xValues = data.map((d) => d[xKey])
48
+ const yValues = data.map((d) => d[yKey])
49
+ const colorScale = colorKey ? buildColorScale(data, colorKey) : null
50
+
51
+ return {
52
+ xScale: buildXScale(xValues, dimensions, padding),
53
+ yScale: scaleLinear()
54
+ .domain([0, max(yValues) * 1.1])
55
+ .nice()
56
+ .range([dimensions.innerHeight, 0]),
57
+ colorScale
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Calculates the actual chart dimensions after applying margins
63
+ *
64
+ * @param {number} width
65
+ * @param {number} height
66
+ * @param {Object} margin
67
+ * @returns {Object} Dimensions with calculated inner width and height
68
+ */
69
+ export function calculateChartDimensions(width, height, margin) {
70
+ return {
71
+ width,
72
+ height,
73
+ margin,
74
+ innerWidth: width - margin.left - margin.right,
75
+ innerHeight: height - margin.top - margin.bottom
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Gets the axis origin value
81
+ *
82
+ * @param {Object} scale D3 scale
83
+ * @returns {number} Origin value
84
+ */
85
+ export function getOriginValue(scale) {
86
+ return scale.ticks ? scale(Math.max(0, min(scale.domain()))) : scale.range()[0]
87
+ }
88
+
89
+ /**
90
+ * Resolve tick values for a band scale, applying downsampling when needed
91
+ * @param {Object} scale
92
+ * @param {number} count
93
+ * @returns {{ ticks: Array, offset: number }}
94
+ */
95
+ function bandTicks(scale, count) {
96
+ const offset = scale.bandwidth() / 2
97
+ const domain = scale.domain()
98
+ const cappedCount = Math.min(Math.round(count), domain.length)
99
+ const step = Math.ceil(domain.length / cappedCount)
100
+ const ticks = cappedCount < domain.length ? domain.filter((_, i) => i % step === 0) : domain
101
+ return { ticks, offset }
102
+ }
103
+
104
+ /**
105
+ * @param {number} rangeSize
106
+ * @param {string} axis
107
+ * @param {number} fontSize
108
+ * @returns {number}
109
+ */
110
+ function defaultTickCount(rangeSize, axis, fontSize) {
111
+ const divisor = fontSize * (axis === 'y' ? 3 : 6)
112
+ return Math.abs(rangeSize / divisor)
113
+ }
114
+
115
+ /**
116
+ * @param {Array} ticks
117
+ * @param {Object} scale
118
+ * @param {number} offset
119
+ * @param {boolean} isXAxis
120
+ * @returns {Array}
121
+ */
122
+ function formatTicks(ticks, scale, offset, isXAxis) {
123
+ const pos = isXAxis ? offset : 0
124
+ return ticks.map((t) => ({ value: t, position: scale(t) + pos }))
125
+ }
126
+
127
+ /**
128
+ * Creates axis ticks
129
+ *
130
+ * @param {Object} scale D3 scale
131
+ * @param {string} axis Axis type ('x' or 'y')
132
+ * @param {number} count Number of ticks
133
+ * @param {number} fontSize Font size for determining tick density
134
+ * @returns {Array} Array of tick objects
135
+ */
136
+ export function createTicks(scale, axis, count = null, fontSize = 12) {
137
+ const [minRange, maxRange] = scale.range()
138
+ const tickCount = count ?? defaultTickCount(maxRange - minRange, axis, fontSize)
139
+
140
+ let ticks, offset
141
+ if (scale.ticks) {
142
+ ticks = scale.ticks(Math.round(tickCount))
143
+ offset = 0
144
+ } else {
145
+ const band = bandTicks(scale, tickCount)
146
+ ticks = band.ticks
147
+ offset = band.offset
148
+ }
149
+
150
+ return formatTicks(ticks, scale, offset, axis === 'x')
151
+ }
package/src/lib/swatch.js CHANGED
@@ -1,16 +1,13 @@
1
- import { writable } from 'svelte/store'
2
1
  import palette from './palette.json'
3
- // skipcq: JS-C1003 - Importing all patterns
4
- import * as patterns from '../patterns'
2
+ import { PATTERN_ORDER } from './brewing/patterns.js'
5
3
  import { shapes } from '../symbols'
6
4
 
7
- export const swatch = writable({
5
+ export const swatch = {
8
6
  palette,
9
- patterns,
10
7
  keys: {
11
8
  gray: ['gray'],
12
9
  color: Object.keys(palette).filter((name) => name !== 'gray'),
13
10
  symbol: shapes,
14
- pattern: Object.keys(patterns)
11
+ pattern: PATTERN_ORDER
15
12
  }
16
- })
13
+ }
package/src/lib/utils.js CHANGED
@@ -1,3 +1,145 @@
1
+ import { scaleBand, scaleLinear, scaleOrdinal } from 'd3-scale'
2
+ import { schemeCategory10 } from 'd3-scale-chromatic'
3
+ import { max } from 'd3-array'
4
+
5
+ /**
6
+ * Creates appropriate scales based on data and dimensions
7
+ *
8
+ * @param {Array} data The dataset
9
+ * @param {Object} dimensions Chart dimensions
10
+ * @param {Object} [options] Additional options
11
+ * @param {string} options.xKey Field to use for x-axis
12
+ * @param {string} options.yKey Field to use for y-axis
13
+ * @param {string} [options.colorKey] Field to use for color mapping
14
+ * @returns {Object} Object containing xScale, yScale, and colorScale
15
+ */
16
+ export function createScales(data, dimensions, options = {}) {
17
+ if (!data || data.length === 0) return {}
18
+
19
+ const { xKey, yKey, colorKey } = options
20
+
21
+ const xScale = scaleBand()
22
+ .domain(data.map((d) => d[xKey]))
23
+ .range([0, dimensions.width])
24
+ .padding(0.2)
25
+
26
+ const yScale = scaleLinear()
27
+ .domain([0, max(data, (d) => d[yKey]) * 1.1]) // Add 10% padding on top
28
+ .nice()
29
+ .range([dimensions.height, 0])
30
+
31
+ let colorScale = null
32
+
33
+ if (colorKey) {
34
+ const uniqueCategories = [...new Set(data.map((d) => d[colorKey]))]
35
+ colorScale = scaleOrdinal().domain(uniqueCategories).range(schemeCategory10)
36
+ }
37
+
38
+ return { xScale, yScale, colorScale }
39
+ }
40
+
41
+ /**
42
+ * Calculates the actual chart dimensions after applying margins
43
+ *
44
+ * @param {Object} dimensions Original dimensions
45
+ * @returns {Object} Dimensions with calculated inner width and height
46
+ */
47
+ export function calculateChartDimensions(dimensions) {
48
+ const { width, height, margin } = dimensions
49
+
50
+ return {
51
+ ...dimensions,
52
+ innerWidth: width - margin.left - margin.right,
53
+ innerHeight: height - margin.top - margin.bottom
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Normalizes data for use with D3 charts
59
+ *
60
+ * @param {Array|Object} inputData Raw data or dataset object
61
+ * @returns {Array} Normalized data array
62
+ */
63
+ export function normalizeData(inputData) {
64
+ if (!inputData) return []
65
+
66
+ // If it's a dataset class instance, call select() to get the data
67
+ if (inputData.select && typeof inputData.select === 'function') {
68
+ return inputData.select()
69
+ }
70
+
71
+ // If it's already an array, return as is
72
+ if (Array.isArray(inputData)) {
73
+ return inputData
74
+ }
75
+
76
+ return []
77
+ }
78
+
79
+ /**
80
+ * Generates a unique ID for SVG elements
81
+ *
82
+ * @param {string} prefix Prefix for the ID
83
+ * @returns {string} A unique ID
84
+ */
85
+ export function uniqueId(prefix = 'chart') {
86
+ return `${prefix}-${Math.random().toString(36).substring(2, 10)}`
87
+ }
88
+
89
+ /**
90
+ * Format a single key-value pair for tooltip
91
+ * @param {string} key
92
+ * @param {unknown} value
93
+ * @param {Function|undefined} formatter
94
+ * @returns {string}
95
+ */
96
+ function formatField(key, value, formatter) {
97
+ const formatted = formatter ? formatter(value) : value
98
+ return `${key}: ${formatted}`
99
+ }
100
+
101
+ /**
102
+ * Formats tooltip content for a data point
103
+ *
104
+ * @param {Object} d Data point
105
+ * @param {Object} options Tooltip format options
106
+ * @returns {string} Formatted tooltip HTML content
107
+ */
108
+ export function formatTooltipContent(d, options = {}) {
109
+ if (!d) return ''
110
+
111
+ const { xKey, yKey, xFormat, yFormat } = options
112
+
113
+ if (xKey && yKey) {
114
+ return [formatField(xKey, d[xKey], xFormat), formatField(yKey, d[yKey], yFormat)].join('<br>')
115
+ }
116
+
117
+ return Object.entries(d)
118
+ .map(([key, value]) => `${key}: ${value}`)
119
+ .join('<br>')
120
+ }
121
+
122
+ /**
123
+ * Generates a tooltip formatter function
124
+ *
125
+ * @param {Object} options Tooltip format options
126
+ * @returns {Function} A function that formats tooltip content
127
+ */
128
+ export function createTooltipFormatter(options = {}) {
129
+ return (d) => formatTooltipContent(d, options)
130
+ }
131
+
132
+ /**
133
+ * Calculates the transform attribute for SVG elements
134
+ *
135
+ * @param {number} x X position
136
+ * @param {number} y Y position
137
+ * @returns {string} Transform attribute value
138
+ */
139
+ export function transform(x, y) {
140
+ return `translate(${x}, ${y})`
141
+ }
142
+
1
143
  import { scaledPath } from '@rokkit/core'
2
144
 
3
145
  export function scaledPathCollection(paths) {
@@ -0,0 +1,31 @@
1
+ import { scaleBand, scaleLinear, scaleTime } from 'd3-scale'
2
+ import { min, max } from 'd3-array'
3
+
4
+ /**
5
+ * Builds an appropriate D3 x-axis scale based on value types.
6
+ * Returns scaleTime for Date values, scaleLinear for numeric values,
7
+ * or scaleBand for categorical values.
8
+ *
9
+ * @param {Array} xValues
10
+ * @param {Object} dimensions
11
+ * @param {number} padding
12
+ * @returns {import('d3-scale').ScaleContinuousNumeric|import('d3-scale').ScaleBand}
13
+ */
14
+ export function buildXScale(xValues, dimensions, padding) {
15
+ const xIsDate = xValues.some((v) => v instanceof Date)
16
+ const xIsNumeric = !xIsDate && xValues.every((v) => !isNaN(parseFloat(v)))
17
+
18
+ if (xIsDate) {
19
+ return scaleTime()
20
+ .domain([min(xValues), max(xValues)])
21
+ .range([0, dimensions.innerWidth])
22
+ .nice()
23
+ }
24
+ if (xIsNumeric) {
25
+ return scaleLinear()
26
+ .domain([min([0, ...xValues]), max(xValues)])
27
+ .range([0, dimensions.innerWidth])
28
+ .nice()
29
+ }
30
+ return scaleBand().domain(xValues).range([0, dimensions.innerWidth]).padding(padding)
31
+ }
@@ -0,0 +1,32 @@
1
+ <script>
2
+ import { getContext } from 'svelte'
3
+ import { toPatternId } from '../lib/brewing/patterns.js'
4
+ import { PATTERNS } from './patterns.js'
5
+ import PatternDef from './PatternDef.svelte'
6
+
7
+ /** @type {{ patterns?: Record<string, import('./patterns.js').PatternMark[]> }} */
8
+ let { patterns = PATTERNS } = $props()
9
+
10
+ const state = getContext('plot-state')
11
+
12
+ const patternDefs = $derived.by(() => {
13
+ const defs = []
14
+ for (const [key, patternName] of (state.patterns ?? new Map()).entries()) {
15
+ const colorEntry = state.colors?.get(key) ?? { stroke: '#444' }
16
+ defs.push({
17
+ id: toPatternId(String(key)),
18
+ marks: patterns[patternName] ?? [],
19
+ stroke: colorEntry.stroke ?? '#444'
20
+ })
21
+ }
22
+ return defs
23
+ })
24
+ </script>
25
+
26
+ {#if patternDefs.length > 0}
27
+ <defs data-plot-pattern-defs>
28
+ {#each patternDefs as def (def.id)}
29
+ <PatternDef id={def.id} marks={def.marks} stroke={def.stroke} />
30
+ {/each}
31
+ </defs>
32
+ {/if}
@@ -0,0 +1,27 @@
1
+ <script>
2
+ import { scaleMark, resolveMarkAttrs } from './scale.js'
3
+
4
+ /** @type {{ id: string, marks?: import('./patterns.js').PatternMark[], size?: number, stroke?: string, thickness?: number }} */
5
+ let { id, marks = [], size = 10, stroke = '#444', thickness = 0.5 } = $props()
6
+
7
+ const resolvedMarks = $derived(
8
+ marks.map((m) => resolveMarkAttrs(scaleMark(m, size), { fill: stroke, stroke, thickness }))
9
+ )
10
+ </script>
11
+
12
+ <pattern {id} patternUnits="userSpaceOnUse" width={size} height={size}>
13
+ <rect width={size} height={size} fill="none" />
14
+ {#each resolvedMarks as { type, attrs }, i (i)}
15
+ {#if type === 'line'}
16
+ <line {...attrs} />
17
+ {:else if type === 'circle'}
18
+ <circle {...attrs} />
19
+ {:else if type === 'rect'}
20
+ <rect {...attrs} />
21
+ {:else if type === 'polygon'}
22
+ <polygon {...attrs} />
23
+ {:else if type === 'path'}
24
+ <path {...attrs} />
25
+ {/if}
26
+ {/each}
27
+ </pattern>
@@ -1,14 +1,4 @@
1
- export { default as Brick } from './Brick.svelte'
2
- export { default as Circles } from './Circles.svelte'
3
- export { default as Dots } from './Dots.svelte'
4
- export { default as CrossHatch } from './CrossHatch.svelte'
5
- export { default as Waves } from './Waves.svelte'
6
- // export { default as Squares } from './Squares.svelte'
7
- export { default as Tile } from './Tile.svelte'
8
- export { default as Triangles } from './Triangles.svelte'
9
- // export { default as Waves } from './Waves.svelte'
10
- // export { default as ZigZag } from './ZigZag.svelte'
11
- // export { default as Flower } from './Flower.svelte'
12
- // export { default as NamedPath } from './NamedPath.svelte'
13
- export { default as CurvedWave } from './CurvedWave.svelte'
14
- export { default as OutlineCircles } from './OutlineCircles.svelte'
1
+ export { default as PatternDef } from './PatternDef.svelte'
2
+ export { default as DefinePatterns } from './DefinePatterns.svelte'
3
+ export { PATTERNS } from './patterns.js'
4
+ export { scaleMark, resolveMarkAttrs } from './scale.js'