@rokkit/chart 1.0.0-next.12 → 1.0.0-next.121
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.
- package/README.md +132 -46
- package/dist/Plot/index.d.ts +5 -0
- package/dist/elements/index.d.ts +6 -0
- package/dist/index.d.ts +14 -0
- package/dist/lib/brewing/axes.svelte.d.ts +72 -0
- package/dist/lib/brewing/bars.svelte.d.ts +54 -0
- package/dist/lib/brewing/dimensions.svelte.d.ts +35 -0
- package/dist/lib/brewing/index.svelte.d.ts +118 -0
- package/dist/lib/brewing/legends.svelte.d.ts +54 -0
- package/dist/lib/brewing/scales.svelte.d.ts +29 -0
- package/dist/lib/brewing/types.d.ts +162 -0
- package/dist/lib/context.d.ts +13 -0
- package/dist/lib/scales.svelte.d.ts +35 -0
- package/dist/lib/utils.d.ts +58 -0
- package/dist/old_lib/brewer.d.ts +9 -0
- package/dist/old_lib/chart.d.ts +40 -0
- package/dist/old_lib/grid.d.ts +72 -0
- package/dist/old_lib/index.d.ts +4 -0
- package/dist/old_lib/plots.d.ts +3 -0
- package/dist/old_lib/swatch.d.ts +285 -0
- package/dist/old_lib/ticks.d.ts +36 -0
- package/dist/old_lib/utils.d.ts +1 -0
- package/dist/patterns/index.d.ts +9 -0
- package/dist/patterns/paths/constants.d.ts +1 -0
- package/dist/symbols/constants/index.d.ts +1 -0
- package/dist/symbols/index.d.ts +5 -0
- package/dist/template/constants.d.ts +43 -0
- package/dist/template/shapes/index.d.ts +4 -0
- package/package.json +28 -44
- package/src/Plot/Axis.svelte +103 -0
- package/src/Plot/Bar.svelte +95 -0
- package/src/Plot/Grid.svelte +68 -0
- package/src/Plot/Legend.svelte +129 -0
- package/src/Plot/Root.svelte +112 -0
- package/src/Plot/index.js +5 -0
- package/src/Symbol.svelte +21 -0
- package/src/{chart/Texture.svelte → Texture.svelte} +3 -3
- package/src/elements/Bar.svelte +2 -8
- package/src/elements/ColorRamp.svelte +3 -5
- package/src/elements/ContinuousLegend.svelte +4 -5
- package/src/elements/DefinePatterns.svelte +22 -0
- package/src/elements/DiscreteLegend.svelte +3 -5
- package/src/elements/Label.svelte +6 -5
- package/src/elements/SymbolGrid.svelte +23 -0
- package/src/elements/index.js +6 -0
- package/src/examples/BarChartExample.svelte +81 -0
- package/src/index.js +18 -16
- package/src/lib/brewing/axes.svelte.js +179 -0
- package/src/lib/brewing/bars.svelte.js +114 -0
- package/src/lib/brewing/dimensions.svelte.js +54 -0
- package/src/lib/brewing/index.svelte.js +214 -0
- package/src/lib/brewing/legends.svelte.js +95 -0
- package/src/lib/brewing/scales.svelte.js +94 -0
- package/src/lib/brewing/types.js +73 -0
- package/src/lib/context.js +122 -0
- package/src/lib/scales.svelte.js +129 -0
- package/src/lib/utils.js +110 -132
- package/src/old_lib/brewer.js +25 -0
- package/src/old_lib/chart.js +213 -0
- package/src/old_lib/grid.js +85 -0
- package/src/old_lib/index.js +4 -0
- package/src/old_lib/plots.js +27 -0
- package/src/old_lib/swatch.js +16 -0
- package/src/old_lib/ticks.js +46 -0
- package/src/old_lib/utils.js +8 -0
- package/src/patterns/Brick.svelte +17 -0
- package/src/patterns/Circles.svelte +18 -0
- package/src/patterns/CrossHatch.svelte +14 -0
- package/src/patterns/CurvedWave.svelte +9 -0
- package/src/patterns/Dots.svelte +19 -0
- package/src/patterns/OutlineCircles.svelte +15 -0
- package/src/patterns/Tile.svelte +17 -0
- package/src/patterns/Triangles.svelte +15 -0
- package/src/patterns/Waves.svelte +13 -0
- package/src/patterns/index.js +14 -0
- package/src/patterns/paths/NamedPattern.svelte +12 -0
- package/src/patterns/paths/constants.js +4 -0
- package/src/symbols/RoundedSquare.svelte +27 -0
- package/src/symbols/Shape.svelte +31 -0
- package/src/symbols/constants/index.js +4 -0
- package/src/symbols/index.js +9 -0
- package/src/symbols/outline.svelte +60 -0
- package/src/symbols/solid.svelte +60 -0
- package/src/template/Texture.svelte +16 -0
- package/src/template/constants.js +43 -0
- package/src/template/shapes/Circles.svelte +16 -0
- package/src/template/shapes/Lines.svelte +17 -0
- package/src/template/shapes/Path.svelte +12 -0
- package/src/template/shapes/Polygons.svelte +18 -0
- package/src/template/shapes/index.js +4 -0
- package/LICENSE +0 -21
- package/src/chart/FacetGrid.svelte +0 -51
- package/src/chart/Grid.svelte +0 -34
- package/src/chart/Legend.svelte +0 -16
- package/src/chart/PatternDefs.svelte +0 -13
- package/src/chart/Swatch.svelte +0 -93
- package/src/chart/SwatchButton.svelte +0 -29
- package/src/chart/SwatchGrid.svelte +0 -55
- package/src/chart/Symbol.svelte +0 -37
- package/src/chart/TexturedShape.svelte +0 -27
- package/src/chart/TimelapseChart.svelte +0 -97
- package/src/chart/Timer.svelte +0 -27
- package/src/chart.js +0 -9
- package/src/components/charts/Axis.svelte +0 -66
- package/src/components/charts/Chart.svelte +0 -35
- package/src/components/index.js +0 -23
- package/src/components/lib/axis.js +0 -0
- package/src/components/lib/chart.js +0 -187
- package/src/components/lib/color.js +0 -327
- package/src/components/lib/funnel.js +0 -204
- package/src/components/lib/index.js +0 -19
- package/src/components/lib/pattern.js +0 -190
- package/src/components/lib/rollup.js +0 -55
- package/src/components/lib/shape.js +0 -199
- package/src/components/lib/summary.js +0 -145
- package/src/components/lib/theme.js +0 -23
- package/src/components/lib/timer.js +0 -41
- package/src/components/lib/utils.js +0 -165
- package/src/components/plots/BarPlot.svelte +0 -36
- package/src/components/plots/BoxPlot.svelte +0 -54
- package/src/components/plots/ScatterPlot.svelte +0 -30
- package/src/components/store.js +0 -70
- package/src/constants.js +0 -66
- package/src/elements/PatternDefs.svelte +0 -13
- package/src/elements/PatternMask.svelte +0 -20
- package/src/elements/Symbol.svelte +0 -38
- package/src/elements/Tooltip.svelte +0 -23
- package/src/funnel.svelte +0 -35
- package/src/geom.js +0 -105
- package/src/lib/axis.js +0 -75
- package/src/lib/colors.js +0 -32
- package/src/lib/geom.js +0 -4
- package/src/lib/shapes.js +0 -144
- package/src/lib/timer.js +0 -44
- package/src/lookup.js +0 -29
- package/src/plots/BarPlot.svelte +0 -55
- package/src/plots/BoxPlot.svelte +0 -0
- package/src/plots/FunnelPlot.svelte +0 -33
- package/src/plots/HeatMap.svelte +0 -5
- package/src/plots/HeatMapCalendar.svelte +0 -129
- package/src/plots/LinePlot.svelte +0 -55
- package/src/plots/Plot.svelte +0 -25
- package/src/plots/RankBarPlot.svelte +0 -38
- package/src/plots/ScatterPlot.svelte +0 -20
- package/src/plots/ViolinPlot.svelte +0 -11
- package/src/plots/heatmap.js +0 -70
- package/src/plots/index.js +0 -10
- package/src/swatch.js +0 -11
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('./types').BarData} BarData
|
|
5
|
+
* @typedef {import('./types').ScaleFields} ScaleFields
|
|
6
|
+
* @typedef {import('./types').ChartScales} ChartScales
|
|
7
|
+
* @typedef {import('./types').ChartDimensions} ChartDimensions
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates bar data for rendering
|
|
12
|
+
*
|
|
13
|
+
* @param {Array} data - Chart data
|
|
14
|
+
* @param {Object} fields - Field mappings
|
|
15
|
+
* @param {string} fields.x - X-axis field
|
|
16
|
+
* @param {string} fields.y - Y-axis field
|
|
17
|
+
* @param {string} fields.color - Color field (optional)
|
|
18
|
+
* @param {Object} scales - Chart scales
|
|
19
|
+
* @param {Function} scales.x - X-axis scale
|
|
20
|
+
* @param {Function} scales.y - Y-axis scale
|
|
21
|
+
* @param {Function} scales.color - Color scale
|
|
22
|
+
* @param {Object} dimensions - Chart dimensions
|
|
23
|
+
* @param {string} defaultColor - Default color if no color scale is provided
|
|
24
|
+
* @returns {BarData[]} Bar data for rendering
|
|
25
|
+
*/
|
|
26
|
+
export function createBars(data, fields, scales, dimensions, defaultColor = '#4682b4') {
|
|
27
|
+
if (!data || data.length === 0 || !scales.x || !scales.y) return [];
|
|
28
|
+
|
|
29
|
+
const { x: xField, y: yField, color: colorField } = fields;
|
|
30
|
+
|
|
31
|
+
return data.map(d => {
|
|
32
|
+
const barWidth = scales.x.bandwidth ? scales.x.bandwidth() : 10;
|
|
33
|
+
const barX = scales.x.bandwidth ? scales.x(d[xField]) : scales.x(d[xField]) - barWidth / 2;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
data: d,
|
|
37
|
+
x: barX,
|
|
38
|
+
y: scales.y(d[yField]),
|
|
39
|
+
width: barWidth,
|
|
40
|
+
height: dimensions.innerHeight - scales.y(d[yField]),
|
|
41
|
+
color: colorField && scales.color ? scales.color(d[colorField]) : defaultColor
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Filter bars based on a selection criteria
|
|
48
|
+
*
|
|
49
|
+
* @param {BarData[]} bars - Bar data array
|
|
50
|
+
* @param {Object} selection - Selection criteria
|
|
51
|
+
* @returns {BarData[]} Filtered bars
|
|
52
|
+
*/
|
|
53
|
+
export function filterBars(bars, selection) {
|
|
54
|
+
if (!selection) return bars;
|
|
55
|
+
|
|
56
|
+
return bars.filter(bar => {
|
|
57
|
+
for (const [key, value] of Object.entries(selection)) {
|
|
58
|
+
if (bar.data[key] !== value) return false;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Creates a grouped bars layout
|
|
66
|
+
*
|
|
67
|
+
* @param {Array} data - Chart data
|
|
68
|
+
* @param {Object} fields - Field mappings
|
|
69
|
+
* @param {Object} scales - Chart scales
|
|
70
|
+
* @param {Object} dimensions - Chart dimensions
|
|
71
|
+
* @param {Object} options - Options
|
|
72
|
+
* @returns {Object} Grouped bar data
|
|
73
|
+
*/
|
|
74
|
+
export function createGroupedBars(data, fields, scales, dimensions, options = {}) {
|
|
75
|
+
if (!data || data.length === 0 || !fields.group) return { groups: [], bars: [] };
|
|
76
|
+
|
|
77
|
+
const { x: xField, y: yField, group: groupField, color: colorField = groupField } = fields;
|
|
78
|
+
const { padding = 0.1 } = options;
|
|
79
|
+
|
|
80
|
+
// Get unique groups and x values
|
|
81
|
+
const groups = [...new Set(data.map(d => d[groupField]))];
|
|
82
|
+
const xValues = [...new Set(data.map(d => d[xField]))];
|
|
83
|
+
|
|
84
|
+
// Calculate group width and individual bar width
|
|
85
|
+
const xScale = scales.x;
|
|
86
|
+
const groupWidth = xScale.bandwidth ? xScale.bandwidth() : 20;
|
|
87
|
+
const barWidth = (groupWidth - padding * (groups.length - 1)) / groups.length;
|
|
88
|
+
|
|
89
|
+
// Create bars for each group
|
|
90
|
+
const bars = [];
|
|
91
|
+
|
|
92
|
+
xValues.forEach(xValue => {
|
|
93
|
+
const groupItems = data.filter(d => d[xField] === xValue);
|
|
94
|
+
const xPos = xScale(xValue);
|
|
95
|
+
|
|
96
|
+
groups.forEach((group, i) => {
|
|
97
|
+
const item = groupItems.find(d => d[groupField] === group);
|
|
98
|
+
if (!item) return;
|
|
99
|
+
|
|
100
|
+
const barX = xPos + i * (barWidth + padding);
|
|
101
|
+
bars.push({
|
|
102
|
+
data: item,
|
|
103
|
+
group,
|
|
104
|
+
x: barX,
|
|
105
|
+
y: scales.y(item[yField]),
|
|
106
|
+
width: barWidth,
|
|
107
|
+
height: dimensions.innerHeight - scales.y(item[yField]),
|
|
108
|
+
color: scales.color ? scales.color(item[colorField]) : '#4682b4'
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return { groups, bars };
|
|
114
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('./types').ChartMargin} ChartMargin
|
|
5
|
+
* @typedef {import('./types').ChartDimensions} ChartDimensions
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Default chart margin
|
|
10
|
+
* @type {ChartMargin}
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_MARGIN = { top: 20, right: 30, bottom: 40, left: 50 };
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Creates chart dimensions based on width, height and margins
|
|
16
|
+
*
|
|
17
|
+
* @param {number} width - Total chart width
|
|
18
|
+
* @param {number} height - Total chart height
|
|
19
|
+
* @param {ChartMargin} margin - Chart margins
|
|
20
|
+
* @returns {ChartDimensions} Chart dimensions
|
|
21
|
+
*/
|
|
22
|
+
export function createDimensions(width = 600, height = 400, margin = DEFAULT_MARGIN) {
|
|
23
|
+
return {
|
|
24
|
+
width,
|
|
25
|
+
height,
|
|
26
|
+
margin: { ...margin },
|
|
27
|
+
innerWidth: width - margin.left - margin.right,
|
|
28
|
+
innerHeight: height - margin.top - margin.bottom
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Updates existing dimensions with new values
|
|
34
|
+
*
|
|
35
|
+
* @param {ChartDimensions} dimensions - Current dimensions
|
|
36
|
+
* @param {Object} updates - Values to update
|
|
37
|
+
* @param {number} [updates.width] - New width
|
|
38
|
+
* @param {number} [updates.height] - New height
|
|
39
|
+
* @param {ChartMargin} [updates.margin] - New margin
|
|
40
|
+
* @returns {ChartDimensions} Updated dimensions
|
|
41
|
+
*/
|
|
42
|
+
export function updateDimensions(dimensions, updates = {}) {
|
|
43
|
+
const newDimensions = { ...dimensions };
|
|
44
|
+
|
|
45
|
+
if (updates.width !== undefined) newDimensions.width = updates.width;
|
|
46
|
+
if (updates.height !== undefined) newDimensions.height = updates.height;
|
|
47
|
+
if (updates.margin !== undefined) newDimensions.margin = { ...updates.margin };
|
|
48
|
+
|
|
49
|
+
// Recalculate inner dimensions
|
|
50
|
+
newDimensions.innerWidth = newDimensions.width - newDimensions.margin.left - newDimensions.margin.right;
|
|
51
|
+
newDimensions.innerHeight = newDimensions.height - newDimensions.margin.top - newDimensions.margin.bottom;
|
|
52
|
+
|
|
53
|
+
return newDimensions;
|
|
54
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { createDimensions, updateDimensions } from './dimensions.svelte.js';
|
|
2
|
+
import { createScales, getOrigin } from './scales.svelte.js';
|
|
3
|
+
import { createBars, filterBars, createGroupedBars } from './bars.svelte.js';
|
|
4
|
+
import {
|
|
5
|
+
createXAxis,
|
|
6
|
+
createYAxis,
|
|
7
|
+
createGrid,
|
|
8
|
+
createTickAttributes
|
|
9
|
+
} from './axes.svelte.js';
|
|
10
|
+
import {
|
|
11
|
+
createLegend,
|
|
12
|
+
filterByLegend,
|
|
13
|
+
createLegendItemAttributes
|
|
14
|
+
} from './legends.svelte.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Chart Brewing - A collection of pure functions for chart data preparation
|
|
18
|
+
*/
|
|
19
|
+
export {
|
|
20
|
+
// Dimensions
|
|
21
|
+
createDimensions,
|
|
22
|
+
updateDimensions,
|
|
23
|
+
|
|
24
|
+
// Scales
|
|
25
|
+
createScales,
|
|
26
|
+
getOrigin,
|
|
27
|
+
|
|
28
|
+
// Bars
|
|
29
|
+
createBars,
|
|
30
|
+
filterBars,
|
|
31
|
+
createGroupedBars,
|
|
32
|
+
|
|
33
|
+
// Axes
|
|
34
|
+
createXAxis,
|
|
35
|
+
createYAxis,
|
|
36
|
+
createGrid,
|
|
37
|
+
createTickAttributes,
|
|
38
|
+
|
|
39
|
+
// Legends
|
|
40
|
+
createLegend,
|
|
41
|
+
filterByLegend,
|
|
42
|
+
createLegendItemAttributes
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Main class that manages chart state and provides access to all brewing functions
|
|
47
|
+
*/
|
|
48
|
+
export class ChartBrewer {
|
|
49
|
+
#data = [];
|
|
50
|
+
#dimensions = createDimensions();
|
|
51
|
+
#scales = { x: null, y: null, color: null };
|
|
52
|
+
#fields = { x: null, y: null, color: null };
|
|
53
|
+
#options = {
|
|
54
|
+
padding: 0.2,
|
|
55
|
+
animationDuration: 300
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a new ChartBrewer instance
|
|
60
|
+
*
|
|
61
|
+
* @param {Object} options Configuration options
|
|
62
|
+
*/
|
|
63
|
+
constructor(options = {}) {
|
|
64
|
+
this.#dimensions = createDimensions(
|
|
65
|
+
options.width,
|
|
66
|
+
options.height,
|
|
67
|
+
options.margin
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (options.padding !== undefined) this.#options.padding = options.padding;
|
|
71
|
+
if (options.animationDuration !== undefined) this.#options.animationDuration = options.animationDuration;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Sets the data for the chart
|
|
76
|
+
*
|
|
77
|
+
* @param {Array} data Data array
|
|
78
|
+
* @returns {ChartBrewer} this for method chaining
|
|
79
|
+
*/
|
|
80
|
+
setData(data) {
|
|
81
|
+
this.#data = Array.isArray(data) ? data : [];
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Sets the field mappings for axes and color
|
|
87
|
+
*
|
|
88
|
+
* @param {Object} fields Field mappings
|
|
89
|
+
* @returns {ChartBrewer} this for method chaining
|
|
90
|
+
*/
|
|
91
|
+
setFields({ x, y, color }) {
|
|
92
|
+
if (x !== undefined) this.#fields.x = x;
|
|
93
|
+
if (y !== undefined) this.#fields.y = y;
|
|
94
|
+
if (color !== undefined) this.#fields.color = color;
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Sets the dimensions of the chart
|
|
100
|
+
*
|
|
101
|
+
* @param {Object} dimensions Chart dimensions
|
|
102
|
+
* @returns {ChartBrewer} this for method chaining
|
|
103
|
+
*/
|
|
104
|
+
setDimensions({ width, height, margin }) {
|
|
105
|
+
this.#dimensions = updateDimensions(this.#dimensions, { width, height, margin });
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Creates scales based on data and dimensions
|
|
111
|
+
*
|
|
112
|
+
* @returns {ChartBrewer} this for method chaining
|
|
113
|
+
*/
|
|
114
|
+
createScales() {
|
|
115
|
+
this.#scales = createScales(this.#data, this.#fields, this.#dimensions, {
|
|
116
|
+
padding: this.#options.padding
|
|
117
|
+
});
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Creates bar data for rendering
|
|
123
|
+
*
|
|
124
|
+
* @returns {Array} Data for rendering bars
|
|
125
|
+
*/
|
|
126
|
+
createBars() {
|
|
127
|
+
return createBars(this.#data, this.#fields, this.#scales, this.#dimensions);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Creates x-axis tick data for rendering
|
|
132
|
+
*
|
|
133
|
+
* @param {Object} options Axis options
|
|
134
|
+
* @returns {Object} Axis rendering data
|
|
135
|
+
*/
|
|
136
|
+
createXAxis(options = {}) {
|
|
137
|
+
return createXAxis(this.#scales, this.#dimensions, options);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Creates y-axis tick data for rendering
|
|
142
|
+
*
|
|
143
|
+
* @param {Object} options Axis options
|
|
144
|
+
* @returns {Object} Axis rendering data
|
|
145
|
+
*/
|
|
146
|
+
createYAxis(options = {}) {
|
|
147
|
+
return createYAxis(this.#scales, this.#dimensions, options);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Creates grid line data for rendering
|
|
152
|
+
*
|
|
153
|
+
* @param {Object} options Grid options
|
|
154
|
+
* @returns {Object} Grid rendering data
|
|
155
|
+
*/
|
|
156
|
+
createGrid(options = {}) {
|
|
157
|
+
return createGrid(this.#scales, this.#dimensions, options);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Creates legend data for rendering
|
|
162
|
+
*
|
|
163
|
+
* @param {Object} options Legend options
|
|
164
|
+
* @returns {Object} Legend rendering data
|
|
165
|
+
*/
|
|
166
|
+
createLegend(options = {}) {
|
|
167
|
+
return createLegend(this.#data, this.#fields, this.#scales, this.#dimensions, options);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Gets all chart dimensions
|
|
172
|
+
*
|
|
173
|
+
* @returns {Object} Chart dimensions
|
|
174
|
+
*/
|
|
175
|
+
getDimensions() {
|
|
176
|
+
return { ...this.#dimensions };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Gets all chart scales
|
|
181
|
+
*
|
|
182
|
+
* @returns {Object} Chart scales
|
|
183
|
+
*/
|
|
184
|
+
getScales() {
|
|
185
|
+
return { ...this.#scales };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Gets the animation duration
|
|
190
|
+
*
|
|
191
|
+
* @returns {number} Animation duration in ms
|
|
192
|
+
*/
|
|
193
|
+
getAnimationDuration() {
|
|
194
|
+
return this.#options.animationDuration;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Gets the data being used
|
|
199
|
+
*
|
|
200
|
+
* @returns {Array} Chart data
|
|
201
|
+
*/
|
|
202
|
+
getData() {
|
|
203
|
+
return [...this.#data];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Gets the fields configuration
|
|
208
|
+
*
|
|
209
|
+
* @returns {Object} Fields configuration
|
|
210
|
+
*/
|
|
211
|
+
getFields() {
|
|
212
|
+
return { ...this.#fields };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('./types').LegendItem} LegendItem
|
|
5
|
+
* @typedef {import('./types').LegendData} LegendData
|
|
6
|
+
* @typedef {import('./types').ScaleFields} ScaleFields
|
|
7
|
+
* @typedef {import('./types').ChartScales} ChartScales
|
|
8
|
+
* @typedef {import('./types').ChartDimensions} ChartDimensions
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates legend data for rendering
|
|
13
|
+
*
|
|
14
|
+
* @param {Array} data - Chart data
|
|
15
|
+
* @param {Object} fields - Field mappings
|
|
16
|
+
* @param {string} fields.color - Color field
|
|
17
|
+
* @param {Object} scales - Chart scales
|
|
18
|
+
* @param {Function} scales.color - Color scale
|
|
19
|
+
* @param {Object} dimensions - Chart dimensions
|
|
20
|
+
* @param {Object} options - Legend options
|
|
21
|
+
* @param {string} [options.title=''] - Legend title
|
|
22
|
+
* @param {string} [options.align='right'] - Legend alignment ('left', 'center', or 'right')
|
|
23
|
+
* @param {string} [options.shape='rect'] - Legend marker shape ('rect' or 'circle')
|
|
24
|
+
* @param {number} [options.markerSize=10] - Size of legend markers
|
|
25
|
+
* @returns {LegendData} Legend rendering data
|
|
26
|
+
*/
|
|
27
|
+
export function createLegend(data, fields, scales, dimensions, options = {}) {
|
|
28
|
+
if (!data || !fields.color || !scales.color) {
|
|
29
|
+
return { items: [], title: '', transform: 'translate(0, 0)' };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { title = '', align = 'right', shape = 'rect', markerSize = 10 } = options;
|
|
33
|
+
|
|
34
|
+
// Get unique color values
|
|
35
|
+
const colorValues = [...new Set(data.map(d => d[fields.color]))];
|
|
36
|
+
|
|
37
|
+
// Create legend items
|
|
38
|
+
const items = colorValues.map((value, index) => ({
|
|
39
|
+
value,
|
|
40
|
+
color: scales.color(value),
|
|
41
|
+
y: index * (markerSize + 5) + (title ? 15 : 0),
|
|
42
|
+
shape,
|
|
43
|
+
markerSize
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// Calculate approximate width for alignment
|
|
47
|
+
const approximateWidth = Math.max(
|
|
48
|
+
...colorValues.map(v => v.toString().length)
|
|
49
|
+
) * 8 + markerSize + 10;
|
|
50
|
+
|
|
51
|
+
// Calculate position based on alignment
|
|
52
|
+
let x = 0;
|
|
53
|
+
if (align === 'right') {
|
|
54
|
+
x = dimensions.innerWidth - approximateWidth;
|
|
55
|
+
} else if (align === 'center') {
|
|
56
|
+
x = (dimensions.innerWidth - approximateWidth) / 2;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
items,
|
|
61
|
+
title,
|
|
62
|
+
transform: `translate(${x}, 0)`
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Filter data based on legend selection
|
|
68
|
+
*
|
|
69
|
+
* @param {Array} data - Chart data
|
|
70
|
+
* @param {string} colorField - Field used for color mapping
|
|
71
|
+
* @param {Array} selectedValues - Selected legend values
|
|
72
|
+
* @returns {Array} Filtered data
|
|
73
|
+
*/
|
|
74
|
+
export function filterByLegend(data, colorField, selectedValues) {
|
|
75
|
+
if (!selectedValues || selectedValues.length === 0) {
|
|
76
|
+
return data;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return data.filter(d => selectedValues.includes(d[colorField]));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Create attributes for legend items
|
|
84
|
+
*
|
|
85
|
+
* @param {LegendItem} item - Legend item
|
|
86
|
+
* @returns {Object} Attributes for the legend item
|
|
87
|
+
*/
|
|
88
|
+
export function createLegendItemAttributes(item) {
|
|
89
|
+
return {
|
|
90
|
+
'data-plot-legend-item': '',
|
|
91
|
+
'transform': `translate(0, ${item.y})`,
|
|
92
|
+
'role': 'img',
|
|
93
|
+
'aria-label': `Legend item for ${item.value}`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { min, max } from 'd3-array';
|
|
2
|
+
import { scaleBand, scaleLinear, scaleTime, scaleOrdinal } from 'd3-scale';
|
|
3
|
+
import { schemeCategory10 } from 'd3-scale-chromatic';
|
|
4
|
+
import { } from './types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('./types').ChartScales} ChartScales
|
|
8
|
+
* @typedef {import('./types').ScaleFields} ScaleFields
|
|
9
|
+
* @typedef {import('./types').ChartDimensions} ChartDimensions
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates scales based on data, fields, and dimensions
|
|
14
|
+
*
|
|
15
|
+
* @param {Array} data - Chart data
|
|
16
|
+
* @param {ScaleFields} fields - Field mappings
|
|
17
|
+
* @param {Object} dimensions - Chart dimensions
|
|
18
|
+
* @param {Object} options - Scale options
|
|
19
|
+
* @param {number} [options.padding=0.2] - Padding for band scales
|
|
20
|
+
* @returns {ChartScales} Chart scales
|
|
21
|
+
*/
|
|
22
|
+
export function createScales(data, fields, dimensions, options = {}) {
|
|
23
|
+
const scales = {
|
|
24
|
+
x: null,
|
|
25
|
+
y: null,
|
|
26
|
+
color: null
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
if (!data || data.length === 0 || !fields.x || !fields.y) {
|
|
30
|
+
return scales;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const padding = options.padding !== undefined ? options.padding : 0.2;
|
|
34
|
+
|
|
35
|
+
// Extract values
|
|
36
|
+
const xValues = data.map(d => d[fields.x]);
|
|
37
|
+
const yValues = data.map(d => d[fields.y]);
|
|
38
|
+
|
|
39
|
+
// Determine x scale type
|
|
40
|
+
const xIsDate = xValues.some(v => v instanceof Date);
|
|
41
|
+
const xIsNumeric = !xIsDate && xValues.every(v => !isNaN(parseFloat(v)));
|
|
42
|
+
|
|
43
|
+
// Create x scale based on data type
|
|
44
|
+
if (xIsDate) {
|
|
45
|
+
scales.x = scaleTime()
|
|
46
|
+
.domain([min(xValues), max(xValues)])
|
|
47
|
+
.range([0, dimensions.innerWidth])
|
|
48
|
+
.nice();
|
|
49
|
+
} else if (xIsNumeric) {
|
|
50
|
+
scales.x = scaleLinear()
|
|
51
|
+
.domain([min([0, ...xValues]), max(xValues)])
|
|
52
|
+
.range([0, dimensions.innerWidth])
|
|
53
|
+
.nice();
|
|
54
|
+
} else {
|
|
55
|
+
scales.x = scaleBand()
|
|
56
|
+
.domain(xValues)
|
|
57
|
+
.range([0, dimensions.innerWidth])
|
|
58
|
+
.padding(padding);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Create y scale
|
|
62
|
+
scales.y = scaleLinear()
|
|
63
|
+
.domain([0, max(yValues) * 1.1]) // Add 10% padding on top
|
|
64
|
+
.nice()
|
|
65
|
+
.range([dimensions.innerHeight, 0]);
|
|
66
|
+
|
|
67
|
+
// Create color scale if color field is set
|
|
68
|
+
if (fields.color) {
|
|
69
|
+
const colorValues = [...new Set(data.map(d => d[fields.color]))];
|
|
70
|
+
scales.color = scaleOrdinal()
|
|
71
|
+
.domain(colorValues)
|
|
72
|
+
.range(schemeCategory10);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return scales;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Gets the origin coordinates for the axes
|
|
80
|
+
*
|
|
81
|
+
* @param {ChartScales} scales - Chart scales
|
|
82
|
+
* @param {Object} dimensions - Chart dimensions
|
|
83
|
+
* @returns {Object} Origin coordinates
|
|
84
|
+
*/
|
|
85
|
+
export function getOrigin(scales, dimensions) {
|
|
86
|
+
return {
|
|
87
|
+
x: scales.y ? scales.y(0) : dimensions.innerHeight,
|
|
88
|
+
y: scales.x ? (
|
|
89
|
+
scales.x.ticks
|
|
90
|
+
? scales.x(Math.max(0, min(scales.x.domain())))
|
|
91
|
+
: 0
|
|
92
|
+
) : 0
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} ChartMargin
|
|
3
|
+
* @property {number} top - Top margin
|
|
4
|
+
* @property {number} right - Right margin
|
|
5
|
+
* @property {number} bottom - Bottom margin
|
|
6
|
+
* @property {number} left - Left margin
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} ChartDimensions
|
|
11
|
+
* @property {number} width - Total chart width
|
|
12
|
+
* @property {number} height - Total chart height
|
|
13
|
+
* @property {ChartMargin} margin - Chart margins
|
|
14
|
+
* @property {number} innerWidth - Chart width without margins
|
|
15
|
+
* @property {number} innerHeight - Chart height without margins
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} ChartScales
|
|
20
|
+
* @property {Function} x - X-axis scale function
|
|
21
|
+
* @property {Function} y - Y-axis scale function
|
|
22
|
+
* @property {Function} color - Color scale function
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {Object} ScaleFields
|
|
27
|
+
* @property {string} x - X-axis field
|
|
28
|
+
* @property {string} y - Y-axis field
|
|
29
|
+
* @property {string} color - Color field
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {Object} TickData
|
|
34
|
+
* @property {*} value - Tick value
|
|
35
|
+
* @property {number} position - Tick position in pixels
|
|
36
|
+
* @property {string} formattedValue - Formatted tick label
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {Object} AxisData
|
|
41
|
+
* @property {TickData[]} ticks - Tick data
|
|
42
|
+
* @property {string} label - Axis label
|
|
43
|
+
* @property {string} transform - SVG transform attribute value
|
|
44
|
+
* @property {string} labelTransform - SVG transform for the label
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {Object} BarData
|
|
49
|
+
* @property {Object} data - Original data point
|
|
50
|
+
* @property {number} x - X position
|
|
51
|
+
* @property {number} y - Y position
|
|
52
|
+
* @property {number} width - Width of the bar
|
|
53
|
+
* @property {number} height - Height of the bar
|
|
54
|
+
* @property {string} color - Color of the bar
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @typedef {Object} LegendItem
|
|
59
|
+
* @property {*} value - Legend item value
|
|
60
|
+
* @property {string} color - Item color
|
|
61
|
+
* @property {number} y - Y position
|
|
62
|
+
* @property {string} shape - Shape type ('rect' or 'circle')
|
|
63
|
+
* @property {number} markerSize - Size of the marker
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @typedef {Object} LegendData
|
|
68
|
+
* @property {LegendItem[]} items - Legend items
|
|
69
|
+
* @property {string} title - Legend title
|
|
70
|
+
* @property {string} transform - SVG transform attribute value
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
export {};
|