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

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 +1 -1
  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
package/README.md CHANGED
@@ -16,30 +16,30 @@ The chart package provides a set of composable components that can be combined t
16
16
 
17
17
  ```svelte
18
18
  <script>
19
- import { Plot } from '@rokkit/chart'
20
- import { dataset } from '@rokkit/data'
21
-
22
- // Sample data
23
- const sampleData = [
24
- { model: 'Model A', name: 'Product 1', category: 'Electronics', count: 45 },
25
- { model: 'Model B', name: 'Product 2', category: 'Clothing', count: 32 },
26
- { model: 'Model C', name: 'Product 3', category: 'Electronics', count: 62 },
27
- // More data...
28
- ]
29
-
30
- // Use the dataset class to process the data
31
- let data = dataset(sampleData)
32
- .groupBy('model')
33
- .summarize('name', { count: values => values.length })
34
- .rollup()
19
+ import { Plot } from '@rokkit/chart'
20
+ import { dataset } from '@rokkit/data'
21
+
22
+ // Sample data
23
+ const sampleData = [
24
+ { model: 'Model A', name: 'Product 1', category: 'Electronics', count: 45 },
25
+ { model: 'Model B', name: 'Product 2', category: 'Clothing', count: 32 },
26
+ { model: 'Model C', name: 'Product 3', category: 'Electronics', count: 62 }
27
+ // More data...
28
+ ]
29
+
30
+ // Use the dataset class to process the data
31
+ let data = dataset(sampleData)
32
+ .groupBy('model')
33
+ .summarize('name', { count: (values) => values.length })
34
+ .rollup()
35
35
  </script>
36
36
 
37
37
  <Plot.Root {data} width={600} height={400} margin={{ top: 20, right: 30, bottom: 40, left: 50 }}>
38
- <Plot.Grid direction="y" />
39
- <Plot.Axis type="x" field="model" label="Model" />
40
- <Plot.Axis type="y" field="count" label="Count" />
41
- <Plot.Bar x="model" y="count" fill="category" />
42
- <Plot.Legend title="Categories" />
38
+ <Plot.Grid direction="y" />
39
+ <Plot.Axis type="x" field="model" label="Model" />
40
+ <Plot.Axis type="y" field="count" label="Count" />
41
+ <Plot.Bar x="model" y="count" fill="category" />
42
+ <Plot.Legend title="Categories" />
43
43
  </Plot.Root>
44
44
  ```
45
45
 
@@ -50,6 +50,7 @@ The chart package provides a set of composable components that can be combined t
50
50
  The container component for all charts.
51
51
 
52
52
  **Props:**
53
+
53
54
  - `data` - The dataset to visualize (array or dataset object)
54
55
  - `width` - Width of the chart in pixels (default: 600)
55
56
  - `height` - Height of the chart in pixels (default: 400)
@@ -63,6 +64,7 @@ The container component for all charts.
63
64
  Renders an axis for the chart.
64
65
 
65
66
  **Props:**
67
+
66
68
  - `type` - Type of axis ('x' or 'y')
67
69
  - `field` - Data field to use for this axis
68
70
  - `label` - Axis label text
@@ -75,6 +77,7 @@ Renders an axis for the chart.
75
77
  Renders a bar chart.
76
78
 
77
79
  **Props:**
80
+
78
81
  - `x` - Field to use for x-axis values
79
82
  - `y` - Field to use for y-axis values
80
83
  - `fill` - Field to use for coloring the bars (optional)
@@ -88,6 +91,7 @@ Renders a bar chart.
88
91
  Renders grid lines for the chart.
89
92
 
90
93
  **Props:**
94
+
91
95
  - `direction` - Direction of grid lines ('x', 'y', or 'both') (default: 'both')
92
96
  - `xTicks` - Number of ticks for x-axis grid (optional)
93
97
  - `yTicks` - Number of ticks for y-axis grid (optional)
@@ -100,6 +104,7 @@ Renders grid lines for the chart.
100
104
  Renders a legend for the chart.
101
105
 
102
106
  **Props:**
107
+
103
108
  - `title` - Title for the legend (optional)
104
109
  - `align` - Alignment of the legend ('left', 'right', 'center') (default: 'right')
105
110
  - `verticalAlign` - Vertical position of the legend ('top', 'bottom') (default: 'top')
@@ -113,23 +118,26 @@ The chart components work seamlessly with the `@rokkit/data` package, allowing f
113
118
 
