@rokkit/chart 1.0.0-next.121 → 1.0.0-next.123

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 (36) hide show
  1. package/README.md +43 -35
  2. package/package.json +3 -3
  3. package/src/Plot/Axis.svelte +87 -95
  4. package/src/Plot/Bar.svelte +86 -86
  5. package/src/Plot/Grid.svelte +57 -57
  6. package/src/Plot/Legend.svelte +120 -122
  7. package/src/Plot/Root.svelte +107 -105
  8. package/src/elements/ColorRamp.svelte +19 -19
  9. package/src/elements/ContinuousLegend.svelte +1 -1
  10. package/src/elements/DefinePatterns.svelte +1 -1
  11. package/src/elements/DiscreteLegend.svelte +1 -1
  12. package/src/elements/SymbolGrid.svelte +1 -1
  13. package/src/examples/BarChartExample.svelte +42 -42
  14. package/src/index.js +13 -13
  15. package/src/lib/brewing/axes.svelte.js +120 -122
  16. package/src/lib/brewing/bars.svelte.js +70 -70
  17. package/src/lib/brewing/dimensions.svelte.js +25 -23
  18. package/src/lib/brewing/index.svelte.js +192 -204
  19. package/src/lib/brewing/legends.svelte.js +51 -52
  20. package/src/lib/brewing/scales.svelte.js +59 -68
  21. package/src/lib/brewing/types.js +2 -2
  22. package/src/lib/context.js +117 -107
  23. package/src/lib/scales.svelte.js +80 -87
  24. package/src/lib/utils.js +70 -72
  25. package/src/old_lib/chart.js +4 -4
  26. package/src/patterns/Brick.svelte +1 -1
  27. package/src/patterns/Circles.svelte +1 -1
  28. package/src/patterns/CrossHatch.svelte +1 -1
  29. package/src/patterns/Dots.svelte +1 -1
  30. package/src/patterns/OutlineCircles.svelte +1 -1
  31. package/src/patterns/Tile.svelte +1 -1
  32. package/src/patterns/Triangles.svelte +1 -1
  33. package/src/symbols/RoundedSquare.svelte +0 -1
  34. package/src/template/shapes/Circles.svelte +1 -1
  35. package/src/template/shapes/Lines.svelte +1 -1
  36. package/src/template/shapes/Polygons.svelte +1 -1
@@ -1,7 +1,7 @@
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';
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
5
 
6
6
  /**
7
7
  * @typedef {import('./types').ChartScales} ChartScales
@@ -11,7 +11,7 @@ import { } from './types.js';
11
11
 
12
12
  /**
13
13
  * Creates scales based on data, fields, and dimensions
14
- *
14
+ *
15
15
  * @param {Array} data - Chart data
16
16
  * @param {ScaleFields} fields - Field mappings
17
17
  * @param {Object} dimensions - Chart dimensions
@@ -20,75 +20,66 @@ import { } from './types.js';
20
20
  * @returns {ChartScales} Chart scales
21
21
  */
22
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;
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().domain(xValues).range([0, dimensions.innerWidth]).padding(padding)
56
+ }
57
+
58
+ // Create y scale
59
+ scales.y = scaleLinear()
60
+ .domain([0, max(yValues) * 1.1]) // Add 10% padding on top
61
+ .nice()
62
+ .range([dimensions.innerHeight, 0])
63
+
64
+ // Create color scale if color field is set
65
+ if (fields.color) {
66
+ const colorValues = [...new Set(data.map((d) => d[fields.color]))]
67
+ scales.color = scaleOrdinal().domain(colorValues).range(schemeCategory10)
68
+ }
69
+
70
+ return scales
76
71
  }
77
72
 
78
73
  /**
79
74
  * Gets the origin coordinates for the axes
80
- *
75
+ *
81
76
  * @param {ChartScales} scales - Chart scales
82
77
  * @param {Object} dimensions - Chart dimensions
83
78
  * @returns {Object} Origin coordinates
84
79
  */
85
80
  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
- }
81
+ return {
82
+ x: scales.y ? scales.y(0) : dimensions.innerHeight,
83
+ y: scales.x ? (scales.x.ticks ? scales.x(Math.max(0, min(scales.x.domain()))) : 0) : 0
84
+ }
85
+ }
@@ -50,7 +50,7 @@
50
50
  * @property {number} x - X position
51
51
  * @property {number} y - Y position
52
52
  * @property {number} width - Width of the bar
