@rokkit/chart 1.0.0-next.146 → 1.0.0-next.148
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/dist/Plot/index.d.ts +4 -0
- package/dist/PlotState.svelte.d.ts +47 -0
- package/dist/crossfilter/createCrossFilter.svelte.d.ts +15 -0
- package/dist/geoms/lib/areas.d.ts +52 -0
- package/dist/geoms/lib/bars.d.ts +3 -0
- package/dist/index.d.ts +38 -1
- package/dist/lib/brewing/BoxBrewer.svelte.d.ts +10 -0
- package/dist/lib/brewing/CartesianBrewer.svelte.d.ts +8 -0
- package/dist/lib/brewing/PieBrewer.svelte.d.ts +8 -0
- package/dist/lib/brewing/ViolinBrewer.svelte.d.ts +9 -0
- package/dist/lib/brewing/brewer.svelte.d.ts +145 -0
- package/dist/lib/brewing/colors.d.ts +17 -0
- package/dist/lib/brewing/marks/arcs.d.ts +17 -0
- package/dist/lib/brewing/marks/areas.d.ts +31 -0
- package/dist/lib/brewing/marks/bars.d.ts +1 -0
- package/dist/lib/brewing/marks/boxes.d.ts +24 -0
- package/dist/lib/brewing/marks/lines.d.ts +24 -0
- package/dist/lib/brewing/marks/points.d.ts +40 -0
- package/dist/lib/brewing/marks/violins.d.ts +20 -0
- package/dist/lib/brewing/patterns.d.ts +14 -0
- package/dist/lib/brewing/scales.d.ts +28 -0
- package/dist/lib/brewing/stats.d.ts +31 -0
- package/dist/lib/brewing/symbols.d.ts +7 -0
- package/dist/lib/plot/chartProps.d.ts +177 -0
- package/dist/lib/plot/crossfilter.d.ts +13 -0
- package/dist/lib/plot/facet.d.ts +24 -0
- package/dist/lib/plot/frames.d.ts +45 -0
- package/dist/lib/plot/helpers.d.ts +3 -0
- package/dist/lib/plot/preset.d.ts +29 -0
- package/dist/lib/plot/scales.d.ts +5 -0
- package/dist/lib/plot/stat.d.ts +32 -0
- package/dist/lib/plot/types.d.ts +89 -0
- package/dist/lib/scales.svelte.d.ts +1 -1
- package/dist/lib/swatch.d.ts +12 -0
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/xscale.d.ts +11 -0
- package/dist/patterns/index.d.ts +4 -9
- package/dist/patterns/patterns.d.ts +72 -0
- package/dist/patterns/scale.d.ts +30 -0
- package/package.json +9 -3
- package/src/AnimatedPlot.svelte +194 -0
- package/src/Chart.svelte +101 -0
- package/src/FacetPlot/Panel.svelte +23 -0
- package/src/FacetPlot.svelte +90 -0
- package/src/Plot/Arc.svelte +29 -0
- package/src/Plot/Area.svelte +25 -0
- package/src/Plot/Axis.svelte +62 -84
- package/src/Plot/Grid.svelte +20 -58
- package/src/Plot/Legend.svelte +160 -120
- package/src/Plot/Line.svelte +27 -0
- package/src/Plot/Point.svelte +27 -0
- package/src/Plot/Timeline.svelte +95 -0
- package/src/Plot/Tooltip.svelte +81 -0
- package/src/Plot/index.js +4 -0
- package/src/Plot.svelte +189 -0
- package/src/PlotState.svelte.js +278 -0
- package/src/Sparkline.svelte +69 -0
- package/src/charts/AreaChart.svelte +25 -0
- package/src/charts/BarChart.svelte +26 -0
- package/src/charts/BoxPlot.svelte +21 -0
- package/src/charts/BubbleChart.svelte +23 -0
- package/src/charts/LineChart.svelte +26 -0
- package/src/charts/PieChart.svelte +25 -0
- package/src/charts/ScatterPlot.svelte +25 -0
- package/src/charts/ViolinPlot.svelte +21 -0
- package/src/crossfilter/CrossFilter.svelte +38 -0
- package/src/crossfilter/FilterBar.svelte +32 -0
- package/src/crossfilter/FilterSlider.svelte +79 -0
- package/src/crossfilter/createCrossFilter.svelte.js +113 -0
- package/src/elements/SymbolGrid.svelte +6 -7
- package/src/geoms/Arc.svelte +81 -0
- package/src/geoms/Area.svelte +50 -0
- package/src/geoms/Bar.svelte +142 -0
- package/src/geoms/Box.svelte +101 -0
- package/src/geoms/LabelPill.svelte +17 -0
- package/src/geoms/Line.svelte +100 -0
- package/src/geoms/Point.svelte +100 -0
- package/src/geoms/Violin.svelte +44 -0
- package/src/geoms/lib/areas.js +131 -0
- package/src/geoms/lib/bars.js +172 -0
- package/src/index.js +52 -3
- package/src/lib/brewing/BoxBrewer.svelte.js +56 -0
- package/src/lib/brewing/CartesianBrewer.svelte.js +16 -0
- package/src/lib/brewing/PieBrewer.svelte.js +14 -0
- package/src/lib/brewing/ViolinBrewer.svelte.js +55 -0
- package/src/lib/brewing/brewer.svelte.js +229 -0
- package/src/lib/brewing/colors.js +22 -0
- package/src/lib/brewing/marks/arcs.js +43 -0
- package/src/lib/brewing/marks/areas.js +59 -0
- package/src/lib/brewing/marks/bars.js +49 -0
- package/src/lib/brewing/marks/boxes.js +75 -0
- package/src/lib/brewing/marks/lines.js +48 -0
- package/src/lib/brewing/marks/points.js +57 -0
- package/src/lib/brewing/marks/violins.js +90 -0
- package/src/lib/brewing/patterns.js +31 -0
- package/src/lib/brewing/scales.js +51 -0
- package/src/lib/brewing/scales.svelte.js +2 -26
- package/src/lib/brewing/stats.js +62 -0
- package/src/lib/brewing/symbols.js +10 -0
- package/src/lib/plot/chartProps.js +76 -0
- package/src/lib/plot/crossfilter.js +16 -0
- package/src/lib/plot/facet.js +58 -0
- package/src/lib/plot/frames.js +90 -0
- package/src/lib/plot/helpers.js +14 -0
- package/src/lib/plot/preset.js +53 -0
- package/src/lib/plot/scales.js +56 -0
- package/src/lib/plot/stat.js +92 -0
- package/src/lib/plot/types.js +65 -0
- package/src/lib/scales.svelte.js +2 -26
- package/src/lib/swatch.js +13 -0
- package/src/lib/utils.js +9 -0
- package/src/lib/xscale.js +31 -0
- package/src/patterns/DefinePatterns.svelte +32 -0
- package/src/patterns/PatternDef.svelte +27 -0
- package/src/patterns/index.js +4 -14
- package/src/patterns/patterns.js +208 -0
- package/src/patterns/scale.js +87 -0
- package/src/spec/chart-spec.js +29 -0
- package/src/symbols/Shape.svelte +1 -1
- package/src/symbols/constants/index.js +1 -1
- package/dist/old_lib/index.d.ts +0 -4
- package/dist/old_lib/plots.d.ts +0 -3
- package/dist/old_lib/swatch.d.ts +0 -285
- package/dist/old_lib/utils.d.ts +0 -1
- package/dist/patterns/paths/constants.d.ts +0 -1
- package/dist/template/constants.d.ts +0 -43
- package/dist/template/shapes/index.d.ts +0 -4
- package/src/old_lib/index.js +0 -4
- package/src/old_lib/plots.js +0 -27
- package/src/old_lib/swatch.js +0 -16
- package/src/old_lib/utils.js +0 -8
- package/src/patterns/Brick.svelte +0 -15
- package/src/patterns/Circles.svelte +0 -18
- package/src/patterns/CrossHatch.svelte +0 -12
- package/src/patterns/CurvedWave.svelte +0 -7
- package/src/patterns/Dots.svelte +0 -20
- package/src/patterns/OutlineCircles.svelte +0 -13
- package/src/patterns/Tile.svelte +0 -16
- package/src/patterns/Triangles.svelte +0 -13
- package/src/patterns/Waves.svelte +0 -9
- package/src/patterns/paths/NamedPattern.svelte +0 -9
- package/src/patterns/paths/constants.js +0 -4
- package/src/template/Texture.svelte +0 -13
- package/src/template/constants.js +0 -43
- package/src/template/shapes/Circles.svelte +0 -15
- package/src/template/shapes/Lines.svelte +0 -16
- package/src/template/shapes/Path.svelte +0 -9
- package/src/template/shapes/Polygons.svelte +0 -15
- package/src/template/shapes/index.js +0 -4
- /package/dist/{old_lib → lib}/brewer.d.ts +0 -0
- /package/dist/{old_lib → lib}/chart.d.ts +0 -0
- /package/dist/{old_lib → lib}/grid.d.ts +0 -0
- /package/dist/{old_lib → lib}/ticks.d.ts +0 -0
- /package/src/{old_lib → lib}/brewer.js +0 -0
- /package/src/{old_lib → lib}/chart.js +0 -0
- /package/src/{old_lib → lib}/grid.js +0 -0
- /package/src/{old_lib → lib}/ticks.js +0 -0
package/src/Plot/Grid.svelte
CHANGED
|
@@ -1,68 +1,30 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
3
|
-
import { createGrid } from '../lib/brewing/axes.svelte.js'
|
|
2
|
+
import { getContext } from 'svelte'
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
direction = 'both',
|
|
7
|
-
xTicks = null,
|
|
8
|
-
yTicks = null,
|
|
9
|
-
color = 'currentColor',
|
|
10
|
-
opacity = 0.1,
|
|
11
|
-
lineStyle = 'solid'
|
|
12
|
-
} = $props()
|
|
4
|
+
const state = getContext('plot-state')
|
|
13
5
|
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
const xGridLines = $derived.by(() => {
|
|
7
|
+
const s = state.xScale
|
|
8
|
+
if (!s || typeof s.bandwidth !== 'function') return []
|
|
9
|
+
return s.domain().map((val) => ({ pos: (s(val) ?? 0) + s.bandwidth() / 2 }))
|
|
10
|
+
})
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
)
|
|
12
|
+
const yGridLines = $derived.by(() => {
|
|
13
|
+
const s = state.yScale
|
|
14
|
+
if (!s || typeof s.ticks !== 'function') return []
|
|
15
|
+
return s.ticks(6).map((val) => ({ pos: s(val) }))
|
|
16
|
+
})
|
|
30
17
|
</script>
|
|
31
18
|
|
|
32
|
-
<g class="
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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}
|
|
19
|
+
<g class="grid" data-plot-grid>
|
|
20
|
+
{#each yGridLines as line (line.pos)}
|
|
21
|
+
<line x1="0" y1={line.pos} x2={state.innerWidth} y2={line.pos} data-plot-grid-line />
|
|
22
|
+
{/each}
|
|
23
|
+
{#each xGridLines as line (line.pos)}
|
|
24
|
+
<line x1={line.pos} y1="0" x2={line.pos} y2={state.innerHeight} data-plot-grid-line="x" />
|
|
25
|
+
{/each}
|
|
62
26
|
</g>
|
|
63
27
|
|
|
64
28
|
<style>
|
|
65
|
-
|
|
66
|
-
pointer-events: none;
|
|
67
|
-
}
|
|
29
|
+
[data-plot-grid-line] { stroke: var(--chart-grid-color, currentColor); opacity: 0.15; stroke-dasharray: 2 4; }
|
|
68
30
|
</style>
|
package/src/Plot/Legend.svelte
CHANGED
|
@@ -1,127 +1,167 @@
|
|
|
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
|
-
|
|
2
|
+
import { getContext } from 'svelte'
|
|
3
|
+
import { toPatternId } from '../lib/brewing/patterns.js'
|
|
4
|
+
import { buildSymbolPath } from '../lib/brewing/marks/points.js'
|
|
5
|
+
|
|
6
|
+
/** @type {Record<string, string>} */
|
|
7
|
+
let { labels = {} } = $props()
|
|
8
|
+
|
|
9
|
+
const state = getContext('plot-state')
|
|
10
|
+
|
|
11
|
+
const isCategorical = $derived(state.colorScaleType === 'categorical')
|
|
12
|
+
const isLineGeom = $derived(state.geomTypes?.has('line') ?? false)
|
|
13
|
+
const isPointGeom = $derived(state.geomTypes?.has('point') ?? false)
|
|
14
|
+
const hasSymbols = $derived((state.symbols?.size ?? 0) > 0)
|
|
15
|
+
|
|
16
|
+
// Split conditions
|
|
17
|
+
const splitPattern = $derived(
|
|
18
|
+
!!state.colorField && !!state.patternField && state.colorField !== state.patternField
|
|
19
|
+
)
|
|
20
|
+
const splitSymbol = $derived(
|
|
21
|
+
hasSymbols && !!state.colorField && !!state.symbolField && state.colorField !== state.symbolField
|
|
22
|
+
)
|
|
23
|
+
const symbolOnly = $derived(hasSymbols && !state.colorField)
|
|
24
|
+
|
|
25
|
+
// Color section items — combined with same-field pattern/symbol overlays
|
|
26
|
+
const colorItems = $derived(
|
|
27
|
+
[...(state.colors?.entries() ?? [])].map(([key, entry]) => ({
|
|
28
|
+
key,
|
|
29
|
+
label: labels[String(key)] ?? String(key),
|
|
30
|
+
fill: entry.fill,
|
|
31
|
+
stroke: entry.stroke,
|
|
32
|
+
patternId: !splitPattern && state.patterns?.has(key) ? toPatternId(String(key)) : null,
|
|
33
|
+
symbolShape: !splitSymbol && state.symbols?.get(key) ? state.symbols.get(key) : null
|
|
34
|
+
}))
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
// Pattern section — only when pattern encodes a different field than color
|
|
38
|
+
const patternItems = $derived(
|
|
39
|
+
splitPattern
|
|
40
|
+
? [...(state.patterns?.entries() ?? [])].map(([key]) => ({
|
|
41
|
+
key,
|
|
42
|
+
label: labels[String(key)] ?? String(key),
|
|
43
|
+
patternId: toPatternId(String(key))
|
|
44
|
+
}))
|
|
45
|
+
: []
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
// Symbol section — only when symbol encodes a different field than color, or symbol-only
|
|
49
|
+
const symbolItems = $derived(
|
|
50
|
+
splitSymbol || symbolOnly
|
|
51
|
+
? [...(state.symbols?.entries() ?? [])].map(([key, shape]) => ({
|
|
52
|
+
key,
|
|
53
|
+
label: labels[String(key)] ?? String(key),
|
|
54
|
+
shape,
|
|
55
|
+
fill: state.colors?.get(key)?.fill ?? '#888',
|
|
56
|
+
stroke: state.colors?.get(key)?.stroke ?? '#888'
|
|
57
|
+
}))
|
|
58
|
+
: []
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
const gradientStyle = $derived.by(() => {
|
|
62
|
+
if (isCategorical) return ''
|
|
63
|
+
return `background: linear-gradient(to right, #cfe2f3, #084594)`
|
|
64
|
+
})
|
|
43
65
|
</script>
|
|
44
66
|
|
|
45
|
-
{#if
|
|
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
|
-
|
|
103
|
-
|
|
67
|
+
{#if isCategorical}
|
|
68
|
+
<div class="legend-root" data-plot-legend>
|
|
69
|
+
|
|
70
|
+
<!-- Symbol-only: no color field, just symbol shapes -->
|
|
71
|
+
{#if symbolOnly}
|
|
72
|
+
<div class="legend categorical">
|
|
73
|
+
{#each symbolItems as item (item.key)}
|
|
74
|
+
<div class="legend-item" data-plot-legend-item>
|
|
75
|
+
<svg width="14" height="14" data-plot-legend-swatch>
|
|
76
|
+
<path transform="translate(7,7)" d={buildSymbolPath(item.shape, 5)} fill={item.fill} />
|
|
77
|
+
</svg>
|
|
78
|
+
<span class="label" data-plot-legend-label>{item.label}</span>
|
|
79
|
+
</div>
|
|
80
|
+
{/each}
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<!-- Color section (line, point, or fill swatches) -->
|
|
84
|
+
{:else if colorItems.length > 0}
|
|
85
|
+
<div class="legend categorical">
|
|
86
|
+
{#each colorItems as item (item.key)}
|
|
87
|
+
<div class="legend-item" data-plot-legend-item>
|
|
88
|
+
{#if isLineGeom}
|
|
89
|
+
<!-- Line swatch with optional combined symbol -->
|
|
90
|
+
<svg width="24" height="14" data-plot-legend-swatch>
|
|
91
|
+
<line x1="2" y1="7" x2="22" y2="7" stroke={item.stroke} stroke-width="2" stroke-linecap="round" />
|
|
92
|
+
{#if item.symbolShape}
|
|
93
|
+
<path transform="translate(12,7)" d={buildSymbolPath(item.symbolShape, 4)} fill={item.stroke} />
|
|
94
|
+
{/if}
|
|
95
|
+
</svg>
|
|
96
|
+
{:else if isPointGeom && item.symbolShape}
|
|
97
|
+
<!-- Symbol shape swatch for scatter -->
|
|
98
|
+
<svg width="14" height="14" data-plot-legend-swatch>
|
|
99
|
+
<path transform="translate(7,7)" d={buildSymbolPath(item.symbolShape, 5)} fill={item.fill} />
|
|
100
|
+
</svg>
|
|
101
|
+
{:else if item.patternId}
|
|
102
|
+
<!-- Fill + pattern overlay -->
|
|
103
|
+
<svg width="14" height="14" data-plot-legend-swatch>
|
|
104
|
+
<rect width="14" height="14" fill={item.fill} />
|
|
105
|
+
<rect width="14" height="14" fill="url(#{item.patternId})" />
|
|
106
|
+
</svg>
|
|
107
|
+
{:else}
|
|
108
|
+
<!-- Plain fill swatch -->
|
|
109
|
+
<span class="swatch" style:background-color={item.fill} data-plot-legend-swatch></span>
|
|
110
|
+
{/if}
|
|
111
|
+
<span class="label" data-plot-legend-label>{item.label}</span>
|
|
112
|
+
</div>
|
|
113
|
+
{/each}
|
|
114
|
+
</div>
|
|
115
|
+
{/if}
|
|
116
|
+
|
|
117
|
+
<!-- Pattern section (different field from color) -->
|
|
118
|
+
{#if patternItems.length > 0}
|
|
119
|
+
<div class="legend categorical legend-section">
|
|
120
|
+
{#each patternItems as item (item.key)}
|
|
121
|
+
<div class="legend-item" data-plot-legend-item>
|
|
122
|
+
<svg width="14" height="14" data-plot-legend-swatch>
|
|
123
|
+
<rect width="14" height="14" fill="var(--color-surface-z2, #ccc)" />
|
|
124
|
+
<rect width="14" height="14" fill="url(#{item.patternId})" />
|
|
125
|
+
</svg>
|
|
126
|
+
<span class="label" data-plot-legend-label>{item.label}</span>
|
|
127
|
+
</div>
|
|
128
|
+
{/each}
|
|
129
|
+
</div>
|
|
130
|
+
{/if}
|
|
131
|
+
|
|
132
|
+
<!-- Symbol section (different field from color) -->
|
|
133
|
+
{#if symbolItems.length > 0 && !symbolOnly}
|
|
134
|
+
<div class="legend categorical legend-section">
|
|
135
|
+
{#each symbolItems as item (item.key)}
|
|
136
|
+
<div class="legend-item" data-plot-legend-item>
|
|
137
|
+
{#if isLineGeom}
|
|
138
|
+
<svg width="24" height="14" data-plot-legend-swatch>
|
|
139
|
+
<line x1="2" y1="7" x2="22" y2="7" stroke={item.stroke ?? item.fill} stroke-width="2" stroke-linecap="round" stroke-dasharray="4 2" />
|
|
140
|
+
<path transform="translate(12,7)" d={buildSymbolPath(item.shape, 4)} fill={item.stroke ?? item.fill} />
|
|
141
|
+
</svg>
|
|
142
|
+
{:else}
|
|
143
|
+
<svg width="14" height="14" data-plot-legend-swatch>
|
|
144
|
+
<path transform="translate(7,7)" d={buildSymbolPath(item.shape, 5)} fill={item.fill} />
|
|
145
|
+
</svg>
|
|
146
|
+
{/if}
|
|
147
|
+
<span class="label" data-plot-legend-label>{item.label}</span>
|
|
148
|
+
</div>
|
|
149
|
+
{/each}
|
|
150
|
+
</div>
|
|
151
|
+
{/if}
|
|
152
|
+
|
|
153
|
+
</div>
|
|
154
|
+
{:else}
|
|
155
|
+
<div class="legend gradient" data-plot-legend>
|
|
156
|
+
<div class="gradient-bar" style={gradientStyle} data-plot-legend-gradient></div>
|
|
157
|
+
</div>
|
|
104
158
|
{/if}
|
|
105
159
|
|
|
106
160
|
<style>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
}
|
|
161
|
+
.legend-root { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
|
|
162
|
+
.legend { display: flex; flex-wrap: wrap; gap: 8px; font-size: 12px; }
|
|
163
|
+
.legend-section { border-top: 1px solid var(--color-surface-z3, #e0e0e0); padding-top: 6px; }
|
|
164
|
+
.legend-item { display: flex; align-items: center; gap: 4px; }
|
|
165
|
+
.swatch { display: inline-block; width: 14px; height: 14px; border-radius: 2px; flex-shrink: 0; }
|
|
166
|
+
.gradient-bar { width: 180px; height: 14px; border-radius: 2px; }
|
|
127
167
|
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from 'svelte'
|
|
3
|
+
|
|
4
|
+
const brewer = getContext('chart-brewer')
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
{#if brewer && brewer.lines && brewer.lines.length > 0}
|
|
8
|
+
<g class="chart-lines" data-plot-type="line">
|
|
9
|
+
{#each brewer.lines as seg (seg.key ?? seg.d)}
|
|
10
|
+
<path
|
|
11
|
+
d={seg.d}
|
|
12
|
+
fill={seg.fill}
|
|
13
|
+
stroke={seg.stroke}
|
|
14
|
+
stroke-width="2"
|
|
15
|
+
stroke-linejoin="round"
|
|
16
|
+
stroke-linecap="round"
|
|
17
|
+
data-plot-element="line"
|
|
18
|
+
/>
|
|
19
|
+
{/each}
|
|
20
|
+
</g>
|
|
21
|
+
{/if}
|
|
22
|
+
|
|
23
|
+
<style>
|
|
24
|
+
.chart-lines {
|
|
25
|
+
pointer-events: none;
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from 'svelte'
|
|
3
|
+
|
|
4
|
+
const brewer = getContext('chart-brewer')
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
{#if brewer && brewer.points && brewer.points.length > 0}
|
|
8
|
+
<g class="chart-points" data-plot-type="point">
|
|
9
|
+
{#each brewer.points as pt, i (i)}
|
|
10
|
+
<circle
|
|
11
|
+
cx={pt.cx}
|
|
12
|
+
cy={pt.cy}
|
|
13
|
+
r={pt.r}
|
|
14
|
+
fill={pt.fill}
|
|
15
|
+
stroke={pt.stroke}
|
|
16
|
+
stroke-width="1"
|
|
17
|
+
data-plot-element="point"
|
|
18
|
+
/>
|
|
19
|
+
{/each}
|
|
20
|
+
</g>
|
|
21
|
+
{/if}
|
|
22
|
+
|
|
23
|
+
<style>
|
|
24
|
+
.chart-points {
|
|
25
|
+
pointer-events: none;
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/**
|
|
3
|
+
* @type {{
|
|
4
|
+
* frameKeys: unknown[],
|
|
5
|
+
* currentIndex: number,
|
|
6
|
+
* playing: boolean,
|
|
7
|
+
* speed: number,
|
|
8
|
+
* onplay: () => void,
|
|
9
|
+
* onpause: () => void,
|
|
10
|
+
* onscrub: (index: number) => void,
|
|
11
|
+
* onspeed: (speed: number) => void
|
|
12
|
+
* }}
|
|
13
|
+
*/
|
|
14
|
+
let {
|
|
15
|
+
frameKeys = [],
|
|
16
|
+
currentIndex = 0,
|
|
17
|
+
playing = false,
|
|
18
|
+
speed = 1,
|
|
19
|
+
onplay,
|
|
20
|
+
onpause,
|
|
21
|
+
onscrub,
|
|
22
|
+
onspeed
|
|
23
|
+
} = $props()
|
|
24
|
+
|
|
25
|
+
const safeIndex = $derived(
|
|
26
|
+
frameKeys.length === 0 ? 0 : Math.min(currentIndex, frameKeys.length - 1)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const SPEEDS = [0.5, 1, 1.5, 2, 4]
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div class="timeline" data-plot-timeline>
|
|
33
|
+
<!-- Play / Pause -->
|
|
34
|
+
<button
|
|
35
|
+
class="play-pause"
|
|
36
|
+
aria-label={playing ? 'Pause' : 'Play'}
|
|
37
|
+
onclick={() => playing ? onpause?.() : onplay?.()}
|
|
38
|
+
disabled={frameKeys.length === 0}
|
|
39
|
+
data-plot-timeline-playpause
|
|
40
|
+
>
|
|
41
|
+
{playing ? '⏸' : '▶'}
|
|
42
|
+
</button>
|
|
43
|
+
|
|
44
|
+
<!-- Frame label -->
|
|
45
|
+
<span class="frame-label" data-plot-timeline-label>{frameKeys[safeIndex] ?? ''}</span>
|
|
46
|
+
|
|
47
|
+
<!-- Scrub slider -->
|
|
48
|
+
<input
|
|
49
|
+
type="range"
|
|
50
|
+
min="0"
|
|
51
|
+
max={Math.max(0, frameKeys.length - 1)}
|
|
52
|
+
value={safeIndex}
|
|
53
|
+
disabled={frameKeys.length === 0}
|
|
54
|
+
class="scrub"
|
|
55
|
+
aria-label="Animation timeline"
|
|
56
|
+
oninput={(e) => onscrub?.(Number(e.currentTarget.value))}
|
|
57
|
+
data-plot-timeline-scrub
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
<!-- Speed selector -->
|
|
61
|
+
<select
|
|
62
|
+
aria-label="Playback speed"
|
|
63
|
+
value={speed}
|
|
64
|
+
onchange={(e) => onspeed?.(Number(e.currentTarget.value))}
|
|
65
|
+
data-plot-timeline-speed
|
|
66
|
+
>
|
|
67
|
+
{#each SPEEDS as s (s)}
|
|
68
|
+
<option value={s}>{s}×</option>
|
|
69
|
+
{/each}
|
|
70
|
+
</select>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<style>
|
|
74
|
+
.timeline {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: 8px;
|
|
78
|
+
padding: 8px 0;
|
|
79
|
+
font-size: 12px;
|
|
80
|
+
}
|
|
81
|
+
.play-pause {
|
|
82
|
+
font-size: 16px;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
background: none;
|
|
85
|
+
border: none;
|
|
86
|
+
padding: 0;
|
|
87
|
+
}
|
|
88
|
+
.scrub {
|
|
89
|
+
flex: 1;
|
|
90
|
+
}
|
|
91
|
+
.frame-label {
|
|
92
|
+
min-width: 4ch;
|
|
93
|
+
text-align: right;
|
|
94
|
+
}
|
|
95
|
+
</style>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from 'svelte'
|
|
3
|
+
|
|
4
|
+
/** @type {{ tooltip?: boolean | ((data: Record<string, unknown>) => string) }} */
|
|
5
|
+
let { tooltip = true } = $props()
|
|
6
|
+
|
|
7
|
+
const plotState = getContext('plot-state')
|
|
8
|
+
|
|
9
|
+
let mouseX = $state(0)
|
|
10
|
+
let mouseY = $state(0)
|
|
11
|
+
|
|
12
|
+
$effect(() => {
|
|
13
|
+
function onMove(e) { mouseX = e.clientX; mouseY = e.clientY }
|
|
14
|
+
window.addEventListener('mousemove', onMove)
|
|
15
|
+
return () => window.removeEventListener('mousemove', onMove)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const hovered = $derived(plotState.hovered)
|
|
19
|
+
const mode = $derived(plotState.mode)
|
|
20
|
+
|
|
21
|
+
function formatDefault(data) {
|
|
22
|
+
return Object.entries(data)
|
|
23
|
+
.filter(([, v]) => v !== undefined && v !== null)
|
|
24
|
+
.map(([k, v]) => `<div class="tt-row"><span class="tt-key">${k}</span><span class="tt-val">${v}</span></div>`)
|
|
25
|
+
.join('')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const content = $derived.by(() => {
|
|
29
|
+
if (!hovered) return ''
|
|
30
|
+
if (typeof tooltip === 'function') return String(tooltip(hovered) ?? '')
|
|
31
|
+
return formatDefault(hovered)
|
|
32
|
+
})
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
{#if hovered && content}
|
|
36
|
+
<div
|
|
37
|
+
class="plot-tooltip"
|
|
38
|
+
data-mode={mode}
|
|
39
|
+
style:left="{mouseX + 14}px"
|
|
40
|
+
style:top="{mouseY - 8}px"
|
|
41
|
+
>
|
|
42
|
+
{@html content}
|
|
43
|
+
</div>
|
|
44
|
+
{/if}
|
|
45
|
+
|
|
46
|
+
<style>
|
|
47
|
+
.plot-tooltip {
|
|
48
|
+
position: fixed;
|
|
49
|
+
pointer-events: none;
|
|
50
|
+
z-index: 1000;
|
|
51
|
+
padding: 8px 10px;
|
|
52
|
+
border-radius: 6px;
|
|
53
|
+
font-size: 12px;
|
|
54
|
+
line-height: 1.5;
|
|
55
|
+
white-space: nowrap;
|
|
56
|
+
box-shadow: 0 4px 12px rgb(0 0 0 / 0.12);
|
|
57
|
+
background: #ffffff;
|
|
58
|
+
border: 1px solid #e2e8f0;
|
|
59
|
+
color: #1e293b;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.plot-tooltip[data-mode="dark"] {
|
|
63
|
+
background: #1e293b;
|
|
64
|
+
border-color: #334155;
|
|
65
|
+
color: #f1f5f9;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
:global(.tt-row) {
|
|
69
|
+
display: flex;
|
|
70
|
+
gap: 10px;
|
|
71
|
+
justify-content: space-between;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
:global(.tt-key) {
|
|
75
|
+
opacity: 0.65;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
:global(.tt-val) {
|
|
79
|
+
font-weight: 600;
|
|
80
|
+
}
|
|
81
|
+
</style>
|
package/src/Plot/index.js
CHANGED
|
@@ -3,3 +3,7 @@ export { default as Axis } from './Axis.svelte'
|
|
|
3
3
|
export { default as Bar } from './Bar.svelte'
|
|
4
4
|
export { default as Legend } from './Legend.svelte'
|
|
5
5
|
export { default as Grid } from './Grid.svelte'
|
|
6
|
+
export { default as Line } from './Line.svelte'
|
|
7
|
+
export { default as Area } from './Area.svelte'
|
|
8
|
+
export { default as Point } from './Point.svelte'
|
|
9
|
+
export { default as Arc } from './Arc.svelte'
|