114
119
  ```svelte
115
120
  <script>
116
- import { Plot } from '@rokkit/chart'
117
- import { dataset } from '@rokkit/data'
118
-
119
- let input = [
120
- // Your raw data here
121
- ]
122
-
123
- $: processedData = dataset(input)
124
- .groupBy('category')
125
- .summarize('name', { count: values => values.length, total: values => values.reduce((a, b) => a + b, 0) })
126
- .rollup()
121
+ import { Plot } from '@rokkit/chart'
122
+ import { dataset } from '@rokkit/data'
123
+
124
+ let input = [
125
+ // Your raw data here
126
+ ]
127
+
128
+ $: processedData = dataset(input)
129
+ .groupBy('category')
130
+ .summarize('name', {
131
+ count: (values) => values.length,
132
+ total: (values) => values.reduce((a, b) => a + b, 0)
133
+ })
134
+ .rollup()
127
135
  </script>
128
136
 
129
137
  <Plot.Root data={processedData} fill="category">
130
- <Plot.Axis type="x" field="category" />
131
- <Plot.Axis type="y" field="count" />
132
- <Plot.Bar x="category" y="count" />
138
+ <Plot.Axis type="x" field="category" />
139
+ <Plot.Axis type="y" field="count" />
140
+ <Plot.Bar x="category" y="count" />
133
141
  </Plot.Root>
134
142
  ```
135
143
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokkit/chart",
3
- "version": "1.0.0-next.121",
3
+ "version": "1.0.0-next.122",
4
4
  "type": "module",
5
5
  "description": "Data-driven chart components",
6
6
  "author": "Jerry Thomas <me@jerrythomas.name>",
@@ -1,103 +1,95 @@
1
1
  <script>
2
- import { getContext } from 'svelte';
3
- import { createXAxis, createYAxis, createTickAttributes } from '../lib/brewing/axes.svelte.js';
2
+ import { getContext } from 'svelte'
3
+ import { createXAxis, createYAxis, createTickAttributes } from '../lib/brewing/axes.svelte.js'
4
4
 
5
- let {
6
- type = 'x',
7
- field = null,
8
- label = '',
9
- ticks = null,
10
- tickFormat = null,
11
- grid = false
12
- } = $props();
5
+ let {
6
+ type = 'x',
7
+ field = null,
8
+ label = '',
9
+ ticks = null,
10
+ tickFormat = null,
11
+ grid = false
12
+ } = $props()
13
13
 
14
- // Get brewer from context
15
- const brewer = getContext('chart-brewer');
16
-
17
- // Set field mappings in the brewer
18
- $effect(() => {
19
- if (field) {
20
- brewer.setFields({
21
- [type]: field
22
- });
23
-
24
- // Ensure scales are updated
25
- brewer.createScales();
26
- }
27
- });
28
-
29
- // Compute axis data whenever scales change
30
- let axisData = $derived(
31
- type === 'x'
32
- ? brewer.createXAxis({ tickCount: ticks, tickFormat, label })
33
- : brewer.createYAxis({ tickCount: ticks, tickFormat, label })
34
- );
14
+ // Get brewer from context
15
+ const brewer = getContext('chart-brewer')
16
+
17
+ // Set field mappings in the brewer
18
+ $effect(() => {
19
+ if (field) {
20
+ brewer.setFields({
21
+ [type]: field
22
+ })
23
+
24
+ // Ensure scales are updated
25
+ brewer.createScales()
26
+ }
27
+ })
28
+
29
+ // Compute axis data whenever scales change
30
+ let axisData = $derived(
31
+ type === 'x'
32
+ ? brewer.createXAxis({ tickCount: ticks, tickFormat, label })
33
+ : brewer.createYAxis({ tickCount: ticks, tickFormat, label })
34
+ )
35
35
  </script>
36
36
 