53
- * @property {number} height - Height of the bar
53
+ * @property {number} height - Height of the bar
54
54
  * @property {string} color - Color of the bar
55
55
  */
56
56
 
@@ -70,4 +70,4 @@
70
70
  * @property {string} transform - SVG transform attribute value
71
71
  */
72
72
 
73
- export {};
73
+ export {}
@@ -1,122 +1,132 @@
1
- import { getContext, setContext } from 'svelte';
2
- import { writable, derived } from 'svelte/store';
3
- import * as d3 from 'd3';
1
+ import { getContext, setContext } from 'svelte'
2
+ import { writable, derived } from 'svelte/store'
3
+ import * as d3 from 'd3'
4
4
 
5
- const CHART_CONTEXT = 'chart-context';
5
+ const CHART_CONTEXT = 'chart-context'
6
6
 
7
7
  /**
8
8
  * Creates chart context and provides it to child components
9
- *
9
+ *
10
10
  * @param {Object} options Initial chart options
11
11
  * @returns {Object} Chart context with all stores and methods
12
12
  */
13
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: $dimensions.width - $dimensions.margin.left - $dimensions.margin.right - $dimensions.padding.left - $dimensions.padding.right,
43
- height: $dimensions.height - $dimensions.margin.top - $dimensions.margin.bottom - $dimensions.padding.top - $dimensions.padding.bottom
44
- };
45
- });
46
-
47
- // Store for plot elements (bars, lines, etc.)
48
- const plots = writable([]);
49
-
50
- // Store for axes
51
- const axes = writable({
52
- x: null,
53
- y: null
54
- });
55
-
56
- const legend = writable({
57
- enabled: false,
58
- items: []
59
- });
60
-
61
- // Helper to add a new plot
62
- function addPlot(plot) {
63
- plots.update(currentPlots => [...currentPlots, plot]);
64
- return () => {
65
- plots.update(currentPlots => currentPlots.filter(p => p !== plot));
66
- };
67
- }
68
-
69
- // Helper to update scales based on data and dimensions
70
- function updateScales(xKey, yKey, colorKey = null) {
71
- return derived([data, innerDimensions], ([$data, $innerDimensions]) => {
72
- if (!$data || $data.length === 0) return null;
73
-
74
- const xScale = d3.scaleBand()
75
- .domain($data.map(d => d[xKey]))
76
- .range([0, $innerDimensions.width])
77
- .padding(0.2);
78
-
79
- const yScale = d3.scaleLinear()
80
- .domain([0, d3.max($data, d => d[yKey])])
81
- .nice()
82
- .range([$innerDimensions.height, 0]);
83
-
84
- let colorScale = null;
85
-
86
- if (colorKey) {
87
- const uniqueCategories = [...new Set($data.map(d => d[colorKey]))];
88
- colorScale = d3.scaleOrdinal()
89
- .domain(uniqueCategories)
90
- .range(d3.schemeCategory10);
91
- }
92
-
93
- return { xScale, yScale, colorScale };
94
- });
95
- }
96
-
97
- // Create and set context
98
- const chartContext = {
99
- dimensions,
100
- innerDimensions,
101
- data,
102
- scales,
103
- plots,
104
- axes,
105
- legend,
106
- addPlot,
107
- updateScales
108
- };
109
-
110
- setContext(CHART_CONTEXT, chartContext);
111
-
112
- return chartContext;
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
113
123
  }
114
124
 
115
125
  /**
116
126
  * Gets chart context provided by parent component
117
- *
127
+ *
118
128
  * @returns {Object} Chart context
119
129
  */
120
130
  export function getChartContext() {
121
- return getContext(CHART_CONTEXT);
122
- }
131
+ return getContext(CHART_CONTEXT)
132
+ }
@@ -1,10 +1,10 @@
1
- import { scaleBand, scaleLinear, scaleTime, scaleOrdinal } from 'd3-scale';
2
- import { schemeCategory10 } from 'd3-scale-chromatic';
3
- import { min, max } from 'd3-array';
1
+ import { scaleBand, scaleLinear, scaleTime, scaleOrdinal } from 'd3-scale'
2
+ import { schemeCategory10 } from 'd3-scale-chromatic'
3
+ import { min, max } from 'd3-array'
4
4
 
