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

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
package/src/lib/utils.js CHANGED
@@ -1,157 +1,144 @@
1
- import { scaleBand, scaleLinear } from 'd3-scale'
2
- import { min, max } from 'd3-array'
3
- import { ascending, quantile } from 'd3-array'
4
- import { nest } from 'd3-collection'
5
- import { omit } from 'ramda'
1
+ import { scaleBand, scaleLinear, scaleOrdinal } from 'd3-scale'
2
+ import { schemeCategory10 } from 'd3-scale-chromatic'
3
+ import { max } from 'd3-array'
6
4
 
7
5
  /**
8
- * Calculates a grid of centres to fit a list of items of `size` within the number of `columns` and `rows`.
6
+ * Creates appropriate scales based on data and dimensions
9
7
  *
10
- * - Attempts to find a best fit square if both columns and rows are not specified
11
- * - Value in columns is prioritized over rows for recalculating the grid
12
- * - Supports padding between the items
13
- *
14
- * @param {number} count
15
- * @param {number} size
16
- * @param {number} pad
17
- * @param {number} columns
18
- * @param {number} rows
19
- * @returns
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
20
15
  */
21
- export function swatch(count, size, pad = 0, columns, rows) {
22
- if (columns > 0) {
23
- rows = Math.ceil(count / columns)
24
- } else if (rows > 0) {
25
- columns = Math.ceil(count / rows)
26
- } else {
27
- columns = Math.ceil(Math.sqrt(count))
28
- rows = Math.ceil(count / columns)
29
- }
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)
30
25
 
31
- const width = (size + pad) * columns + pad
32
- const height = (size + pad) * rows + pad
33
- const data = [...Array(count).keys()].map((index) => ({
34
- cx: (size + pad) / 2 + (index % columns) * (size + pad),
35
- cy: (size + pad) / 2 + Math.floor(index / columns) * (size + pad),
36
- r: size / 2
37
- }))
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
+ }
38
37
 
39
- return { width, height, data }
38
+ return { xScale, yScale, colorScale }
40
39
  }
40
+
41
41
  /**
42
- * Get a scale function mapping the values between a range of lower and upper values
42
+ * Calculates the actual chart dimensions after applying margins
43
43
  *
44
- * @param {Array} values
45
- * @param {Array[2]} bounds
46
- * @param {number} buffer
47
- * @returns
44
+ * @param {Object} dimensions Original dimensions
45
+ * @returns {Object} Dimensions with calculated inner width and height
48
46
  */
49
- export function getScale(values, bounds, buffer = 0, ordinal = false) {
50
- if (ordinal || values.some(isNaN)) {
51
- return scaleBand().range(bounds).domain(values).padding(0.5)
52
- } else {
53
- values = values.map((x) => +x)
54
-
55
- let minValue = min([...values, 0])
56
- let maxValue = max(values)
57
-
58
- if (minValue < 0 && maxValue > 0) {
59
- maxValue = max([-1 * minValue, maxValue])
60
- minValue = -1 * maxValue
61
- }
62
- const margin = (maxValue - minValue) * buffer
63
- return scaleLinear()
64
- .domain([minValue - margin, maxValue + margin])
65
- .range(bounds)
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
66
54
  }
67
55
  }
56
+
68
57
  /**
69
- * Obtain the scale function for the `x` and `y` fields in the data set.
58
+ * Normalizes data for use with D3 charts
70
59
  *
71
- * @param {array<dict>} data
72
- * @param {string} x
73
- * @param {string} y
74
- * @param {number} width
75
- * @param {number} height
76
- * @returns
60
+ * @param {Array|Object} inputData Raw data or dataset object
61
+ * @returns {Array} Normalized data array
77
62
  */
