@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.
- package/README.md +43 -35
- package/package.json +3 -3
- package/src/Plot/Axis.svelte +87 -95
- package/src/Plot/Bar.svelte +86 -86
- package/src/Plot/Grid.svelte +57 -57
- package/src/Plot/Legend.svelte +120 -122
- package/src/Plot/Root.svelte +107 -105
- package/src/elements/ColorRamp.svelte +19 -19
- package/src/elements/ContinuousLegend.svelte +1 -1
- package/src/elements/DefinePatterns.svelte +1 -1
- package/src/elements/DiscreteLegend.svelte +1 -1
- package/src/elements/SymbolGrid.svelte +1 -1
- package/src/examples/BarChartExample.svelte +42 -42
- package/src/index.js +13 -13
- package/src/lib/brewing/axes.svelte.js +120 -122
- package/src/lib/brewing/bars.svelte.js +70 -70
- package/src/lib/brewing/dimensions.svelte.js +25 -23
- package/src/lib/brewing/index.svelte.js +192 -204
- package/src/lib/brewing/legends.svelte.js +51 -52
- package/src/lib/brewing/scales.svelte.js +59 -68
- package/src/lib/brewing/types.js +2 -2
- package/src/lib/context.js +117 -107
- package/src/lib/scales.svelte.js +80 -87
- package/src/lib/utils.js +70 -72
- package/src/old_lib/chart.js +4 -4
- package/src/patterns/Brick.svelte +1 -1
- package/src/patterns/Circles.svelte +1 -1
- package/src/patterns/CrossHatch.svelte +1 -1
- package/src/patterns/Dots.svelte +1 -1
- package/src/patterns/OutlineCircles.svelte +1 -1
- package/src/patterns/Tile.svelte +1 -1
- package/src/patterns/Triangles.svelte +1 -1
- package/src/symbols/RoundedSquare.svelte +0 -1
- package/src/template/shapes/Circles.svelte +1 -1
- package/src/template/shapes/Lines.svelte +1 -1
- package/src/template/shapes/Polygons.svelte +1 -1
package/src/Plot/Legend.svelte
CHANGED
|
@@ -1,129 +1,127 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
2
|
+
import { getContext } from 'svelte'
|
|
3
|
+
import { createLegend, createLegendItemAttributes } from '../lib/brewing/legends.svelte.js'
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
title = '',
|
|
7
|
+
align = 'right',
|
|
8
|
+
verticalAlign = 'top',
|
|
9
|
+
shape = 'rect',
|
|
10
|
+
markerSize = 10,
|
|
11
|
+
onClick = null
|
|
12
|
+
} = $props()
|
|
13
|
+
|
|
14
|
+
// Get brewer from context
|
|
15
|
+
const brewer = getContext('chart-brewer')
|
|
16
|
+
|
|
17
|
+
// Get legend data
|
|
18
|
+
let legendData = $derived(
|
|
19
|
+
brewer.createLegend({
|
|
20
|
+
title,
|
|
21
|
+
align,
|
|
22
|
+
shape,
|
|
23
|
+
markerSize
|
|
24
|
+
})
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
// Track selected items (for filtering)
|
|
28
|
+
let selectedItems = $state([])
|
|
29
|
+
|
|
30
|
+
function toggleItem(item) {
|
|
31
|
+
if (!onClick) return
|
|
32
|
+
|
|
33
|
+
const isSelected = selectedItems.includes(item.value)
|
|
34
|
+
|
|
35
|
+
if (isSelected) {
|
|
36
|
+
selectedItems = selectedItems.filter((v) => v !== item.value)
|
|
37
|
+
} else {
|
|
38
|
+
selectedItems = [...selectedItems, item.value]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onClick(selectedItems)
|
|
42
|
+
}
|
|
41
43
|
</script>
|
|
42
44
|
|
|
43
45
|
{#if legendData.items.length > 0}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
</text>
|
|
103
|
-
</g>
|
|
104
|
-
{/each}
|
|
105
|
-
</g>
|
|
46
|
+
<g class="chart-legend" transform={legendData.transform} data-plot-legend>
|
|
47
|
+
<!-- Legend title -->
|
|
48
|
+
{#if legendData.title}
|
|
49
|
+
<text
|
|
50
|
+
class="legend-title"
|
|
51
|
+
x="0"
|
|
52
|
+
y="-6"
|
|
53
|
+
text-anchor={align === 'left' ? 'start' : align === 'right' ? 'end' : 'middle'}
|
|
54
|
+
data-plot-legend-title
|
|
55
|
+
>
|
|
56
|
+
{legendData.title}
|
|
57
|
+
</text>
|
|
58
|
+
{/if}
|
|
59
|
+
|
|
60
|
+
<!-- Legend items -->
|
|
61
|
+
{#each legendData.items as item, index (index)}
|
|
62
|
+
{@const attrs = createLegendItemAttributes(item)}
|
|
63
|
+
{@const isSelected = selectedItems.includes(item.value)}
|
|
64
|
+
<g
|
|
65
|
+
{...attrs}
|
|
66
|
+
class="legend-item"
|
|
67
|
+
class:selected={isSelected}
|
|
68
|
+
onclick={() => toggleItem(item)}
|
|
69
|
+
>
|
|
70
|
+
<!-- Shape: circle or rect -->
|
|
71
|
+
{#if item.shape === 'circle'}
|
|
72
|
+
<circle
|
|
73
|
+
cx={item.markerSize / 2}
|
|
74
|
+
cy={item.markerSize / 2}
|
|
75
|
+
r={item.markerSize / 2}
|
|
76
|
+
fill={item.color}
|
|
77
|
+
stroke={isSelected ? 'currentColor' : 'none'}
|
|
78
|
+
stroke-width={isSelected ? 1 : 0}
|
|
79
|
+
data-plot-legend-marker="circle"
|
|
80
|
+
/>
|
|
81
|
+
{:else}
|
|
82
|
+
<rect
|
|
83
|
+
width={item.markerSize}
|
|
84
|
+
height={item.markerSize}
|
|
85
|
+
fill={item.color}
|
|
86
|
+
stroke={isSelected ? 'currentColor' : 'none'}
|
|
87
|
+
stroke-width={isSelected ? 1 : 0}
|
|
88
|
+
data-plot-legend-marker="rect"
|
|
89
|
+
/>
|
|
90
|
+
{/if}
|
|
91
|
+
|
|
92
|
+
<!-- Text label -->
|
|
93
|
+
<text
|
|
94
|
+
x={item.markerSize + 5}
|
|
95
|
+
y={item.markerSize - 2}
|
|
96
|
+
text-anchor="start"
|
|
97
|
+
data-plot-legend-label
|
|
98
|
+
>
|
|
99
|
+
{item.value}
|
|
100
|
+
</text>
|
|
101
|
+
</g>
|
|
102
|
+
{/each}
|
|
103
|
+
</g>
|
|
106
104
|
{/if}
|
|
107
105
|
|
|
108
106
|
<style>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
</style>
|
|
107
|
+
.chart-legend {
|
|
108
|
+
font-size: 12px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.legend-title {
|
|
112
|
+
font-weight: bold;
|
|
113
|
+
font-size: 14px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.legend-item {
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.legend-item:hover [data-plot-legend-label] {
|
|
121
|
+
font-weight: 500;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.legend-item.selected [data-plot-legend-label] {
|
|
125
|
+
font-weight: 700;
|
|
126
|
+
}
|
|
127
|
+
</style>
|
package/src/Plot/Root.svelte
CHANGED
|
@@ -1,112 +1,114 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
2
|
+
import { setContext } from 'svelte'
|
|
3
|
+
import { ChartBrewer } from '../lib/brewing/index.svelte.js'
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
data = [],
|
|
7
|
+
width = 600,
|
|
8
|
+
height = 400,
|
|
9
|
+
margin = { top: 20, right: 30, bottom: 40, left: 50 },
|
|
10
|
+
fill = null,
|
|
11
|
+
responsive = true,
|
|
12
|
+
animationDuration = 300
|
|
13
|
+
} = $props()
|
|
14
|
+
|
|
15
|
+
// Create chart brewer instance
|
|
16
|
+
let brewer = $state(
|
|
17
|
+
new ChartBrewer({
|
|
18
|
+
width,
|
|
19
|
+
height,
|
|
20
|
+
margin,
|
|
21
|
+
animationDuration
|
|
22
|
+
})
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
// Chart dimensions derived from brewer
|
|
26
|
+
let dimensions = $derived(brewer.getDimensions())
|
|
27
|
+
|
|
28
|
+
// Process data
|
|
29
|
+
$effect(() => {
|
|
30
|
+
// If data has a select method (dataset object), call it to get actual data
|
|
31
|
+
const chartData = data.select && typeof data.select === 'function' ? data.select() : data
|
|
32
|
+
|
|
33
|
+
// Update brewer with data and fields
|
|
34
|
+
brewer.setData(chartData)
|
|
35
|
+
brewer.setFields({ color: fill })
|
|
36
|
+
|
|
37
|
+
// Create scales after setting data
|
|
38
|
+
brewer.createScales()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Update chart dimensions when props change
|
|
42
|
+
$effect(() => {
|
|
43
|
+
brewer.setDimensions({ width, height, margin })
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// Provide chart context to child components
|
|
47
|
+
setContext('chart-brewer', brewer)
|
|
48
|
+
|
|
49
|
+
// Handle responsive behavior
|
|
50
|
+
let container
|
|
51
|
+
|
|
52
|
+
$effect(() => {
|
|
53
|
+
if (!responsive || !container || !document) return
|
|
54
|
+
|
|
55
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
56
|
+
const entry = entries[0]
|
|
57
|
+
if (!entry) return
|
|
58
|
+
|
|
59
|
+
const containerWidth = entry.contentRect.width
|
|
60
|
+
const aspectRatio = height / width
|
|
61
|
+
|
|
62
|
+
// Update chart dimensions while maintaining aspect ratio
|
|
63
|
+
brewer.setDimensions({
|
|
64
|
+
width: containerWidth,
|
|
65
|
+
height: containerWidth * aspectRatio
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// Update scales after dimensions change
|
|
69
|
+
brewer.createScales()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Start observing container size
|
|
73
|
+
resizeObserver.observe(container)
|
|
74
|
+
|
|
75
|
+
return () => {
|
|
76
|
+
resizeObserver.disconnect()
|
|
77
|
+
}
|
|
78
|
+
})
|
|
77
79
|
</script>
|
|
78
80
|
|
|
79
81
|
<div class="chart-container" bind:this={container} data-plot-root>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
82
|
+
<svg
|
|
83
|
+
width={dimensions.width}
|
|
84
|
+
height={dimensions.height}
|
|
85
|
+
viewBox="0 0 {dimensions.width} {dimensions.height}"
|
|
86
|
+
role="img"
|
|
87
|
+
aria-label="Chart visualization"
|
|
88
|
+
>
|
|
89
|
+
<g
|
|
90
|
+
class="chart-area"
|
|
91
|
+
transform="translate({dimensions.margin.left}, {dimensions.margin.top})"
|
|
92
|
+
data-plot-canvas
|
|
93
|
+
>
|
|
94
|
+
<slot />
|
|
95
|
+
</g>
|
|
96
|
+
</svg>
|
|
95
97
|
</div>
|
|
96
98
|
|
|
97
99
|
<style>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
</style>
|
|
100
|
+
.chart-container {
|
|
101
|
+
position: relative;
|
|
102
|
+
width: 100%;
|
|
103
|
+
height: auto;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
svg {
|
|
107
|
+
display: block;
|
|
108
|
+
overflow: visible;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.chart-area {
|
|
112
|
+
pointer-events: all;
|
|
113
|
+
}
|
|
114
|
+
</style>
|
|
@@ -2,36 +2,36 @@
|
|
|
2
2
|
import { scaleLinear } from 'd3-scale'
|
|
3
3
|
import { id as uniqueId } from '@rokkit/core'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
export let y = 0
|
|
7
|
-
export let textSize = 5
|
|
8
|
-
export let height = 10
|
|
9
|
-
export let width = 100
|
|
10
|
-
export let tickCount = 5
|
|
11
|
-
export let scale
|
|
5
|
+
let { x = 0, y = 0, textSize = 5, height = 10, width = 100, tickCount = 5, scale } = $props()
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
let scaleTicks = $derived(
|
|
8
|
+
scaleLinear()
|
|
9
|
+
.range([x, x + width])
|
|
10
|
+
.domain(scale.domain())
|
|
11
|
+
)
|
|
12
|
+
let scalePercent = $derived(scaleLinear().range([0, 100]).domain(scale.domain()))
|
|
13
|
+
let ticks = $derived(
|
|
14
|
+
scale.ticks.apply(scale, [tickCount]).map((d) => ({ x: scaleTicks(d), value: d }))
|
|
15
|
+
)
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
let colors = $derived(
|
|
18
|
+
ticks.map(({ value }) => ({
|
|
19
|
+
color: scale(value),
|
|
20
|
+
offset: `${scalePercent(value)}%`
|
|
21
|
+
}))
|
|
22
|
+
)
|
|
23
|
+
let id = $state(uniqueId('legend-'))
|
|
24
24
|
</script>
|
|
25
25
|
|
|
26
26
|
<defs>
|
|
27
27
|
<linearGradient {id}>
|
|
28
|
-
{#each colors as { color, offset }}
|
|
28
|
+
{#each colors as { color, offset }, index (index)}
|
|
29
29
|
<stop stop-color={color} {offset} />
|
|
30
30
|
{/each}
|
|
31
31
|
</linearGradient>
|
|
32
32
|
</defs>
|
|
33
33
|
<rect {x} y={y + height} {width} {height} fill="url(#{id})" />
|
|
34
|
-
{#each ticks as { x, value }}
|
|
34
|
+
{#each ticks as { x, value }, index (index)}
|
|
35
35
|
<line x1={x} y1={y + (2 * height) / 3} x2={x} y2={y + height * 2} />
|
|
36
36
|
<text {x} y={y + height / 2} font-size={textSize}>{value}</text>
|
|
37
37
|
{/each}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
</linearGradient>
|
|
28
28
|
</defs>
|
|
29
29
|
<rect {x} y={y + height} {width} {height} fill="url(#{id})" />
|
|
30
|
-
{#each ticks as { x, label }}
|
|
30
|
+
{#each ticks as { x, label }, index (index)}
|
|
31
31
|
<line x1={x} y1={y + (2 * height) / 3} x2={x} y2={y + height * 2} />
|
|
32
32
|
<text {x} y={y + height / 2} font-size={textSize}>{label}</text>
|
|
33
33
|
{/each}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<error> Patterns should be an array and should have unique names for each pattern </error>
|
|
14
14
|
{:else if patterns.length > 0}
|
|
15
15
|
<defs>
|
|
16
|
-
{#each patterns as { id, component, fill, stroke }}
|
|
16
|
+
{#each patterns as { id, component, fill, stroke }, index (index)}
|
|
17
17
|
<pattern {id} {patternUnits} width={size} height={size}>
|
|
18
18
|
<svelte:component this={component} {size} {fill} {stroke} />
|
|
19
19
|
</pattern>
|