@rokkit/chart 1.0.0-next.13 → 1.0.0-next.131

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 (149) hide show
  1. package/README.md +140 -46
  2. package/dist/Plot/index.d.ts +5 -0
  3. package/dist/elements/index.d.ts +6 -0
  4. package/dist/index.d.ts +14 -0
  5. package/dist/lib/brewing/axes.svelte.d.ts +72 -0
  6. package/dist/lib/brewing/bars.svelte.d.ts +54 -0
  7. package/dist/lib/brewing/dimensions.svelte.d.ts +35 -0
  8. package/dist/lib/brewing/index.svelte.d.ts +118 -0
  9. package/dist/lib/brewing/legends.svelte.d.ts +54 -0
  10. package/dist/lib/brewing/scales.svelte.d.ts +29 -0
  11. package/dist/lib/brewing/types.d.ts +162 -0
  12. package/dist/lib/context.d.ts +13 -0
  13. package/dist/lib/scales.svelte.d.ts +35 -0
  14. package/dist/lib/utils.d.ts +58 -0
  15. package/dist/old_lib/brewer.d.ts +9 -0
  16. package/dist/old_lib/chart.d.ts +40 -0
  17. package/dist/old_lib/grid.d.ts +72 -0
  18. package/dist/old_lib/index.d.ts +4 -0
  19. package/dist/old_lib/plots.d.ts +3 -0
  20. package/dist/old_lib/swatch.d.ts +285 -0
  21. package/dist/old_lib/ticks.d.ts +36 -0
  22. package/dist/old_lib/utils.d.ts +1 -0
  23. package/dist/patterns/index.d.ts +9 -0
  24. package/dist/patterns/paths/constants.d.ts +1 -0
  25. package/dist/symbols/constants/index.d.ts +1 -0
  26. package/dist/symbols/index.d.ts +5 -0
  27. package/dist/template/constants.d.ts +43 -0
  28. package/dist/template/shapes/index.d.ts +4 -0
  29. package/package.json +28 -44
  30. package/src/Plot/Axis.svelte +95 -0
  31. package/src/Plot/Bar.svelte +96 -0
  32. package/src/Plot/Grid.svelte +68 -0
  33. package/src/Plot/Legend.svelte +127 -0
  34. package/src/Plot/Root.svelte +107 -0
  35. package/src/Plot/index.js +5 -0
  36. package/src/Symbol.svelte +21 -0
  37. package/src/Texture.svelte +10 -0
  38. package/src/elements/Bar.svelte +22 -24
  39. package/src/elements/ColorRamp.svelte +20 -22
  40. package/src/elements/ContinuousLegend.svelte +20 -17
  41. package/src/elements/DefinePatterns.svelte +24 -0
  42. package/src/elements/DiscreteLegend.svelte +15 -15
  43. package/src/elements/Label.svelte +11 -8
  44. package/src/elements/SymbolGrid.svelte +27 -0
  45. package/src/elements/index.js +6 -0
  46. package/src/examples/BarChartExample.svelte +81 -0
  47. package/src/index.js +18 -16
  48. package/src/lib/brewing/axes.svelte.js +177 -0
  49. package/src/lib/brewing/bars.svelte.js +114 -0
  50. package/src/lib/brewing/dimensions.svelte.js +56 -0
  51. package/src/lib/brewing/index.svelte.js +202 -0
  52. package/src/lib/brewing/legends.svelte.js +94 -0
  53. package/src/lib/brewing/scales.svelte.js +85 -0
  54. package/src/lib/brewing/types.js +73 -0
  55. package/src/lib/context.js +132 -0
  56. package/src/lib/scales.svelte.js +122 -0
  57. package/src/lib/utils.js +96 -120
  58. package/src/old_lib/brewer.js +25 -0
  59. package/src/old_lib/chart.js +213 -0
  60. package/src/old_lib/grid.js +85 -0
  61. package/src/old_lib/index.js +4 -0
  62. package/src/old_lib/plots.js +27 -0
  63. package/src/old_lib/swatch.js +16 -0
  64. package/src/old_lib/ticks.js +46 -0
  65. package/src/old_lib/utils.js +8 -0
  66. package/src/patterns/Brick.svelte +15 -0
  67. package/src/patterns/Circles.svelte +18 -0
  68. package/src/patterns/CrossHatch.svelte +12 -0
  69. package/src/patterns/CurvedWave.svelte +7 -0
  70. package/src/patterns/Dots.svelte +20 -0
  71. package/src/patterns/OutlineCircles.svelte +13 -0
  72. package/src/patterns/Tile.svelte +16 -0
  73. package/src/patterns/Triangles.svelte +13 -0
  74. package/src/patterns/Waves.svelte +9 -0
  75. package/src/patterns/index.js +14 -0
  76. package/src/patterns/paths/NamedPattern.svelte +9 -0
  77. package/src/patterns/paths/constants.js +4 -0
  78. package/src/symbols/RoundedSquare.svelte +22 -0
  79. package/src/symbols/Shape.svelte +24 -0
  80. package/src/symbols/constants/index.js +4 -0
  81. package/src/symbols/index.js +9 -0
  82. package/src/symbols/outline.svelte +60 -0
  83. package/src/symbols/solid.svelte +60 -0
  84. package/src/template/Texture.svelte +13 -0
  85. package/src/template/constants.js +43 -0
  86. package/src/template/shapes/Circles.svelte +13 -0
  87. package/src/template/shapes/Lines.svelte +14 -0
  88. package/src/template/shapes/Path.svelte +9 -0
  89. package/src/template/shapes/Polygons.svelte +9 -0
  90. package/src/template/shapes/index.js +4 -0
  91. package/LICENSE +0 -21
  92. package/src/chart/FacetGrid.svelte +0 -51
  93. package/src/chart/Grid.svelte +0 -34
  94. package/src/chart/Legend.svelte +0 -16
  95. package/src/chart/PatternDefs.svelte +0 -13
  96. package/src/chart/Swatch.svelte +0 -93
  97. package/src/chart/SwatchButton.svelte +0 -29
  98. package/src/chart/SwatchGrid.svelte +0 -55
  99. package/src/chart/Symbol.svelte +0 -37
  100. package/src/chart/Texture.svelte +0 -16
  101. package/src/chart/TexturedShape.svelte +0 -27
  102. package/src/chart/TimelapseChart.svelte +0 -97
  103. package/src/chart/Timer.svelte +0 -27
  104. package/src/chart.js +0 -9
  105. package/src/components/charts/Axis.svelte +0 -66
  106. package/src/components/charts/Chart.svelte +0 -35
  107. package/src/components/index.js +0 -23
  108. package/src/components/lib/axis.js +0 -0
  109. package/src/components/lib/chart.js +0 -187
  110. package/src/components/lib/color.js +0 -327
  111. package/src/components/lib/funnel.js +0 -204
  112. package/src/components/lib/index.js +0 -19
  113. package/src/components/lib/pattern.js +0 -190
  114. package/src/components/lib/rollup.js +0 -55
  115. package/src/components/lib/shape.js +0 -199
  116. package/src/components/lib/summary.js +0 -145
  117. package/src/components/lib/theme.js +0 -23
  118. package/src/components/lib/timer.js +0 -41
  119. package/src/components/lib/utils.js +0 -165
  120. package/src/components/plots/BarPlot.svelte +0 -36
  121. package/src/components/plots/BoxPlot.svelte +0 -54
  122. package/src/components/plots/ScatterPlot.svelte +0 -30
  123. package/src/components/store.js +0 -70
  124. package/src/constants.js +0 -66
  125. package/src/elements/PatternDefs.svelte +0 -13
  126. package/src/elements/PatternMask.svelte +0 -20
  127. package/src/elements/Symbol.svelte +0 -38
  128. package/src/elements/Tooltip.svelte +0 -23
  129. package/src/funnel.svelte +0 -35
  130. package/src/geom.js +0 -105
  131. package/src/lib/axis.js +0 -75
  132. package/src/lib/colors.js +0 -32
  133. package/src/lib/geom.js +0 -4
  134. package/src/lib/shapes.js +0 -144
  135. package/src/lib/timer.js +0 -44
  136. package/src/lookup.js +0 -29
  137. package/src/plots/BarPlot.svelte +0 -55
  138. package/src/plots/BoxPlot.svelte +0 -0
  139. package/src/plots/FunnelPlot.svelte +0 -33
  140. package/src/plots/HeatMap.svelte +0 -5
  141. package/src/plots/HeatMapCalendar.svelte +0 -129
  142. package/src/plots/LinePlot.svelte +0 -55
  143. package/src/plots/Plot.svelte +0 -25
  144. package/src/plots/RankBarPlot.svelte +0 -38
  145. package/src/plots/ScatterPlot.svelte +0 -20
  146. package/src/plots/ViolinPlot.svelte +0 -11
  147. package/src/plots/heatmap.js +0 -70
  148. package/src/plots/index.js +0 -10
  149. package/src/swatch.js +0 -11