78
- export function getScales(data, x, y, width, height, margin, ordinal) {
79
- const xValues = [...new Set(data.map((item) => item[x]))]
80
- const yValues = [...new Set(data.map((item) => item[y]))]
63
+ export function normalizeData(inputData) {
64
+ if (!inputData) return []
81
65
 
82
- return {
83
- x: getScale(xValues, [0 + margin.left, width - margin.right], 0, ordinal.x),
84
- y: getScale(yValues, [height - margin.top, margin.bottom], 0.1, ordinal.y)
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
85
74
  }
75
+
76
+ return []
86
77
  }
87
78
 
88
79
  /**
89
- * Summarize `data` by fields `x` and `y` and return a nested array with
90
- * key as unique `x` values and value as statistical summaries of `y` values
80
+ * Generates a unique ID for SVG elements
91
81
  *
92
- * @param {*} data
93
- * @param {*} x
94
- * @param {*} y
95
- * @returns
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}
96
95
  */
97
- export function aggregate(data, x, y) {
98
- const summary = nest()
99
- .key((d) => d[x])
100
- .rollup((d) => {
101
- let values = d.map((g) => g[y]).sort(ascending)
102
- let q1 = quantile(values, 0.25)
103
- let q3 = quantile(values, 0.75)
104
- let median = quantile(values, 0.5)
105
- let interQuantileRange = q3 - q1
106
- let min = q1 - 1.5 * interQuantileRange
107
- let max = q3 + 1.5 * interQuantileRange
108
- return { q1, q3, median, interQuantileRange, min, max }
109
- })
110
- .entries(data)
111
- return summary
96
+ function formatField(key, value, formatter) {
97
+ const formatted = formatter ? formatter(value) : value
98
+ return `${key}: ${formatted}`
112
99
  }
113
100
 
114
101
  /**
115
- * Generate a palette with same size as input data containing values from palette array.
116
- * After the palette array is exhausted the fallback value is used
102
+ * Formats tooltip content for a data point
117
103
  *
118
- * @param {Array} values
119
- * @param {Array} palette
120
- * @param {*} fallback
121
- * @returns
104
+ * @param {Object} d Data point
105
+ * @param {Object} options Tooltip format options
106
+ * @returns {string} Formatted tooltip HTML content
122
107
  */
123
- export function getPaletteForValues(values, palette, fallback) {
124
- return values.map((_, index) =>
125
- index < palette.length ? palette[index] : fallback
126
- )
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 [
115
+ formatField(xKey, d[xKey], xFormat),
116
+ formatField(yKey, d[yKey], yFormat)
117
+ ].join('<br>')
118
+ }
119
+
120
+ return Object.entries(d)
121
+ .map(([key, value]) => `${key}: ${value}`)
122
+ .join('<br>')
127
123
  }
128
124
 
129
125
  /**
130
- * Converts input object array into a nested key,value array.
131
- * 'key' contains unique values for the attribute specified by the key parameter
132
- * and value contains array of all remaining attributes in an array.
126
+ * Generates a tooltip formatter function
133
127
  *
134
- * @param {Array<Object>} data
135
- * @param {string} key
136
- * @param {string} label
137
- * @returns
128
+ * @param {Object} options Tooltip format options
129
+ * @returns {Function} A function that formats tooltip content
138
130
  */
139
- export function toNested(data, key, label) {
140
- return nest()
141
- .key((d) => d[key])
142
- .rollup((values) => values.map((value) => omit([key], value)))
143
- .entries(data.sort((a, b) => ascending(a[label], b[label])))
131
+ export function createTooltipFormatter(options = {}) {
132
+ return (d) => formatTooltipContent(d, options)
144
133
  }
134
+
145
135
  /**
146
- * Repeats array items of b using array items of a ask keys
136
+ * Calculates the transform attribute for SVG elements
147
137
  *
148
- * @param {Array} b
149
- * @param {Array} a
150
- * @returns {Object} with keys as items in a and values as items in b
138
+ * @param {number} x X position
139
+ * @param {number} y Y position
140
+ * @returns {string} Transform attribute value
151
141
  */
152
- export function repeatAcross(b, a) {
153
- return a.reduce(
154
- (acc, item, index) => ({ ...acc, [item]: b[index % b.length] }),
155
- {}
156
- )
142
+ export function transform(x, y) {
143
+ return `translate(${x}, ${y})`
157
144
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Get fill patterns for a set of values
3
+ *
4
+ * @param {Array} values - Array of values
5
+ * @param {Object} swatch - Object with keys for color, gray, and pattern
6
+ * @param {Boolean} gray - Boolean to determine if gray or color
7
+ * @returns {Object} - Object with keys for pattern and color
8
+ */
9
+ export function getFillPatterns(values, swatch, gray = false) {
10
+ const colors = gray ? swatch.keys.gray : swatch.keys.color
11
+ const max_colors = colors.length
12
+ const max_patterns = swatch.keys.pattern.length
13
+
14
+ const mix = values
15
+ .map((value, index) => ({
16
+ [value]: {
17
+ pattern: swatch.keys.pattern[index % max_patterns],
18
+ color: colors[index % max_colors]
19
+ }
20
+ }))
21
+ .reduce((acc, current) => ({ ...acc, ...current }), {})
22
+ return mix
23
+ }
24
+
25
+ // export function getStrokePatterns() {}
@@ -0,0 +1,213 @@
1
+ import { min, max } from 'd3-array'
2
+ import { scaleBand, scaleLinear, scaleTime } from 'd3-scale'
3
+
4
+ function getOriginValue(scale) {
5
+ return scale.ticks ? scale(Math.max(0, Math.min(...scale.domain()))) : scale.range()[0]
6
+ }
7
+
8
+ function getScale(domain, range, padding = 0) {
9
+ if (domain.some(isNaN)) {
10
+ return scaleBand().domain(domain).range(range).padding(padding)
11
+ } else if (domain[0] instanceof Date) {
12
+ return scaleTime()
13
+ .domain([min(domain), max(domain)])
14
+ .range(range)
15
+ .nice()
16
+ }
17
+
18
+ return scaleLinear()
19
+ .domain([min([0, ...domain]), max([0, ...domain])])
20
+ .range(range)
21
+ .nice()
22
+ }
23
+
24
+ class Chart {
25
+ // data = []
26
+ // width = 512
27
+ // height = 512
28
+ // origin = { x: 0, y: 0 }
29
+ // range = {
30
+ // x: [0, this.width],
31
+ // y: [this.height, 0]
32
+ // }
33
+ // x
34
+ // y
35
+ // stat = 'identity'
36
+ // scale
37
+ // fill
38
+ // color
39
+ // value
40
+ // shape
41
+ // valueFormat
42
+ // valueLabel
43
+ // domain
44
+ // margin
45
+ // spacing
46
+ // padding
47
+ // flipCoords = false
48
+
49
+ constructor(data, opts) {
50
+ this.width = Number(opts.width) || 2048
51
+ this.height = Number(opts.height) || 2048
52
+ this.flipCoords = opts.flipCoords || false
53
+ this.x = opts.x
54
+ this.y = opts.y
55
+ this.value = opts.value || opts.y
56
+ this.valueLabel = opts.valueLabel || this.value
57
+ this.valueFormat = opts.valueFormat || ((d) => d)
58
+ this.fill = opts.fill || opts.x
59
+ this.color = opts.color || opts.fill
60
+ this.shape = opts.shape || opts.fill
61
+
62
+ this.padding = opts.padding !== undefined ? Number(opts.padding) : 32
63
+
64
+ this.spacing =
65
+ Number(opts.spacing) >= 0 && Number(opts.spacing) <= 0.5 ? Number(opts.spacing) : 0
66
+ this.margin = {
67
+ top: Number(opts.margin?.top) || 0,
68
+ left: Number(opts.margin?.left) || 0,
69
+ right: Number(opts.margin?.right) || 0,
70
+ bottom: Number(opts.margin?.bottom) || 0
71
+ }
72
+ this.domain = {
73
+ x: [...new Set(data.map((d) => d[this.x]))],
74
+ y: [...new Set(data.map((d) => d[this.y]))]
75
+ }
76
+ if (this.flipCoords) {
77
+ this.domain = { y: this.domain.x, x: this.domain.y }
78
+ }
79
+ this.stat = opts.stat || 'identity'
80
+
81
+ this.data = data.map((d) => ({
82
+ x: this.flipCoords ? d[this.y] : d[this.x],
83
+ y: this.flipCoords ? d[this.x] : d[this.y],
84
+ // fill: d[this.fill],
85
+ color: d[this.color]
86
+ // shape: d[this.shape]
87
+ }))
88
+
89
+ this.refresh()
90
+ }
91
+
92
+ padding(value) {
93
+ this.padding = value
94
+ return this.refresh()
95
+ }
96
+
97
+ margin(value) {
98
+ this.margin = value
99
+ return this.refresh()
100
+ }
101
+
102
+ refresh() {
103
+ this.range = {
104
+ x: [this.margin.left + this.padding, this.width - this.margin.right - this.padding],
105
+ y: [this.height - this.padding - this.margin.bottom, this.margin.top + this.padding]
106
+ }
107
+
108
+ const scale = {
109
+ x: getScale(this.domain.x, this.range.x, this.spacing),
110
+ y: getScale(this.domain.y, this.range.y, this.spacing)
111
+ }
112
+
113
+ // scale['value'] = this.value === this.x ? scale.x : scale.y
114
+
115
+ this.origin = {
116
+ x: getOriginValue(scale.x),
117
+ y: getOriginValue(scale.y)
118
+ }
119
+
120
+ this.scale = scale
121
+
122
+ return this
123
+ }
124
+
125
+ // get scale() {
126
+ // return this.scale
127
+ // }
128
+ // get origin() {
129
+ // return this.origin
130
+ // }
131
+ // get margin() {
132
+ // return this.margin
133
+ // }
134
+ // get range() {
135
+ // const [x1, x2] = this.scale.x.range()
136
+ // const [y1, y2] = this.scale.y.range()
137
+
138
+ // return { x1, y1, x2, y2 }
139
+ // }
140
+ // get data() {
141
+ // // aggregate data group by x,y,fill,shape, color
142
+ // // stat = [min, max, avg, std, q1, q3, median, sum, count, box, all]
143
+
144
+ // return this.data
145
+ // }
146
+ // get width() {
147
+ // return this.width
148
+ // }
149
+ // get height() {
150
+ // return this.height
151
+ // }
152
+ // set width(value) {
153
+ // this.width = value
154
+ // }
155
+ // set height(value) {
156
+ // this.height = value
157
+ // }
158
+ // get domain() {
159
+ // return this.domain
160
+ // }
161
+ // get flipCoords() {
162
+ // return this.flipCoords
163
+ // }
164
+ aggregate(value, stat) {
165
+ this.value = value
166
+ this.stat = stat
167
+
168
+ // this.data = nest(this.data)
169
+ }
170
+
171
+ ticks(axis, count, fontSize = 8) {
172
+ const scale = this.scale[axis]
173
+ const [minRange, maxRange] = scale.range()
174
+ let ticks = []
175
+ let offset = 0
176
+
177
+ count = count || Math.abs((maxRange - minRange) / (fontSize * (axis === 'y' ? 8 : 8)))
178
+
179
+ if (scale.ticks) {
180
+ ticks = scale.ticks(Math.round(count))
181
+ } else {
182
+ offset = scale.bandwidth() / 2
183
+ count = Math.min(Math.round(count), scale.domain().length)
184
+
185
+ ticks = scale.domain()
186
+ if (count < scale.domain().length) {
187
+ const diff = scale.domain().length - count
188
+ ticks = ticks.filter((d, i) => i % diff === 0)
189
+ }
190
+ }
191
+
192
+ ticks = ticks
193
+ .map((t) => ({
194
+ label: t,
195
+ pos: scale(t)
196
+ }))
197
+ .map(({ label, pos }) => ({
198
+ label,
199
+ offset: {
200
+ x: axis === 'x' ? offset : 0,
201
+ y: axis === 'y' ? offset : 0
202
+ },
203
+ x: axis === 'x' ? pos : this.origin.x,
204
+ y: axis === 'y' ? pos : this.origin.y
205
+ }))
206
+
207
+ return ticks
208
+ }
209
+ }
210
+
211
+ export function chart(data, aes) {
212
+ return new Chart(data, aes)
213
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @typedef GridPoint
3
+ * @property {number} x - x-coordinate of the point
4
+ * @property {number} y - y-coordinate of the point
5
+ * @property {number} r - radius of the point
6
+ */
7
+
8
+ /**
9
+ * @typedef SwatchGrid
10
+ * @property {number} width - width of the grid
11
+ * @property {number} height - height of the grid
12
+ * @property {GridPoint[]} data - data points of the grid
13
+ */
14
+
15
+ /**
16
+ * @tyoedef {Object} GridOptions
17
+ * @property {number} [pad=0] - The padding between the items
18
+ * @property {number} [columns=0] - The number of columns
19
+ * @property {number} [rows=0] - The number of rows
20
+ */
21
+
22
+ /**
23
+ * Calculates a grid of centres to fit a list of items of `size` within the number of `columns` and `rows`.
24
+ *
25
+ * - Attempts to find a best fit square if both columns and rows are not specified
26
+ * - Value in columns is prioritized over rows for recalculating the grid
27
+ * - Supports padding between the items
28
+ *
29
+ * @param {number} count - number of items
30
+ * @param {number} size - size of the items
31
+ * @param {GridOptions} options - options for the grid
32
+ * @returns {SwatchGrid}
33
+ */
34
+ export function swatchGrid(count, size, options) {
35
+ const { pad = 0 } = options || {}
36
+ let { columns = 0, rows = 0 } = options || {}
37
+ if (columns > 0) {
38
+ rows = Math.ceil(count / columns)
39
+ } else if (rows > 0) {
40
+ columns = Math.ceil(count / rows)
41
+ } else {
42
+ columns = Math.ceil(Math.sqrt(count))
43
+ rows = Math.ceil(count / columns)
44
+ }
45
+
46
+ const width = (size + pad) * columns + pad
47
+ const height = (size + pad) * rows + pad
48
+ const radius = size / 2
49
+ const data = [...Array(count).keys()].map((index) => ({
50
+ x: pad + radius + (index % columns) * (size + pad),
51
+ y: pad + radius + Math.floor(index / columns) * (size + pad),
52
+ r: radius
53
+ }))
54
+
55
+ return { width, height, data }
56
+ }
57
+
58
+ /**
59
+ * Spreads values as patterns with colors from a palette
60
+ *
61
+ * @param {number[]} values - values to spread
62
+ * @param {string[]} patterns - patterns to spread
63
+ * @param {string[]} palette - colors to spread
64
+ * @returns {Record<number, { id: string, pattern: string, color: string }>}
65
+ */
66
+ export function spreadValuesAsPatterns(values, patterns, palette) {
67
+ const result = values
68
+ .map((value, index) => ({
69
+ pattern: patterns[index % patterns.length],
70
+ color: palette[index % palette.length],
71
+ value
72
+ }))
73
+ .reduce(
74
+ (acc, { value, pattern, color }) => ({
75
+ ...acc,
76
+ [value]: {
77
+ id: `${pattern}_${color}`,
78
+ pattern,
79
+ color
80
+ }
81
+ }),
82
+ {}
83
+ )
84
+ return result
85
+ }
@@ -0,0 +1,4 @@
1
+ export { swatch } from './swatch'
2
+ export { swatchGrid } from './grid'
3
+ export * from './chart'
4
+ export * from './brewer'
@@ -0,0 +1,27 @@
1
+ // skipcq: JS-C1003 - Importing all plots from observablehq/plot
2
+ import * as Plot from '@observablehq/plot'
3
+
4
+ export function plotter(node, options) {
5
+ let { data, aes, type, opts } = options
6
+ // let chart = Plot.lineY(options.data).plot({ grid: true })
7
+
8
+ function render() {
9
+ const chart = Plot[type](data, aes).plot({
10
+ color: { scheme: 'turbo', range: [0.1, 0.9] },
11
+ ...opts
12
+ })
13
+ node.firstChild?.remove() // remove old chart, if any
14
+ node.append(chart)
15
+ }
16
+ render()
17
+
18
+ return {
19
+ update: (input) => {
20
+ data = input.data
21
+ aes = input.aes
22
+ type = input.type
23
+ opts = input.opts
24
+ render()
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,16 @@
1
+ import { writable } from 'svelte/store'
2
+ import palette from './palette.json'
3
+ // skipcq: JS-C1003 - Importing all patterns
4
+ import * as patterns from '../patterns'
5
+ import { shapes } from '../symbols'
6
+
7
+ export const swatch = writable({
8
+ palette,
9
+ patterns,
10
+ keys: {
11
+ gray: ['gray'],
12
+ color: Object.keys(palette).filter((name) => name !== 'gray'),
13
+ symbol: shapes,
14
+ pattern: Object.keys(patterns)
15
+ }
16
+ })
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @typedef {Object} TickSteps
3
+ * @property {number} major - count of major ticks
4
+ * @property {number} minor - count of minor ticks
5
+ */
6
+
7
+ /**
8
+ * Generate an array of ticks for a given axis and the tick type
9
+ *
10
+ * @param {number} lower - The lower bound of the axis
11
+ * @param {number} upper - The upper bound of the axis
12
+ * @param {TickSteps} steps - The number of steps between major and minor ticks
13
+ * @param {string} type - The type of tick to generate
14
+ *
15
+ * @returns {Array} - An array of objects representing the ticks
16
+ */
17
+ export function ticksByType(lower, upper, steps, type) {
18
+ if (steps <= 0) return []
19
+ return Array.from({ length: Math.floor((upper - lower) / steps) + 1 }, (_, i) => ({
20
+ position: i * steps,
21
+ label: i * steps,
22
+ type
23
+ })).filter((tick) => tick.position > lower && tick.position < upper)
24
+ }
25
+
26
+ /**
27
+ * Generate an array of ticks for a given axis
28
+ *
29
+ * @param {number} lower - The lower bound of the axis
30
+ * @param {number} upper - The upper bound of the axis
31
+ * @param {TickSteps} steps - The number of steps between major and minor ticks
32
+ *
33
+ * @returns {Array} - An array of objects representing the ticks
34
+ */
35
+ export function getTicks(lower, upper, steps = { major: 10, minor: 0 }) {
36
+ const majorTicks = ticksByType(lower, upper, steps.major, 'major')
37
+ const minorTicks = ticksByType(lower, upper, steps.minor, 'minor').filter(
38
+ (tick) => !majorTicks.find((major) => major.position === tick.position)
39
+ )
40
+ const end = [
41
+ { position: upper, label: upper, type: 'end' },
42
+ { position: lower, label: lower, type: 'end' }
43
+ ]
44
+
45
+ return [...minorTicks, ...majorTicks, ...end].sort((a, b) => a.position - b.position)
46
+ }