37
- <g
38
- class="axis {type}-axis"
39
- transform={axisData.transform}
40
- data-plot-axis={type}
41
- >
42
- {#if axisData.ticks.length > 0}
43
- <!-- Axis line -->
44
- <line
45
- data-plot-axis-line
46
- x1={type === 'x' ? 0 : 0}
47
- y1={type === 'x' ? 0 : 0}
48
- x2={type === 'x' ? brewer.getDimensions().innerWidth : 0}
49
- y2={type === 'x' ? 0 : brewer.getDimensions().innerHeight}
50
- stroke="currentColor"
51
- />
52
-
53
- <!-- Ticks -->
54
- {#each axisData.ticks as tick}
55
- {@const attrs = createTickAttributes(tick, type)}
56
- <g {...attrs}>
57
- <line
58
- x1="0"
59
- y1="0"
60
- x2={type === 'x' ? 0 : -6}
61
- y2={type === 'x' ? 6 : 0}
62
- stroke="currentColor"
63
- />
64
- <text
65
- x={type === 'x' ? 0 : -9}
66
- y={type === 'x' ? 9 : 0}
67
- data-plot-tick-label
68
- >
69
- {tick.formattedValue}
70
- </text>
71
- </g>
72
- {/each}
73
-
74
- <!-- Axis label -->
75
- {#if label}
76
- <text
77
- class="axis-label {type}-axis-label"
78
- transform={axisData.labelTransform}
79
- text-anchor="middle"
80
- data-plot-axis-label
81
- >
82
- {label}
83
- </text>
84
- {/if}
85
- {/if}
37
+ <g class="axis {type}-axis" transform={axisData.transform} data-plot-axis={type}>
38
+ {#if axisData.ticks.length > 0}
39
+ <!-- Axis line -->
40
+ <line
41
+ data-plot-axis-line
42
+ x1={type === 'x' ? 0 : 0}
43
+ y1={type === 'x' ? 0 : 0}
44
+ x2={type === 'x' ? brewer.getDimensions().innerWidth : 0}
45
+ y2={type === 'x' ? 0 : brewer.getDimensions().innerHeight}
46
+ stroke="currentColor"
47
+ />
48
+
49
+ <!-- Ticks -->
50
+ {#each axisData.ticks as tick, index (index)}
51
+ {@const attrs = createTickAttributes(tick, type)}
52
+ <g {...attrs}>
53
+ <line
54
+ x1="0"
55
+ y1="0"
56
+ x2={type === 'x' ? 0 : -6}
57
+ y2={type === 'x' ? 6 : 0}
58
+ stroke="currentColor"
59
+ />
60
+ <text x={type === 'x' ? 0 : -9} y={type === 'x' ? 9 : 0} data-plot-tick-label>
61
+ {tick.formattedValue}
62
+ </text>
63
+ </g>
64
+ {/each}
65
+
66
+ <!-- Axis label -->
67
+ {#if label}
68
+ <text
69
+ class="axis-label {type}-axis-label"
70
+ transform={axisData.labelTransform}
71
+ text-anchor="middle"
72
+ data-plot-axis-label
73
+ >
74
+ {label}
75
+ </text>
76
+ {/if}
77
+ {/if}
86
78
  </g>
87
79
 
88
80
  <style>
89
- .axis {
90
- font-size: 12px;
91
- }
92
-
93
- .axis-label {
94
- font-size: 14px;
95
- font-weight: 500;
96
- fill: currentColor;
97
- }
98
-
99
- [data-plot-tick-label] {
100
- font-size: 11px;
101
- fill: currentColor;
102
- }
103
- </style>
81
+ .axis {
82
+ font-size: 12px;
83
+ }
84
+
85
+ .axis-label {
86
+ font-size: 14px;
87
+ font-weight: 500;
88
+ fill: currentColor;
89
+ }
90
+
91
+ [data-plot-tick-label] {
92
+ font-size: 11px;
93
+ fill: currentColor;
94
+ }
95
+ </style>
@@ -1,95 +1,95 @@
1
1
  <script>
2
- import { getContext } from 'svelte';
3
- import { ChartBrewer } from '../lib/brewing/index.svelte.js';
2
+ import { getContext } from 'svelte'
3
+ import { ChartBrewer } from '../lib/brewing/index.svelte.js'
4
4
 
5
- let {
6
- x = null,
7
- y = null,
8
- fill = null,
9
- color = "#4682b4",
10
- opacity = 1,
11
- animationDuration = 300,
12
- onClick = null
13
- } = $props();
5
+ let {
6
+ x = null,
7
+ y = null,
8
+ fill = null,
9
+ color = '#4682b4',
10
+ opacity = 1,
11
+ animationDuration = 300,
12
+ onClick = null
13
+ } = $props()
14
14
 
15
- // Get brewer from context
16
- const brewer = getContext('chart-brewer');
17
-
18
- // Set field mappings in the brewer
19
- $effect(() => {
20
- brewer.setFields({
21
- x,
22
- y,
23
- color: fill
24
- });
25
-
26
- // Ensure scales are updated
27
- brewer.createScales();
28
- });
29
-
30
- // Compute bars whenever data or fields change
31
- let bars = $derived(brewer.createBars());
32
-
33
- // Animation transition values
34
- let initialY = $state(0);
35
- let initialHeight = $state(0);
36
-
37
- // Handle resetting animation state for new bars
38
- $effect(() => {
39
- if (bars && bars.length > 0) {
40
- initialY = brewer.getDimensions().innerHeight;
41
- initialHeight = 0;
42
-
43
- // Reset to actual positions after a delay
44
- setTimeout(() => {
45
- initialY = 0;
46
- initialHeight = 0;
47
- }, 10);
48
- }
49
- });
50
-
51
- // Handle bar click
52
- function handleClick(event, bar) {
53
- if (onClick) onClick(bar.data, event);
54
- }
15
+ // Get brewer from context
16
+ const brewer = getContext('chart-brewer')
17
+
18
+ // Set field mappings in the brewer
19
+ $effect(() => {
20
+ brewer.setFields({
21
+ x,
22
+ y,
23
+ color: fill
24
+ })
25
+
26
+ // Ensure scales are updated
27
+ brewer.createScales()
28
+ })
29
+
30
+ // Compute bars whenever data or fields change
31
+ let bars = $derived(brewer.createBars())
32
+
33
+ // Animation transition values
34
+ let initialY = $state(0)
35
+ let initialHeight = $state(0)
36
+
37
+ // Handle resetting animation state for new bars
38
+ $effect(() => {
39
+ if (bars && bars.length > 0) {
40
+ initialY = brewer.getDimensions().innerHeight
41
+ initialHeight = 0
42
+
43
+ // Reset to actual positions after a delay
44
+ setTimeout(() => {
45
+ initialY = 0
46
+ initialHeight = 0
47
+ }, 10)
48
+ }
49
+ })
50
+
51
+ // Handle bar click
52
+ function handleClick(event, bar) {
53
+ if (onClick) onClick(bar.data, event)
54
+ }
55
55
  </script>
56
56
 
57
57
  {#if bars && bars.length > 0}
58
- <g class="chart-bars" data-plot-type="bar">
59
- {#each bars as bar, i (bar.data[x])}
60
- {@const barY = initialY > 0 ? brewer.getDimensions().innerHeight : bar.y}
61
- {@const barHeight = initialHeight > 0 ? 0 : bar.height}
62
-
63
- <rect
64
- class="bar"
65
- x={bar.x}
66
- y={barY}
67
- width={bar.width}
68
- height={barHeight}
69
- fill={bar.color}
70
- opacity={opacity}
71
- on:click={(event) => handleClick(event, bar)}
72
- on:mouseenter={(event) => {
73
- event.target.setAttribute('opacity', Math.min(opacity + 0.2, 1));
74
- }}
75
- on:mouseleave={(event) => {
76
- event.target.setAttribute('opacity', opacity);
77
- }}
78
- style="transition: y {animationDuration}ms ease, height {animationDuration}ms ease;"
79
- role="graphics-symbol"
80
- aria-label="Bar representing {bar.data[x]} with value {bar.data[y]}"
81
- data-plot-element="bar"
82
- data-plot-value={bar.data[y]}
83
- data-plot-category={bar.data[x]}
84
- >
85
- <title>{bar.data[x]}: {bar.data[y]}</title>
86
- </rect>
87
- {/each}
88
- </g>
58
+ <g class="chart-bars" data-plot-type="bar">
59
+ {#each bars as bar, i (bar.data[x])}
60
+ {@const barY = initialY > 0 ? brewer.getDimensions().innerHeight : bar.y}
61
+ {@const barHeight = initialHeight > 0 ? 0 : bar.height}
62
+
63
+ <rect
64
+ class="bar"
65
+ x={bar.x}
66
+ y={barY}
67
+ width={bar.width}
68
+ height={barHeight}
69
+ fill={bar.color}
70
+ {opacity}
71
+ on:click={(event) => handleClick(event, bar)}
72
+ on:mouseenter={(event) => {
73
+ event.target.setAttribute('opacity', Math.min(opacity + 0.2, 1))
74
+ }}
75
+ on:mouseleave={(event) => {
76
+ event.target.setAttribute('opacity', opacity)
77
+ }}
78
+ style="transition: y {animationDuration}ms ease, height {animationDuration}ms ease;"
79
+ role="graphics-symbol"
80
+ aria-label="Bar representing {bar.data[x]} with value {bar.data[y]}"
81
+ data-plot-element="bar"
82
+ data-plot-value={bar.data[y]}
83
+ data-plot-category={bar.data[x]}
84
+ >
85
+ <title>{bar.data[x]}: {bar.data[y]}</title>
86
+ </rect>
87
+ {/each}
88
+ </g>
89
89
  {/if}
90
90
 
91
91
  <style>
92
- .chart-bars .bar {
93
- cursor: pointer;
94
- }
95
- </style>
92
+ .chart-bars .bar {
93
+ cursor: pointer;
94
+ }
95
+ </style>
@@ -1,68 +1,68 @@
1
1
  <script>
2
- import { getContext } from 'svelte';
3
- import { createGrid } from '../lib/brewing/axes.svelte.js';
2
+ import { getContext } from 'svelte'
3
+ import { createGrid } from '../lib/brewing/axes.svelte.js'
4
4
 
5
- let {
6
- direction = 'both',
7
- xTicks = null,
8
- yTicks = null,
9
- color = 'currentColor',
10
- opacity = 0.1,
11
- lineStyle = 'solid'
12
- } = $props();
5
+ let {
6
+ direction = 'both',
7
+ xTicks = null,
8
+ yTicks = null,
9
+ color = 'currentColor',
10
+ opacity = 0.1,
11
+ lineStyle = 'solid'
12
+ } = $props()
13
13
 
14
- // Get brewer from context
15
- const brewer = getContext('chart-brewer');
16
-
17
- // Get grid data
18
- let gridData = $derived(brewer.createGrid({
19
- direction,
20
- xTickCount: xTicks,
21
- yTickCount: yTicks
22
- }));
14
+ // Get brewer from context
15
+ const brewer = getContext('chart-brewer')
23
16
 
24
- // Convert lineStyle to stroke-dasharray
25
- let strokeDasharray = $derived(
26
- lineStyle === 'dashed' ? '5,5' :
27
- lineStyle === 'dotted' ? '1,3' :
28
- 'none'
29
- );
17
+ // Get grid data
18
+ let gridData = $derived(
19
+ brewer.createGrid({
20
+ direction,
21
+ xTickCount: xTicks,
22
+ yTickCount: yTicks
23
+ })
24
+ )
25
+
26
+ // Convert lineStyle to stroke-dasharray
27
+ let strokeDasharray = $derived(
28
+ lineStyle === 'dashed' ? '5,5' : lineStyle === 'dotted' ? '1,3' : 'none'
29
+ )
30
30
  </script>
31
31
 
32
32
  <g class="chart-grid" data-plot-grid={direction}>
33
- {#if direction === 'x' || direction === 'both'}
34
- {#each gridData.xLines as line}
35
- <line
36
- data-plot-grid-line="x"
37
- x1={line.x1}
38
- y1={line.y1}
39
- x2={line.x2}
40
- y2={line.y2}
41
- stroke={color}
42
- stroke-opacity={opacity}
43
- stroke-dasharray={strokeDasharray}
44
- />
45
- {/each}
46
- {/if}
33
+ {#if direction === 'x' || direction === 'both'}
34
+ {#each gridData.xLines as line, index (index)}
35
+ <line
36
+ data-plot-grid-line="x"
37
+ x1={line.x1}
38
+ y1={line.y1}
39
+ x2={line.x2}
40
+ y2={line.y2}
41
+ stroke={color}
42
+ stroke-opacity={opacity}
43
+ stroke-dasharray={strokeDasharray}
44
+ />
45
+ {/each}
46
+ {/if}
47
47
 
48
- {#if direction === 'y' || direction === 'both'}
49
- {#each gridData.yLines as line}
50
- <line
51
- data-plot-grid-line="y"
52
- x1={line.x1}
53
- y1={line.y1}
54
- x2={line.x2}
55
- y2={line.y2}
56
- stroke={color}
57
- stroke-opacity={opacity}
58
- stroke-dasharray={strokeDasharray}
59
- />
60
- {/each}
61
- {/if}
48
+ {#if direction === 'y' || direction === 'both'}
49
+ {#each gridData.yLines as line, index (index)}
50
+ <line
51
+ data-plot-grid-line="y"
52
+ x1={line.x1}
53
+ y1={line.y1}
54
+ x2={line.x2}
55
+ y2={line.y2}
56
+ stroke={color}
57
+ stroke-opacity={opacity}
58
+ stroke-dasharray={strokeDasharray}
59
+ />
60
+ {/each}
61
+ {/if}
62
62
  </g>
63
63
 
64
64
  <style>
65
- .chart-grid {
66
- pointer-events: none;
67
- }
68
- </style>
65
+ .chart-grid {
66
+ pointer-events: none;
67
+ }
68
+ </style>