@@ -0,0 +1,132 @@
1
+ import { getContext, setContext } from 'svelte'
2
+ import { writable, derived } from 'svelte/store'
3
+ import * as d3 from 'd3'
4
+
5
+ const CHART_CONTEXT = 'chart-context'
6
+
7
+ /**
8
+ * Creates chart context and provides it to child components
9
+ *
10
+ * @param {Object} options Initial chart options
11
+ * @returns {Object} Chart context with all stores and methods
12
+ */
13
+ export function createChartContext(options = {}) {
14
+ // Default config values
15
+ const defaultOptions = {
16
+ width: 600,
17
+ height: 400,
18
+ margin: { top: 20, right: 30, bottom: 40, left: 50 },
19
+ padding: { top: 0, right: 0, bottom: 0, left: 0 },
20
+ responsive: true,
21
+ animationDuration: 300,
22
+ data: []
23
+ }
24
+
25
+ // Merge options with defaults
26
+ const config = { ...defaultOptions, ...options }
27
+
28
+ // Create stores for reactive properties
29
+ const dimensions = writable({
30
+ width: config.width,
31
+ height: config.height,
32
+ margin: { ...config.margin },
33
+ padding: { ...config.padding }
34
+ })
35
+
36
+ const data = writable(config.data)
37
+ const scales = writable({})
38
+
39
+ // Compute inner dimensions (subtracting margins)
40
+ const innerDimensions = derived(dimensions, ($dimensions) => {
41
+ return {
42
+ width:
43
+ $dimensions.width -
44
+ $dimensions.margin.left -
45
+ $dimensions.margin.right -
46
+ $dimensions.padding.left -
47
+ $dimensions.padding.right,
48
+ height:
49
+ $dimensions.height -
50
+ $dimensions.margin.top -
51
+ $dimensions.margin.bottom -
52
+ $dimensions.padding.top -
53
+ $dimensions.padding.bottom
54
+ }
55
+ })
56
+
57
+ // Store for plot elements (bars, lines, etc.)
58
+ const plots = writable([])
59
+
60
+ // Store for axes
61
+ const axes = writable({
62
+ x: null,
63
+ y: null
64
+ })
65
+
66
+ const legend = writable({
67
+ enabled: false,
68
+ items: []
69
+ })
70
+
71
+ // Helper to add a new plot
72
+ function addPlot(plot) {
73
+ plots.update((currentPlots) => [...currentPlots, plot])
74
+ return () => {
75
+ plots.update((currentPlots) => currentPlots.filter((p) => p !== plot))
76
+ }
77
+ }
78
+
79
+ // Helper to update scales based on data and dimensions
80
+ function updateScales(xKey, yKey, colorKey = null) {
81
+ return derived([data, innerDimensions], ([$data, $innerDimensions]) => {
82
+ if (!$data || $data.length === 0) return null
83
+
84
+ const xScale = d3
85
+ .scaleBand()
86
+ .domain($data.map((d) => d[xKey]))
87
+ .range([0, $innerDimensions.width])
88
+ .padding(0.2)
89
+
90
+ const yScale = d3
91
+ .scaleLinear()
92
+ .domain([0, d3.max($data, (d) => d[yKey])])
93
+ .nice()
94
+ .range([$innerDimensions.height, 0])
95
+
96
+ let colorScale = null
97
+
98
+ if (colorKey) {
99
+ const uniqueCategories = [...new Set($data.map((d) => d[colorKey]))]
100
+ colorScale = d3.scaleOrdinal().domain(uniqueCategories).range(d3.schemeCategory10)
101
+ }
102
+
103
+ return { xScale, yScale, colorScale }
104
+ })
105
+ }
106
+
107
+ // Create and set context
108
+ const chartContext = {
109
+ dimensions,
110
+ innerDimensions,
111
+ data,
112
+ scales,
113
+ plots,
114
+ axes,
115
+ legend,
116
+ addPlot,
117
+ updateScales
118
+ }
119
+
120
+ setContext(CHART_CONTEXT, chartContext)
121
+
122
+ return chartContext
123
+ }
124
+
125
+ /**
126
+ * Gets chart context provided by parent component
127
+ *
128
+ * @returns {Object} Chart context
129
+ */
130
+ export function getChartContext() {
131
+ return getContext(CHART_CONTEXT)
132
+ }
@@ -0,0 +1,122 @@
1
+ import { scaleBand, scaleLinear, scaleTime, scaleOrdinal } from 'd3-scale'
2
+ import { schemeCategory10 } from 'd3-scale-chromatic'
3
+ import { min, max } from 'd3-array'
4
+
5
+ /**
6
+ * Creates appropriate scales based on data and dimensions
7
+ *
8
+ * @param {Array} data The dataset
9
+ * @param {string} xKey Field to use for x-axis
10
+ * @param {string} yKey Field to use for y-axis
11
+ * @param {Object} dimensions Chart dimensions
12
+ * @param {Object} options Additional options
13
+ * @returns {Object} Object containing xScale, yScale, and colorScale
14
+ */
15
+ export function createScales(data, xKey, yKey, dimensions, options = {}) {
16
+ if (!data || data.length === 0) return {}
17
+
18
+ const { colorKey = null, padding = 0.2 } = options
19
+
20
+ // Determine if x values are numeric, dates, or categorical
21
+ const xValues = data.map((d) => d[xKey])
22
+ const xIsDate = xValues.some((v) => v instanceof Date)
23
+ const xIsNumeric = !xIsDate && xValues.every((v) => !isNaN(parseFloat(v)))
24
+
25
+ // Create x-scale based on data type
26
+ let xScale
27
+ if (xIsDate) {
28
+ xScale = scaleTime()
29
+ .domain([min(xValues), max(xValues)])
30
+ .range([0, dimensions.innerWidth])
31
+ .nice()
32
+ } else if (xIsNumeric) {
33
+ xScale = scaleLinear()
34
+ .domain([min([0, ...xValues]), max(xValues)])
35
+ .range([0, dimensions.innerWidth])
36
+ .nice()
37
+ } else {
38
+ xScale = scaleBand().domain(xValues).range([0, dimensions.innerWidth]).padding(padding)
39
+ }
40
+
41
+ // Create y-scale
42
+ const yValues = data.map((d) => d[yKey])
43
+ const yScale = scaleLinear()
44
+ .domain([0, max(yValues) * 1.1]) // Add 10% padding on top
45
+ .nice()
46
+ .range([dimensions.innerHeight, 0])
47
+
48
+ // Create color scale if colorKey is provided
49
+ let colorScale = null
50
+ if (colorKey) {
51
+ const uniqueCategories = [...new Set(data.map((d) => d[colorKey]))]
52
+ colorScale = scaleOrdinal().domain(uniqueCategories).range(schemeCategory10)
53
+ }
54
+
55
+ return { xScale, yScale, colorScale }
56
+ }
57
+
58
+ /**
59
+ * Calculates the actual chart dimensions after applying margins
60
+ *
61
+ * @param {Object} dimensions Original dimensions
62
+ * @returns {Object} Dimensions with calculated inner width and height
63
+ */
64
+ export function calculateChartDimensions(width, height, margin) {
65
+ return {
66
+ width,
67
+ height,
68
+ margin,
69
+ innerWidth: width - margin.left - margin.right,
70
+ innerHeight: height - margin.top - margin.bottom
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Gets the axis origin value
76
+ *
77
+ * @param {Object} scale D3 scale
78
+ * @returns {number} Origin value
79
+ */
80
+ export function getOriginValue(scale) {
81
+ return scale.ticks ? scale(Math.max(0, min(scale.domain()))) : scale.range()[0]
82
+ }
83
+
84
+ /**
85
+ * Creates axis ticks
86
+ *
87
+ * @param {Object} scale D3 scale
88
+ * @param {string} axis Axis type ('x' or 'y')
89
+ * @param {number} count Number of ticks
90
+ * @param {number} fontSize Font size for determining tick density
91
+ * @returns {Array} Array of tick objects
92
+ */
93
+ export function createTicks(scale, axis, count = null, fontSize = 12) {
94
+ const [minRange, maxRange] = scale.range()
95
+ let ticks = []
96
+ let offset = 0
97
+
98
+ // Calculate default count based on available space
99
+ if (!count) {
100
+ count = Math.abs((maxRange - minRange) / (fontSize * (axis === 'y' ? 3 : 6)))
101
+ }
102
+
103
+ // Get ticks based on scale type
104
+ if (scale.ticks) {
105
+ ticks = scale.ticks(Math.round(count))
106
+ } else {
107
+ offset = scale.bandwidth() / 2
108
+ count = Math.min(Math.round(count), scale.domain().length)
109
+
110
+ ticks = scale.domain()
111
+ if (count < scale.domain().length) {
112
+ const step = Math.ceil(scale.domain().length / count)
113
+ ticks = ticks.filter((_, i) => i % step === 0)
114
+ }
115
+ }
116
+
117
+ // Format ticks with positions
118
+ return ticks.map((t) => ({
119
+ value: t,
120
+ position: scale(t) + (axis === 'x' ? offset : 0)
121
+ }))
122
+ }
package/src/lib/utils.js CHANGED
@@ -1,157 +1,133 @@
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 {string} xKey Field to use for x-axis
10
+ * @param {string} yKey Field to use for y-axis
11
+ * @param {Object} dimensions Chart dimensions
12
+ * @param {Object} [options] Additional options
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, xKey, yKey, dimensions, options = {}) {
17
+ if (!data || data.length === 0) return {}
18
+
19
+ const xScale = scaleBand()
20
+ .domain(data.map((d) => d[xKey]))
21
+ .range([0, dimensions.width])
22
+ .padding(0.2)
30
23
 
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
- }))
24
+ const yScale = scaleLinear()
25
+ .domain([0, max(data, (d) => d[yKey]) * 1.1]) // Add 10% padding on top
26
+ .nice()
27
+ .range([dimensions.height, 0])
28
+
29
+ let colorScale = null
30
+
31
+ if (options.colorKey) {
32
+ const uniqueCategories = [...new Set(data.map((d) => d[options.colorKey]))]
33
+ colorScale = scaleOrdinal().domain(uniqueCategories).range(schemeCategory10)
34
+ }
38
35
 
