@rokkit/chart 1.0.0-next.136 → 1.0.0-next.138

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.
@@ -1,9 +1,3 @@
1
- /**
2
- * @typedef {import('./types').TickData} TickData
3
- * @typedef {import('./types').AxisData} AxisData
4
- * @typedef {import('./types').ChartScales} ChartScales
5
- * @typedef {import('./types').ChartDimensions} ChartDimensions
6
- */
7
1
  /**
8
2
  * Creates x-axis tick data for rendering
9
3
  *
@@ -18,7 +12,7 @@
18
12
  */
19
13
  export function createXAxis(scales: {
20
14
  x: Function;
21
- }, dimensions: Object, options?: {
15
+ }, dimensions: Object, options: {
22
16
  tickCount?: number | undefined;
23
17
  tickFormat?: Function | undefined;
24
18
  label?: string | undefined;
@@ -37,7 +31,7 @@ export function createXAxis(scales: {
37
31
  */
38
32
  export function createYAxis(scales: {
39
33
  y: Function;
40
- }, dimensions: Object, options?: {
34
+ }, dimensions: Object, options: {
41
35
  tickCount?: number | undefined;
42
36
  tickFormat?: Function | undefined;
43
37
  label?: string | undefined;
@@ -53,7 +47,7 @@ export function createYAxis(scales: {
53
47
  * @param {number} [options.yTickCount] - Number of y-axis ticks
54
48
  * @returns {Object} Grid rendering data
55
49
  */
56
- export function createGrid(scales: Object, dimensions: Object, options?: {
50
+ export function createGrid(scales: Object, dimensions: Object, options: {
57
51
  direction?: string | undefined;
58
52
  xTickCount?: number | undefined;
59
53
  yTickCount?: number | undefined;
@@ -1,9 +1,3 @@
1
- /**
2
- * @typedef {import('./types').BarData} BarData
3
- * @typedef {import('./types').ScaleFields} ScaleFields
4
- * @typedef {import('./types').ChartScales} ChartScales
5
- * @typedef {import('./types').ChartDimensions} ChartDimensions
6
- */
7
1
  /**
8
2
  * Creates bar data for rendering
9
3
  *
@@ -16,8 +10,9 @@
16
10
  * @param {Function} scales.x - X-axis scale
17
11
  * @param {Function} scales.y - Y-axis scale
18
12
  * @param {Function} scales.color - Color scale
19
- * @param {Object} dimensions - Chart dimensions
20
- * @param {string} defaultColor - Default color if no color scale is provided
13
+ * @param {Object} options - Options including dimensions and defaultColor
14
+ * @param {Object} options.dimensions - Chart dimensions
15
+ * @param {string} [options.defaultColor='#4682b4'] - Default color if no color scale
21
16
  * @returns {BarData[]} Bar data for rendering
22
17
  */
23
18
  export function createBars(data: any[], fields: {
@@ -28,7 +23,10 @@ export function createBars(data: any[], fields: {
28
23
  x: Function;
29
24
  y: Function;
30
25
  color: Function;
31
- }, dimensions: Object, defaultColor?: string): BarData[];
26
+ }, options: {
27
+ dimensions: Object;
28
+ defaultColor?: string | undefined;
29
+ }): BarData[];
32
30
  /**
33
31
  * Filter bars based on a selection criteria
34
32
  *
@@ -43,11 +41,15 @@ export function filterBars(bars: BarData[], selection: Object): BarData[];
43
41
  * @param {Array} data - Chart data
44
42
  * @param {Object} fields - Field mappings
45
43
  * @param {Object} scales - Chart scales
46
- * @param {Object} dimensions - Chart dimensions
47
- * @param {Object} options - Options
44
+ * @param {Object} options - Options including dimensions and padding
45
+ * @param {Object} options.dimensions - Chart dimensions
46
+ * @param {number} [options.padding=0.1] - Padding between bars in a group
48
47
  * @returns {Object} Grouped bar data
49
48
  */
50
- export function createGroupedBars(data: any[], fields: Object, scales: Object, dimensions: Object, options?: Object): Object;
49
+ export function createGroupedBars(data: any[], fields: Object, scales: Object, options: {
50
+ dimensions: Object;
51
+ padding?: number | undefined;
52
+ }): Object;
51
53
  export type BarData = import("./types").BarData;
52
54
  export type ScaleFields = import("./types").ScaleFields;
53
55
  export type ChartScales = import("./types").ChartScales;
@@ -1,10 +1,3 @@
1
- /**
2
- * @typedef {import('./types').LegendItem} LegendItem
3
- * @typedef {import('./types').LegendData} LegendData
4
- * @typedef {import('./types').ScaleFields} ScaleFields
5
- * @typedef {import('./types').ChartScales} ChartScales
6
- * @typedef {import('./types').ChartDimensions} ChartDimensions
7
- */
8
1
  /**
9
2
  * Creates legend data for rendering
10
3
  *
@@ -13,8 +6,8 @@
13
6
  * @param {string} fields.color - Color field
14
7
  * @param {Object} scales - Chart scales
15
8
  * @param {Function} scales.color - Color scale
16
- * @param {Object} dimensions - Chart dimensions
17
- * @param {Object} options - Legend options
9
+ * @param {Object} options - Legend options including dimensions
10
+ * @param {Object} options.dimensions - Chart dimensions
18
11
  * @param {string} [options.title=''] - Legend title
19
12
  * @param {string} [options.align='right'] - Legend alignment ('left', 'center', or 'right')
20
13
  * @param {string} [options.shape='rect'] - Legend marker shape ('rect' or 'circle')
@@ -25,7 +18,8 @@ export function createLegend(data: any[], fields: {
25
18
  color: string;
26
19
  }, scales: {
27
20
  color: Function;
28
- }, dimensions: Object, options?: {
21
+ }, options: {
22
+ dimensions: Object;
29
23
  title?: string | undefined;
30
24
  align?: string | undefined;
31
25
  shape?: string | undefined;
@@ -1,8 +1,3 @@
1
- /**
2
- * @typedef {import('./types').ChartScales} ChartScales
3
- * @typedef {import('./types').ScaleFields} ScaleFields
4
- * @typedef {import('./types').ChartDimensions} ChartDimensions
5
- */
6
1
  /**
7
2
  * Creates scales based on data, fields, and dimensions
8
3
  *
@@ -1,21 +1,21 @@
1
- /**
2
- * Creates appropriate scales based on data and dimensions
3
- *
4
- * @param {Array} data The dataset
5
- * @param {string} xKey Field to use for x-axis
6
- * @param {string} yKey Field to use for y-axis
7
- * @param {Object} dimensions Chart dimensions
8
- * @param {Object} options Additional options
9
- * @returns {Object} Object containing xScale, yScale, and colorScale
10
- */
11
- export function createScales(data: any[], xKey: string, yKey: string, dimensions: Object, options?: Object): Object;
1
+ export function createScales(data: any, dimensions: any, options: any): {
2
+ xScale?: undefined;
3
+ yScale?: undefined;
4
+ colorScale?: undefined;
5
+ } | {
6
+ xScale: Object;
7
+ yScale: any;
8
+ colorScale: Object | null;
9
+ };
12
10
  /**
13
11
  * Calculates the actual chart dimensions after applying margins
14
12
  *
15
- * @param {Object} dimensions Original dimensions
13
+ * @param {number} width
14
+ * @param {number} height
15
+ * @param {Object} margin
16
16
  * @returns {Object} Dimensions with calculated inner width and height
17
17
  */
18
- export function calculateChartDimensions(width: any, height: any, margin: any): Object;
18
+ export function calculateChartDimensions(width: number, height: number, margin: Object): Object;
19
19
  /**
20
20
  * Gets the axis origin value
21
21
  *
@@ -2,14 +2,16 @@
2
2
  * Creates appropriate scales based on data and dimensions
3
3
  *
4
4
  * @param {Array} data The dataset
5
- * @param {string} xKey Field to use for x-axis
6
- * @param {string} yKey Field to use for y-axis
7
5
  * @param {Object} dimensions Chart dimensions
8
6
  * @param {Object} [options] Additional options
7
+ * @param {string} options.xKey Field to use for x-axis
8
+ * @param {string} options.yKey Field to use for y-axis
9
9
  * @param {string} [options.colorKey] Field to use for color mapping
10
10
  * @returns {Object} Object containing xScale, yScale, and colorScale
11
11
  */
12
- export function createScales(data: any[], xKey: string, yKey: string, dimensions: Object, options?: {
12
+ export function createScales(data: any[], dimensions: Object, options?: {
13
+ xKey: string;
14
+ yKey: string;
13
15
  colorKey?: string | undefined;
14
16
  }): Object;
15
17
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokkit/chart",
3
- "version": "1.0.0-next.136",
3
+ "version": "1.0.0-next.138",
4
4
  "type": "module",
5
5
  "description": "Data-driven chart components",
6
6
  "repository": {
@@ -9,6 +9,84 @@ import {} from './types.js'
9
9
  * @typedef {import('./types').ChartDimensions} ChartDimensions
10
10
  */
11
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
+
12
90
  /**
13
91
  * Creates x-axis tick data for rendering
14
92
  *
@@ -21,40 +99,10 @@ import {} from './types.js'
21
99
  * @param {string} [options.label] - Axis label
22
100
  * @returns {AxisData} Axis rendering data
23
101
  */
24
- export function createXAxis(scales, dimensions, options = {}) {
102
+ export function createXAxis(scales, dimensions, options) {
25
103
  if (!scales.x) return { ticks: [] }
26
-
27
- const { tickCount = null, tickFormat = null, label = '' } = options
28
- const xScale = scales.x
29
-
30
- // Determine tick values
31
- let tickValues
32
- if (xScale.ticks) {
33
- // For continuous scales (linear, time)
34
- tickValues = tickCount !== null ? xScale.ticks(tickCount) : xScale.ticks()
35
- } else {
36
- // For band scales
37
- tickValues = xScale.domain()
38
- if (tickCount !== null && tickCount < tickValues.length) {
39
- const step = Math.max(1, Math.floor(tickValues.length / tickCount))
40
- tickValues = tickValues.filter((_, i) => i % step === 0)
41
- }
42
- }
43
-
44
- // Format ticks
45
- const formatter = tickFormat || ((v) => v)
46
-
47
- // Generate tick data
48
- const ticks = tickValues.map((value) => {
49
- const position = xScale.bandwidth ? xScale(value) + xScale.bandwidth() / 2 : xScale(value)
50
-
51
- return {
52
- value,
53
- position,
54
- formattedValue: formatter(value)
55
- }
56
- })
57
-
104
+ const { tickCount, formatter, label } = parseAxisOptions(options)
105
+ const ticks = mapTicks(scales.x, resolveXTicks(scales.x, tickCount), formatter, xPosition)
58
106
  return {
59
107
  ticks,
60
108
  label,
@@ -63,6 +111,37 @@ export function createXAxis(scales, dimensions, options = {}) {
63
111
  }
64
112
  }
65
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
+
66
145
  /**
67
146
  * Creates y-axis tick data for rendering
68
147
  *
@@ -75,40 +154,69 @@ export function createXAxis(scales, dimensions, options = {}) {
75
154
  * @param {string} [options.label] - Axis label
76
155
  * @returns {AxisData} Axis rendering data
77
156
  */
78
- export function createYAxis(scales, dimensions, options = {}) {
157
+ export function createYAxis(scales, dimensions, options) {
79
158
  if (!scales.y) return { ticks: [] }
80
-
81
- const { tickCount = null, tickFormat = null, label = '' } = options
82
- const yScale = scales.y
83
-
84
- // Determine tick values
85
- let tickValues = []
86
-
87
- if (yScale.ticks) {
88
- tickValues = tickCount !== null ? yScale.ticks(tickCount) : yScale.ticks()
89
- } else if (yScale.domain) {
90
- tickValues = yScale.domain()
91
- if (tickCount !== null && tickValues.length > tickCount) {
92
- const step = Math.max(1, Math.floor(tickValues.length / tickCount))
93
- tickValues = tickValues.filter((_, i) => i % step === 0)
94
- }
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)`
95
166
  }
167
+ }
96
168
 
97
- // Format ticks
98
- const formatter = tickFormat || ((v) => v)
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
+ }
99
183
 
100
- // Generate tick data
101
- const ticks = tickValues.map((value) => ({
102
- value,
103
- position: yScale(value),
104
- formattedValue: formatter(value)
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
105
196
  }))
197
+ }
106
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 || {}
107
205
  return {
108
- ticks,
109
- label,
110
- transform: 'translate(0, 0)',
111
- labelTransform: `translate(-40, ${dimensions.innerHeight / 2}) rotate(-90)`
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'
112
220
  }
113
221
  }
114
222
 
@@ -123,33 +231,13 @@ export function createYAxis(scales, dimensions, options = {}) {
123
231
  * @param {number} [options.yTickCount] - Number of y-axis ticks
124
232
  * @returns {Object} Grid rendering data
125
233
  */
126
- export function createGrid(scales, dimensions, options = {}) {
127
- const { direction = 'both', xTickCount = null, yTickCount = null } = options
128
- const result = { xLines: [], yLines: [] }
129
-
130
- // Generate X grid lines (vertical)
131
- if ((direction === 'x' || direction === 'both') && scales.x) {
132
- const xAxis = createXAxis(scales, dimensions, { tickCount: xTickCount })
133
- result.xLines = xAxis.ticks.map((tick) => ({
134
- x1: tick.position,
135
- y1: 0,
136
- x2: tick.position,
137
- y2: dimensions.innerHeight
138
- }))
139
- }
140
-
141
- // Generate Y grid lines (horizontal)
142
- if ((direction === 'y' || direction === 'both') && scales.y) {
143
- const yAxis = createYAxis(scales, dimensions, { tickCount: yTickCount })
144
- result.yLines = yAxis.ticks.map((tick) => ({
145
- x1: 0,
146
- y1: tick.position,
147
- x2: dimensions.innerWidth,
148
- y2: tick.position
149
- }))
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) : []
150
240
  }
151
-
152
- return result
153
241
  }
154
242
 
155
243
  /**