5
5
  /**
6
6
  * Creates appropriate scales based on data and dimensions
7
- *
7
+ *
8
8
  * @param {Array} data The dataset
9
9
  * @param {string} xKey Field to use for x-axis
10
10
  * @param {string} yKey Field to use for y-axis
@@ -13,84 +13,77 @@ import { min, max } from 'd3-array';
13
13
  * @returns {Object} Object containing xScale, yScale, and colorScale
14
14
  */
15
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()
39
- .domain(xValues)
40
- .range([0, dimensions.innerWidth])
41
- .padding(padding);
42
- }
43
-
44
- // Create y-scale
45
- const yValues = data.map(d => d[yKey]);
46
- const yScale = scaleLinear()
47
- .domain([0, max(yValues) * 1.1]) // Add 10% padding on top
48
- .nice()
49
- .range([dimensions.innerHeight, 0]);
50
-
51
- // Create color scale if colorKey is provided
52
- let colorScale = null;
53
- if (colorKey) {
54
- const uniqueCategories = [...new Set(data.map(d => d[colorKey]))];
55
- colorScale = scaleOrdinal()
56
- .domain(uniqueCategories)
57
- .range(schemeCategory10);
58
- }
59
-
60
- return { xScale, yScale, colorScale };
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 }
61
56
  }
62
57
 
63
58
  /**
64
59
  * Calculates the actual chart dimensions after applying margins
65
- *
60
+ *
66
61
  * @param {Object} dimensions Original dimensions
67
62
  * @returns {Object} Dimensions with calculated inner width and height
68
63
  */
69
64
  export function calculateChartDimensions(width, height, margin) {
70
- return {
71
- width,
72
- height,
73
- margin,
74
- innerWidth: width - margin.left - margin.right,
75
- innerHeight: height - margin.top - margin.bottom
76
- };
65
+ return {
66
+ width,
67
+ height,
68
+ margin,
69
+ innerWidth: width - margin.left - margin.right,
70
+ innerHeight: height - margin.top - margin.bottom
71
+ }
77
72
  }
78
73
 
79
74
  /**
80
75
  * Gets the axis origin value
81
- *
76
+ *
82
77
  * @param {Object} scale D3 scale
83
78
  * @returns {number} Origin value
84
79
  */
85
80
  export function getOriginValue(scale) {
86
- return scale.ticks
87
- ? scale(Math.max(0, min(scale.domain())))
88
- : scale.range()[0];
81
+ return scale.ticks ? scale(Math.max(0, min(scale.domain()))) : scale.range()[0]
89
82
  }
90
83
 
91
84
  /**
92
85
  * Creates axis ticks
93
- *
86
+ *
94
87
  * @param {Object} scale D3 scale
95
88
  * @param {string} axis Axis type ('x' or 'y')
96
89
  * @param {number} count Number of ticks
@@ -98,32 +91,32 @@ export function getOriginValue(scale) {
98
91
  * @returns {Array} Array of tick objects
99
92
  */
100
93
  export function createTicks(scale, axis, count = null, fontSize = 12) {
101
- const [minRange, maxRange] = scale.range();
102
- let ticks = [];
103
- let offset = 0;
94
+ const [minRange, maxRange] = scale.range()
95
+ let ticks = []
96
+ let offset = 0
104
97
 
105
- // Calculate default count based on available space
106
- if (!count) {
107
- count = Math.abs((maxRange - minRange) / (fontSize * (axis === 'y' ? 3 : 6)));
108
- }
98
+ // Calculate default count based on available space
99
+ if (!count) {
100
+ count = Math.abs((maxRange - minRange) / (fontSize * (axis === 'y' ? 3 : 6)))
101
+ }
109
102
 
110
- // Get ticks based on scale type
111
- if (scale.ticks) {
112
- ticks = scale.ticks(Math.round(count));
113
- } else {
114
- offset = scale.bandwidth() / 2;
115
- count = Math.min(Math.round(count), scale.domain().length);
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)
116
109
 
117
- ticks = scale.domain();
118
- if (count < scale.domain().length) {
119
- const step = Math.ceil(scale.domain().length / count);
120
- ticks = ticks.filter((_, i) => i % step === 0);
121
- }
122
- }
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
+ }
123
116
 
124
- // Format ticks with positions
125
- return ticks.map(t => ({
126
- value: t,
127
- position: scale(t) + (axis === 'x' ? offset : 0)
128
- }));
129
- }
117
+ // Format ticks with positions
118
+ return ticks.map((t) => ({
119
+ value: t,
120
+ position: scale(t) + (axis === 'x' ? offset : 0)
121
+ }))
122
+ }