39
- return { width, height, data }
36
+ return { xScale, yScale, colorScale }
40
37
  }
38
+
41
39
  /**
42
- * Get a scale function mapping the values between a range of lower and upper values
40
+ * Calculates the actual chart dimensions after applying margins
43
41
  *
44
- * @param {Array} values
45
- * @param {Array[2]} bounds
46
- * @param {number} buffer
47
- * @returns
42
+ * @param {Object} dimensions Original dimensions
43
+ * @returns {Object} Dimensions with calculated inner width and height
48
44
  */
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)
45
+ export function calculateChartDimensions(dimensions) {
46
+ const { width, height, margin } = dimensions
47
+
48
+ return {
49
+ ...dimensions,
50
+ innerWidth: width - margin.left - margin.right,
51
+ innerHeight: height - margin.top - margin.bottom
66
52
  }
67
53
  }
54
+
68
55
  /**
69
- * Obtain the scale function for the `x` and `y` fields in the data set.
56
+ * Normalizes data for use with D3 charts
70
57
  *
71
- * @param {array<dict>} data
72
- * @param {string} x
73
- * @param {string} y
74
- * @param {number} width
75
- * @param {number} height
76
- * @returns
58
+ * @param {Array|Object} inputData Raw data or dataset object
59
+ * @returns {Array} Normalized data array
77
60
  */
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]))]
61
+ export function normalizeData(inputData) {
62
+ if (!inputData) return []
81
63
 
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)
64
+ // If it's a dataset class instance, call select() to get the data
65
+ if (inputData.select && typeof inputData.select === 'function') {
66
+ return inputData.select()
85
67
  }
68
+
69
+ // If it's already an array, return as is
70
+ if (Array.isArray(inputData)) {
71
+ return inputData
72
+ }
73
+
74
+ return []
86
75
  }
87
76
 
88
77
  /**
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
78
+ * Generates a unique ID for SVG elements
91
79
  *
92
- * @param {*} data
93
- * @param {*} x
94
- * @param {*} y
95
- * @returns
80
+ * @param {string} prefix Prefix for the ID
81
+ * @returns {string} A unique ID
96
82
  */
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
83
+ export function uniqueId(prefix = 'chart') {
84
+ return `${prefix}-${Math.random().toString(36).substring(2, 10)}`
112
85
  }
113
86
 
114
87
  /**
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
88
+ * Formats tooltip content for a data point
117
89
  *
118
- * @param {Array} values
119
- * @param {Array} palette
120
- * @param {*} fallback
121
- * @returns
90
+ * @param {Object} d Data point
91
+ * @param {Object} options Tooltip format options
92
+ * @returns {string} Formatted tooltip HTML content
122
93
  */
123
- export function getPaletteForValues(values, palette, fallback) {
124
- return values.map((_, index) =>
125
- index < palette.length ? palette[index] : fallback
126
- )
94
+ export function formatTooltipContent(d, options = {}) {
95
+ if (!d) return ''
96
+
97
+ const { xKey, yKey, xFormat, yFormat } = options
98
+
99
+ if (xKey && yKey) {
100
+ const xValue = d[xKey]
101
+ const yValue = d[yKey]
102
+
103
+ const xFormatted = xFormat ? xFormat(xValue) : xValue
104
+ const yFormatted = yFormat ? yFormat(yValue) : yValue
105
+
106
+ return `${xKey}: ${xFormatted}<br>${yKey}: ${yFormatted}`
107
+ }
108
+
109
+ return Object.entries(d)
110
+ .map(([key, value]) => `${key}: ${value}`)
111
+ .join('<br>')
127
112
  }
128
113
 
129
114
  /**
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.
115
+ * Generates a tooltip formatter function
133
116
  *
134
- * @param {Array<Object>} data
135
- * @param {string} key
136
- * @param {string} label
137
- * @returns
117
+ * @param {Object} options Tooltip format options
118
+ * @returns {Function} A function that formats tooltip content
138
119
  */
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])))
120
+ export function createTooltipFormatter(options = {}) {
121
+ return (d) => formatTooltipContent(d, options)
144
122
  }
123
+
145
124
  /**
146
- * Repeats array items of b using array items of a ask keys
125
+ * Calculates the transform attribute for SVG elements
147
126
  *
148
- * @param {Array} b
149
- * @param {Array} a
150
- * @returns {Object} with keys as items in a and values as items in b
127
+ * @param {number} x X position
128
+ * @param {number} y Y position
129
+ * @returns {string} Transform attribute value
151
130
  */
152
- export function repeatAcross(b, a) {
153
- return a.reduce(
154
- (acc, item, index) => ({ ...acc, [item]: b[index % b.length] }),
155
- {}
156
- )
131
+ export function transform(x, y) {
132
+ return `translate(${x}, ${y})`
157
133
  }
@@ -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() {}