@rokkit/chart 1.1.17 → 1.1.19
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/AnimatedPlot.svelte.d.ts +35 -0
- package/dist/Chart.svelte.d.ts +21 -0
- package/dist/ChartExporter.svelte.d.ts +22 -0
- package/dist/ChartLegend.svelte.d.ts +30 -0
- package/dist/ChartProvider.svelte.d.ts +10 -0
- package/dist/FacetPlot/Panel.svelte.d.ts +22 -0
- package/dist/FacetPlot.svelte.d.ts +32 -0
- package/dist/Plot/Arc.svelte.d.ts +18 -0
- package/dist/Plot/Area.svelte.d.ts +14 -0
- package/dist/Plot/Axis.svelte.d.ts +13 -0
- package/dist/Plot/Bar.svelte.d.ts +11 -0
- package/dist/Plot/Grid.svelte.d.ts +3 -0
- package/dist/Plot/Legend.svelte.d.ts +6 -0
- package/dist/Plot/Line.svelte.d.ts +13 -0
- package/dist/Plot/Point.svelte.d.ts +13 -0
- package/dist/Plot/Root.svelte.d.ts +21 -0
- package/dist/Plot/Timeline.svelte.d.ts +13 -0
- package/dist/Plot/Tooltip.svelte.d.ts +7 -0
- package/dist/Plot.svelte.d.ts +34 -0
- package/dist/PlotState.svelte.d.ts +0 -3
- package/dist/Sparkline.svelte.d.ts +16 -0
- package/dist/Symbol.svelte.d.ts +12 -0
- package/dist/Texture.svelte.d.ts +12 -0
- package/dist/charts/AreaChart.svelte.d.ts +25 -0
- package/dist/charts/BarChart.svelte.d.ts +26 -0
- package/dist/charts/BoxPlot.svelte.d.ts +18 -0
- package/dist/charts/BubbleChart.svelte.d.ts +23 -0
- package/dist/charts/LineChart.svelte.d.ts +26 -0
- package/dist/charts/PieChart.svelte.d.ts +19 -0
- package/dist/charts/ScatterPlot.svelte.d.ts +29 -0
- package/dist/charts/ViolinPlot.svelte.d.ts +18 -0
- package/dist/crossfilter/CrossFilter.svelte.d.ts +12 -0
- package/dist/crossfilter/FilterBar.svelte.d.ts +12 -0
- package/dist/crossfilter/FilterHistogram.svelte.d.ts +19 -0
- package/dist/crossfilter/FilterSlider.svelte.d.ts +15 -0
- package/dist/elements/Bar.svelte.d.ts +17 -0
- package/dist/elements/ColorRamp.svelte.d.ts +17 -0
- package/dist/elements/ContinuousLegend.svelte.d.ts +18 -0
- package/dist/elements/DefinePatterns.svelte.d.ts +21 -0
- package/dist/elements/DiscreteLegend.svelte.d.ts +17 -0
- package/dist/elements/Label.svelte.d.ts +11 -0
- package/dist/elements/SymbolGrid.svelte.d.ts +8 -0
- package/dist/examples/BarChartExample.svelte.d.ts +18 -0
- package/dist/geoms/Arc.svelte.d.ts +20 -0
- package/dist/geoms/Area.svelte.d.ts +16 -0
- package/dist/geoms/Bar.svelte.d.ts +23 -0
- package/dist/geoms/Box.svelte.d.ts +12 -0
- package/dist/geoms/Candlestick.svelte.d.ts +20 -0
- package/dist/geoms/Heatmap.svelte.d.ts +13 -0
- package/dist/geoms/Hexbin.svelte.d.ts +13 -0
- package/dist/geoms/LabelPill.svelte.d.ts +9 -0
- package/dist/geoms/Line.svelte.d.ts +24 -0
- package/dist/geoms/Point.svelte.d.ts +30 -0
- package/dist/geoms/Ribbon.svelte.d.ts +16 -0
- package/dist/geoms/Violin.svelte.d.ts +12 -0
- package/dist/geoms/Waterfall.svelte.d.ts +18 -0
- package/dist/geoms/lib/areas.d.ts +5 -3
- package/dist/lib/brewing/brewer.svelte.d.ts +24 -15
- package/dist/lib/brewing/marks/arcs.d.ts +16 -6
- package/dist/lib/brewing/marks/areas.d.ts +2 -2
- package/dist/lib/brewing/marks/boxes.d.ts +3 -3
- package/dist/lib/brewing/marks/lines.d.ts +6 -6
- package/dist/lib/brewing/marks/points.d.ts +13 -12
- package/dist/lib/brewing/marks/violins.d.ts +3 -3
- package/dist/lib/brewing/scales.d.ts +3 -3
- package/dist/lib/chart.d.ts +2 -2
- package/dist/lib/keyboard-nav.d.ts +1 -1
- package/dist/lib/plot/facet.d.ts +2 -2
- package/dist/lib/plot/frames.d.ts +9 -9
- package/dist/lib/plot/scales.d.ts +3 -3
- package/dist/lib/plot/types.d.ts +7 -4
- package/dist/lib/scales.svelte.d.ts +2 -2
- package/dist/lib/utils.d.ts +7 -1
- package/dist/lib/xscale.d.ts +1 -1
- package/dist/patterns/DefinePatterns.svelte.d.ts +7 -0
- package/dist/patterns/PatternDef.svelte.d.ts +11 -0
- package/dist/spec/chart-spec.d.ts +22 -0
- package/dist/symbols/RoundedSquare.svelte.d.ts +11 -0
- package/dist/symbols/Shape.svelte.d.ts +18 -0
- package/dist/symbols/constants/index.d.ts +1 -1
- package/dist/symbols/index.d.ts +2 -2
- package/dist/symbols/outline.svelte.d.ts +18 -0
- package/dist/symbols/solid.svelte.d.ts +18 -0
- package/package.json +22 -3
- package/src/AnimatedPlot.svelte +49 -43
- package/src/Chart.svelte +34 -22
- package/src/ChartExporter.svelte +19 -16
- package/src/ChartLegend.svelte +24 -20
- package/src/ChartProvider.svelte +10 -2
- package/src/FacetPlot/Panel.svelte +23 -3
- package/src/FacetPlot.svelte +29 -26
- package/src/Plot/Arc.svelte +3 -2
- package/src/Plot/Area.svelte +30 -8
- package/src/Plot/Axis.svelte +26 -45
- package/src/Plot/Bar.svelte +20 -5
- package/src/Plot/Grid.svelte +5 -4
- package/src/Plot/Legend.svelte +8 -4
- package/src/Plot/Line.svelte +24 -7
- package/src/Plot/Point.svelte +24 -6
- package/src/Plot/Root.svelte +17 -2
- package/src/Plot/Timeline.svelte +13 -14
- package/src/Plot/Tooltip.svelte +12 -6
- package/src/Plot.svelte +58 -40
- package/src/PlotState.svelte.js +6 -2
- package/src/Sparkline.svelte +22 -8
- package/src/Symbol.svelte +15 -5
- package/src/Texture.svelte +12 -2
- package/src/charts/AreaChart.svelte +26 -3
- package/src/charts/BarChart.svelte +27 -3
- package/src/charts/BoxPlot.svelte +19 -3
- package/src/charts/BubbleChart.svelte +24 -3
- package/src/charts/LineChart.svelte +27 -3
- package/src/charts/PieChart.svelte +20 -3
- package/src/charts/ScatterPlot.svelte +27 -3
- package/src/charts/ViolinPlot.svelte +19 -3
- package/src/crossfilter/CrossFilter.svelte +13 -10
- package/src/crossfilter/FilterBar.svelte +14 -3
- package/src/crossfilter/FilterHistogram.svelte +49 -24
- package/src/crossfilter/FilterSlider.svelte +16 -5
- package/src/elements/Bar.svelte +16 -2
- package/src/elements/ColorRamp.svelte +28 -4
- package/src/elements/ContinuousLegend.svelte +20 -3
- package/src/elements/DefinePatterns.svelte +23 -7
- package/src/elements/DiscreteLegend.svelte +18 -2
- package/src/elements/Label.svelte +11 -2
- package/src/elements/SymbolGrid.svelte +13 -4
- package/src/examples/BarChartExample.svelte +12 -15
- package/src/geoms/Arc.svelte +18 -5
- package/src/geoms/Area.svelte +38 -20
- package/src/geoms/Bar.svelte +40 -23
- package/src/geoms/Box.svelte +13 -4
- package/src/geoms/Candlestick.svelte +27 -7
- package/src/geoms/Heatmap.svelte +20 -9
- package/src/geoms/Hexbin.svelte +35 -13
- package/src/geoms/LabelPill.svelte +9 -3
- package/src/geoms/Line.svelte +27 -10
- package/src/geoms/Point.svelte +33 -13
- package/src/geoms/Ribbon.svelte +26 -8
- package/src/geoms/Violin.svelte +13 -4
- package/src/geoms/Waterfall.svelte +32 -12
- package/src/geoms/lib/areas.js +2 -2
- package/src/lib/brewing/marks/arcs.js +3 -2
- package/src/lib/brewing/marks/boxes.js +1 -1
- package/src/lib/brewing/marks/lines.js +2 -2
- package/src/lib/brewing/marks/points.js +3 -2
- package/src/lib/brewing/marks/violins.js +1 -1
- package/src/lib/plot/facet.js +1 -1
- package/src/lib/plot/frames.js +4 -4
- package/src/lib/plot/types.js +6 -3
- package/src/lib/utils.js +7 -1
- package/src/patterns/DefinePatterns.svelte +9 -4
- package/src/patterns/PatternDef.svelte +11 -3
- package/src/symbols/RoundedSquare.svelte +15 -4
- package/src/symbols/Shape.svelte +21 -3
- package/src/symbols/outline.svelte +2 -0
- package/src/symbols/solid.svelte +2 -0
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { setContext, untrack } from 'svelte'
|
|
3
|
+
import type { Snippet } from 'svelte'
|
|
3
4
|
import { createCrossFilter } from './createCrossFilter.svelte.js'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
type CrossFilter = ReturnType<typeof createCrossFilter>
|
|
7
|
+
type FilterState = CrossFilter['filters']
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
crossfilter?: CrossFilter
|
|
11
|
+
mode?: 'dim' | 'hide'
|
|
12
|
+
filters?: FilterState
|
|
13
|
+
children?: Snippet
|
|
14
|
+
}
|
|
15
|
+
|
|
13
16
|
let {
|
|
14
17
|
crossfilter: externalCf = undefined,
|
|
15
18
|
mode = 'dim',
|
|
16
19
|
filters = $bindable(),
|
|
17
20
|
children
|
|
18
|
-
} = $props()
|
|
21
|
+
}: Props = $props()
|
|
19
22
|
|
|
20
23
|
// Use an externally provided instance (spec/helpers API) or create one internally.
|
|
21
24
|
// untrack() suppresses "captures initial value" warning — intentional: the cf
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import PlotChart from '../Plot.svelte'
|
|
3
3
|
import Bar from '../geoms/Bar.svelte'
|
|
4
|
+
import type { PlotSpec } from '../lib/plot/types.js'
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
data?: Record<string, unknown>[]
|
|
8
|
+
field?: string
|
|
9
|
+
valueField?: string
|
|
10
|
+
stat?: string
|
|
11
|
+
width?: number
|
|
12
|
+
height?: number
|
|
13
|
+
mode?: 'light' | 'dark'
|
|
14
|
+
}
|
|
4
15
|
|
|
5
16
|
let {
|
|
6
17
|
data = [],
|
|
@@ -10,9 +21,9 @@
|
|
|
10
21
|
width = 300,
|
|
11
22
|
height = 120,
|
|
12
23
|
mode = 'light'
|
|
13
|
-
} = $props()
|
|
24
|
+
}: Props = $props()
|
|
14
25
|
|
|
15
|
-
const spec = $derived({
|
|
26
|
+
const spec = $derived<PlotSpec>({
|
|
16
27
|
x: field,
|
|
17
28
|
y: valueField
|
|
18
29
|
})
|
|
@@ -1,15 +1,29 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
// @ts-nocheck
|
|
1
|
+
<script lang="ts">
|
|
3
2
|
import { getContext } from 'svelte'
|
|
4
|
-
import { bin, extent, max } from 'd3-array'
|
|
3
|
+
import { bin, extent, max, type Bin } from 'd3-array'
|
|
5
4
|
import { scaleLinear } from 'd3-scale'
|
|
6
5
|
import { format } from 'd3-format'
|
|
6
|
+
import type { createCrossFilter } from './createCrossFilter.svelte.js'
|
|
7
|
+
|
|
8
|
+
type Row = Record<string, unknown>
|
|
9
|
+
type CrossFilter = ReturnType<typeof createCrossFilter>
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Interactive histogram with brush selection for crossfilter range dimensions.
|
|
10
13
|
* Renders bins as bars; mouse drag selects a range and calls cf.setRange().
|
|
11
14
|
* Click without dragging clears the filter.
|
|
12
15
|
*/
|
|
16
|
+
type Props = {
|
|
17
|
+
data?: Row[]
|
|
18
|
+
field?: string
|
|
19
|
+
label?: string
|
|
20
|
+
bins?: number
|
|
21
|
+
width?: number
|
|
22
|
+
height?: number
|
|
23
|
+
color?: string
|
|
24
|
+
dimColor?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
13
27
|
let {
|
|
14
28
|
data = [],
|
|
15
29
|
field = '',
|
|
@@ -19,9 +33,9 @@
|
|
|
19
33
|
height = 120,
|
|
20
34
|
color = 'rgb(var(--color-primary, 249 115 22))',
|
|
21
35
|
dimColor = 'rgb(var(--color-surface-z3, 203 213 225))'
|
|
22
|
-
} = $props()
|
|
36
|
+
}: Props = $props()
|
|
23
37
|
|
|
24
|
-
const cf = getContext('crossfilter')
|
|
38
|
+
const cf = getContext<CrossFilter | undefined>('crossfilter')
|
|
25
39
|
|
|
26
40
|
const margin = { top: 8, right: 10, bottom: 22, left: 32 }
|
|
27
41
|
|
|
@@ -30,12 +44,16 @@
|
|
|
30
44
|
|
|
31
45
|
// Compute histogram bins from data
|
|
32
46
|
const binner = $derived(
|
|
33
|
-
bin()
|
|
34
|
-
.value((d) => d[field])
|
|
47
|
+
bin<Row, number>()
|
|
48
|
+
.value((d) => Number(d[field]))
|
|
35
49
|
.thresholds(binCount)
|
|
36
50
|
)
|
|
37
|
-
const binned = $derived(data.length > 0 ? binner(data) : [])
|
|
38
|
-
const domainExtent = $derived
|
|
51
|
+
const binned = $derived<Bin<Row, number>[]>(data.length > 0 ? binner(data) : [])
|
|
52
|
+
const domainExtent = $derived.by<[number, number]>(() => {
|
|
53
|
+
if (data.length === 0) return [0, 1]
|
|
54
|
+
const [lo, hi] = extent(data, (d) => Number(d[field]))
|
|
55
|
+
return [lo ?? 0, hi ?? 1]
|
|
56
|
+
})
|
|
39
57
|
|
|
40
58
|
// Scales
|
|
41
59
|
const xScale = $derived(
|
|
@@ -53,12 +71,17 @@
|
|
|
53
71
|
: scaleLinear().domain([0, 1]).range([innerHeight, 0])
|
|
54
72
|
)
|
|
55
73
|
|
|
56
|
-
// Active range from crossfilter
|
|
57
|
-
|
|
74
|
+
// Active range from crossfilter — the histogram only operates on continuous
|
|
75
|
+
// [min, max] dimensions, so ignore categorical Set filters.
|
|
76
|
+
const activeRange = $derived.by<[number, number] | null>(() => {
|
|
77
|
+
const f = cf?.filters?.get(field)
|
|
78
|
+
if (Array.isArray(f) && f.length === 2) return [f[0], f[1]]
|
|
79
|
+
return null
|
|
80
|
+
})
|
|
58
81
|
|
|
59
82
|
// Brush state
|
|
60
|
-
let brushStartPx = $state(null)
|
|
61
|
-
let brushEndPx = $state(null)
|
|
83
|
+
let brushStartPx = $state<number | null>(null)
|
|
84
|
+
let brushEndPx = $state<number | null>(null)
|
|
62
85
|
let brushing = $state(false)
|
|
63
86
|
|
|
64
87
|
// Brush rect in pixel space
|
|
@@ -77,32 +100,34 @@
|
|
|
77
100
|
return { x: lo, width: Math.max(1, hi - lo) }
|
|
78
101
|
})
|
|
79
102
|
|
|
80
|
-
function pixelToValue(px) {
|
|
103
|
+
function pixelToValue(px: number) {
|
|
81
104
|
return xScale.invert(Math.max(0, Math.min(innerWidth, px)))
|
|
82
105
|
}
|
|
83
106
|
|
|
84
|
-
function getLocalX(e) {
|
|
107
|
+
function getLocalX(e: MouseEvent & { currentTarget: Element }) {
|
|
85
108
|
const svgEl = e.currentTarget.closest('svg')
|
|
109
|
+
if (!svgEl) return 0
|
|
86
110
|
const rect = svgEl.getBoundingClientRect()
|
|
87
111
|
return e.clientX - rect.left - margin.left
|
|
88
112
|
}
|
|
89
113
|
|
|
90
|
-
function onMouseDown(e) {
|
|
114
|
+
function onMouseDown(e: MouseEvent & { currentTarget: Element }) {
|
|
91
115
|
brushStartPx = getLocalX(e)
|
|
92
116
|
brushEndPx = brushStartPx
|
|
93
117
|
brushing = true
|
|
94
118
|
}
|
|
95
119
|
|
|
96
|
-
function onMouseMove(e) {
|
|
120
|
+
function onMouseMove(e: MouseEvent & { currentTarget: Element }) {
|
|
97
121
|
if (!brushing) return
|
|
98
122
|
brushEndPx = getLocalX(e)
|
|
99
123
|
}
|
|
100
124
|
|
|
101
125
|
function onMouseUp() {
|
|
102
|
-
if (!brushing) return
|
|
126
|
+
if (!brushing || brushStartPx === null) return
|
|
103
127
|
brushing = false
|
|
104
|
-
const
|
|
105
|
-
const
|
|
128
|
+
const end = brushEndPx ?? brushStartPx
|
|
129
|
+
const lo = Math.min(brushStartPx, end)
|
|
130
|
+
const hi = Math.max(brushStartPx, end)
|
|
106
131
|
if (hi - lo < 3) {
|
|
107
132
|
// Treat as click — clear filter
|
|
108
133
|
cf?.clearFilter(field)
|
|
@@ -118,10 +143,10 @@
|
|
|
118
143
|
}
|
|
119
144
|
|
|
120
145
|
// Check if a bin is within active range
|
|
121
|
-
function binInRange(b) {
|
|
146
|
+
function binInRange(b: Bin<Row, number>) {
|
|
122
147
|
if (!activeRange) return true
|
|
123
148
|
const [lo, hi] = activeRange
|
|
124
|
-
return b.x1 > lo && b.x0 < hi
|
|
149
|
+
return (b.x1 ?? 0) > lo && (b.x0 ?? 0) < hi
|
|
125
150
|
}
|
|
126
151
|
|
|
127
152
|
// Y-axis tick values (2-3 ticks)
|
|
@@ -195,8 +220,8 @@
|
|
|
195
220
|
|
|
196
221
|
<!-- Bars -->
|
|
197
222
|
{#each binned as b (b.x0)}
|
|
198
|
-
{@const bx = xScale(b.x0) + 1}
|
|
199
|
-
{@const bw = Math.max(0, xScale(b.x1) - xScale(b.x0) - 1)}
|
|
223
|
+
{@const bx = xScale(b.x0 ?? 0) + 1}
|
|
224
|
+
{@const bw = Math.max(0, xScale(b.x1 ?? 0) - xScale(b.x0 ?? 0) - 1)}
|
|
200
225
|
{@const by = yScale(b.length)}
|
|
201
226
|
{@const bh = innerHeight - by}
|
|
202
227
|
<rect
|
|
@@ -1,14 +1,25 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { getContext } from 'svelte'
|
|
3
|
+
import type { createCrossFilter } from './createCrossFilter.svelte.js'
|
|
4
|
+
|
|
5
|
+
type CrossFilter = ReturnType<typeof createCrossFilter>
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Dual range slider for a continuous crossfilter dimension.
|
|
6
9
|
* NOTE: Interim implementation using HTML range inputs.
|
|
7
10
|
* The spec calls for a Plot+Point+brush architecture, deferred until brush geom is implemented.
|
|
8
11
|
*/
|
|
9
|
-
|
|
12
|
+
type Props = {
|
|
13
|
+
field: string
|
|
14
|
+
min?: number
|
|
15
|
+
max?: number
|
|
16
|
+
step?: number
|
|
17
|
+
label?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let { field, min, max, step = 0.1, label = '' }: Props = $props()
|
|
10
21
|
|
|
11
|
-
const cf = getContext('crossfilter')
|
|
22
|
+
const cf = getContext<CrossFilter | undefined>('crossfilter')
|
|
12
23
|
|
|
13
24
|
// Initialize from props; $effect keeps in sync when min/max change
|
|
14
25
|
let low = $state(0)
|
|
@@ -19,12 +30,12 @@
|
|
|
19
30
|
high = max ?? 100
|
|
20
31
|
})
|
|
21
32
|
|
|
22
|
-
function handleLow(e) {
|
|
33
|
+
function handleLow(e: Event & { currentTarget: HTMLInputElement }) {
|
|
23
34
|
low = Math.min(Number(e.currentTarget.value), high)
|
|
24
35
|
cf?.setRange(field, [low, high])
|
|
25
36
|
}
|
|
26
37
|
|
|
27
|
-
function handleHigh(e) {
|
|
38
|
+
function handleHigh(e: Event & { currentTarget: HTMLInputElement }) {
|
|
28
39
|
high = Math.max(Number(e.currentTarget.value), low)
|
|
29
40
|
cf?.setRange(field, [low, high])
|
|
30
41
|
}
|
package/src/elements/Bar.svelte
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Readable } from 'svelte/store'
|
|
2
3
|
import { format } from 'd3-format'
|
|
3
4
|
import { get } from 'svelte/store'
|
|
4
5
|
import Label from './Label.svelte'
|
|
5
6
|
|
|
7
|
+
type Scales = { x: (value: number) => number }
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
rank: number
|
|
11
|
+
value: number
|
|
12
|
+
name: string
|
|
13
|
+
formatString?: string
|
|
14
|
+
scales: Readable<Scales>
|
|
15
|
+
height?: number
|
|
16
|
+
fill?: string
|
|
17
|
+
spaceBetween?: number
|
|
18
|
+
}
|
|
19
|
+
|
|
6
20
|
let {
|
|
7
21
|
rank,
|
|
8
22
|
value,
|
|
@@ -12,7 +26,7 @@
|
|
|
12
26
|
height = 60,
|
|
13
27
|
fill,
|
|
14
28
|
spaceBetween = 5
|
|
15
|
-
} = $props()
|
|
29
|
+
}: Props = $props()
|
|
16
30
|
|
|
17
31
|
const textHeight = 16
|
|
18
32
|
const charWidth = 12
|
|
@@ -1,15 +1,39 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { scaleLinear } from 'd3-scale'
|
|
3
3
|
import { id as uniqueId } from '@rokkit/core'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
type ColorScale = {
|
|
6
|
+
(value: number): string
|
|
7
|
+
domain(): number[]
|
|
8
|
+
ticks(count?: number): number[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
x?: number
|
|
13
|
+
y?: number
|
|
14
|
+
textSize?: number
|
|
15
|
+
height?: number
|
|
16
|
+
width?: number
|
|
17
|
+
tickCount?: number
|
|
18
|
+
scale: ColorScale
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let {
|
|
22
|
+
x = 0,
|
|
23
|
+
y = 0,
|
|
24
|
+
textSize = 5,
|
|
25
|
+
height = 10,
|
|
26
|
+
width = 100,
|
|
27
|
+
tickCount = 5,
|
|
28
|
+
scale
|
|
29
|
+
}: Props = $props()
|
|
6
30
|
|
|
7
31
|
let scaleTicks = $derived(
|
|
8
|
-
scaleLinear()
|
|
32
|
+
scaleLinear<number>()
|
|
9
33
|
.range([x, x + width])
|
|
10
34
|
.domain(scale.domain())
|
|
11
35
|
)
|
|
12
|
-
let scalePercent = $derived(scaleLinear().range([0, 100]).domain(scale.domain()))
|
|
36
|
+
let scalePercent = $derived(scaleLinear<number>().range([0, 100]).domain(scale.domain()))
|
|
13
37
|
let ticks = $derived(
|
|
14
38
|
scale.ticks.apply(scale, [tickCount]).map((d) => ({ x: scaleTicks(d), value: d }))
|
|
15
39
|
)
|
|
@@ -1,6 +1,23 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { scaleLinear } from 'd3-scale'
|
|
3
3
|
|
|
4
|
+
type ColorScale = {
|
|
5
|
+
(value: number): string
|
|
6
|
+
domain(): number[]
|
|
7
|
+
ticks?(count?: number): number[]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
x?: number
|
|
12
|
+
y?: number
|
|
13
|
+
textSize?: number
|
|
14
|
+
height?: number
|
|
15
|
+
width?: number
|
|
16
|
+
tickCount?: number
|
|
17
|
+
scale: ColorScale
|
|
18
|
+
id?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
4
21
|
let {
|
|
5
22
|
x = 0,
|
|
6
23
|
y = 0,
|
|
@@ -10,10 +27,10 @@
|
|
|
10
27
|
tickCount = 5,
|
|
11
28
|
scale,
|
|
12
29
|
id = 'legend'
|
|
13
|
-
} = $props()
|
|
30
|
+
}: Props = $props()
|
|
14
31
|
|
|
15
32
|
let scaleTicks = $derived(
|
|
16
|
-
scaleLinear()
|
|
33
|
+
scaleLinear<number>()
|
|
17
34
|
.range([x, x + width])
|
|
18
35
|
.domain(scale.domain())
|
|
19
36
|
)
|
|
@@ -1,12 +1,28 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Component } from 'svelte'
|
|
3
|
+
import type { SVGAttributes } from 'svelte/elements'
|
|
2
4
|
import { uniq } from 'ramda'
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
size
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
type PatternComponentProps = {
|
|
7
|
+
size?: number
|
|
8
|
+
fill?: string
|
|
9
|
+
stroke?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type Pattern = {
|
|
13
|
+
id: string
|
|
14
|
+
component: Component<PatternComponentProps>
|
|
15
|
+
fill?: string
|
|
16
|
+
stroke?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type Props = {
|
|
20
|
+
size?: number
|
|
21
|
+
patternUnits?: SVGAttributes<SVGPatternElement>['patternUnits']
|
|
22
|
+
patterns?: Pattern[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let { size = 10, patternUnits = 'userSpaceOnUse', patterns = [] }: Props = $props()
|
|
10
26
|
|
|
11
27
|
let names = $derived(uniq(patterns.map(({ id }) => id)))
|
|
12
28
|
</script>
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
type ColorScale = {
|
|
3
|
+
(value: number): string
|
|
4
|
+
ticks(count?: number): number[]
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
x?: number
|
|
9
|
+
y?: number
|
|
10
|
+
textSize?: number
|
|
11
|
+
size?: number
|
|
12
|
+
space?: number
|
|
13
|
+
padding?: number
|
|
14
|
+
scale: ColorScale
|
|
15
|
+
tickCount?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
2
18
|
let {
|
|
3
19
|
x = 0,
|
|
4
20
|
y = 0,
|
|
@@ -8,7 +24,7 @@
|
|
|
8
24
|
padding = 5,
|
|
9
25
|
scale,
|
|
10
26
|
tickCount = 10
|
|
11
|
-
} = $props()
|
|
27
|
+
}: Props = $props()
|
|
12
28
|
|
|
13
29
|
let sizeWithSpace = $derived(size + space)
|
|
14
30
|
let ticks = $derived(scale.ticks.apply(scale, [tickCount]))
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
type Props = {
|
|
3
|
+
x: number
|
|
4
|
+
y: number
|
|
5
|
+
text: string
|
|
6
|
+
angle?: number
|
|
7
|
+
small?: boolean
|
|
8
|
+
anchor?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { x, y, text, angle = 0, small = false, anchor = 'middle' }: Props = $props()
|
|
3
12
|
|
|
4
13
|
let transform = $derived(`translate(${x},${y}) rotate(${angle})`)
|
|
5
14
|
let validAnchor = $derived(['start', 'middle', 'end'].includes(anchor) ? anchor : 'middle')
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { swatch } from '../lib/swatch'
|
|
3
3
|
import { swatchGrid } from '../lib/grid'
|
|
4
4
|
import Symbol from '../Symbol.svelte'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type Props = {
|
|
7
|
+
base?: string
|
|
8
|
+
size?: number
|
|
9
|
+
shade?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let { base = 'teal', size = 4, shade = 600 }: Props = $props()
|
|
7
13
|
|
|
8
14
|
let grid = $derived(swatchGrid(swatch.keys.symbol.length, size, 10))
|
|
15
|
+
|
|
16
|
+
const palette: Record<string, Record<string, string>> = swatch.palette
|
|
17
|
+
let color = $derived(palette[base]?.[shade])
|
|
9
18
|
</script>
|
|
10
19
|
|
|
11
20
|
<svg viewBox="0 0 {grid.width} {grid.height}">
|
|
@@ -15,8 +24,8 @@
|
|
|
15
24
|
{y}
|
|
16
25
|
size={r * 2}
|
|
17
26
|
name={swatch.keys.symbol[index]}
|
|
18
|
-
fill={
|
|
19
|
-
stroke={
|
|
27
|
+
fill={color}
|
|
28
|
+
stroke={color}
|
|
20
29
|
/>
|
|
21
30
|
{/each}
|
|
22
31
|
</svg>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { Plot } from '../index.js'
|
|
3
3
|
import { dataset } from '@rokkit/data'
|
|
4
4
|
|
|
5
|
+
type Row = Record<string, unknown>
|
|
6
|
+
|
|
5
7
|
// Sample data
|
|
6
8
|
const sampleData = [
|
|
7
9
|
{ model: 'Model A', name: 'Product 1', category: 'Electronics', count: 45 },
|
|
@@ -14,33 +16,28 @@
|
|
|
14
16
|
]
|
|
15
17
|
|
|
16
18
|
// Use the dataset class to process the data
|
|
17
|
-
const data = dataset(sampleData)
|
|
19
|
+
const data: Row[] = dataset(sampleData)
|
|
18
20
|
.groupBy('category')
|
|
19
|
-
.summarize('name', { count: (values) => values.length })
|
|
21
|
+
.summarize('name', { count: (values: unknown[]) => values.length })
|
|
20
22
|
.rollup()
|
|
23
|
+
.select()
|
|
21
24
|
|
|
22
25
|
// Chart dimensions
|
|
23
26
|
const width = 600
|
|
24
27
|
const height = 400
|
|
25
28
|
const margin = { top: 20, right: 100, bottom: 60, left: 60 }
|
|
26
|
-
|
|
27
|
-
// Click handler for bars
|
|
28
|
-
function handleBarClick(item) {
|
|
29
|
-
console.log('Bar clicked:', item)
|
|
30
|
-
alert(`Clicked on ${item.category} with count ${item.count}`)
|
|
31
|
-
}
|
|
32
29
|
</script>
|
|
33
30
|
|
|
34
31
|
<div class="example">
|
|
35
32
|
<h2>Bar Chart Example</h2>
|
|
36
33
|
|
|
37
34
|
<div class="chart-wrapper">
|
|
38
|
-
<Plot.Root {data} {width} {height} {margin}
|
|
39
|
-
<Plot.Grid
|
|
40
|
-
<Plot.Axis type="x"
|
|
41
|
-
<Plot.Axis type="y"
|
|
42
|
-
<Plot.Bar x="category" y="count" fill="category"
|
|
43
|
-
<Plot.Legend
|
|
35
|
+
<Plot.Root {data} x="category" y="count" color="category" {width} {height} {margin}>
|
|
36
|
+
<Plot.Grid />
|
|
37
|
+
<Plot.Axis type="x" label="Product Category" />
|
|
38
|
+
<Plot.Axis type="y" label="Number of Products" />
|
|
39
|
+
<Plot.Bar {data} x="category" y="count" fill="category" />
|
|
40
|
+
<Plot.Legend labels={{ category: 'Categories' }} />
|
|
44
41
|
</Plot.Root>
|
|
45
42
|
</div>
|
|
46
43
|
|
package/src/geoms/Arc.svelte
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { getContext, onMount, onDestroy } from 'svelte'
|
|
3
|
+
import type { PlotState } from '../PlotState.svelte.js'
|
|
3
4
|
import { buildArcs } from '../lib/brewing/marks/arcs.js'
|
|
4
5
|
|
|
6
|
+
type Row = Record<string, unknown>
|
|
7
|
+
|
|
5
8
|
/**
|
|
6
9
|
* `fill` is the primary prop name; `color` is accepted as an alias for
|
|
7
10
|
* spec-driven usage (Plot.svelte passes `color` to all geoms generically).
|
|
8
|
-
* @type {{ theta?: string, fill?: string, color?: string, pattern?: string, stat?: string, labelFn?: (data: Record<string, unknown>) => string, options?: { innerRadius?: number } }}
|
|
9
11
|
*/
|
|
12
|
+
type Props = {
|
|
13
|
+
theta?: string
|
|
14
|
+
fill?: string
|
|
15
|
+
color?: string
|
|
16
|
+
pattern?: string
|
|
17
|
+
stat?: string
|
|
18
|
+
labelFn?: (data: Row) => string
|
|
19
|
+
options?: { innerRadius?: number }
|
|
20
|
+
onselect?: (data: Row) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
10
23
|
let {
|
|
11
24
|
theta,
|
|
12
25
|
fill,
|
|
@@ -16,12 +29,12 @@
|
|
|
16
29
|
stat = 'identity',
|
|
17
30
|
options = {},
|
|
18
31
|
onselect = undefined
|
|
19
|
-
} = $props()
|
|
32
|
+
}: Props = $props()
|
|
20
33
|
|
|
21
34
|
const fillField = $derived(fill ?? color)
|
|
22
35
|
|
|
23
|
-
const plotState = getContext('plot-state')
|
|
24
|
-
let id = $state(null)
|
|
36
|
+
const plotState = getContext<PlotState>('plot-state')
|
|
37
|
+
let id = $state<string | null>(null)
|
|
25
38
|
|
|
26
39
|
onMount(() => {
|
|
27
40
|
id = plotState.registerGeom({
|
package/src/geoms/Area.svelte
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { getContext, onMount, onDestroy } from 'svelte'
|
|
3
|
+
import type { PlotState } from '../PlotState.svelte.js'
|
|
3
4
|
import { buildAreas, buildStackedAreas } from './lib/areas.js'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
type Options = {
|
|
7
|
+
stack?: boolean
|
|
8
|
+
curve?: 'linear' | 'smooth' | 'step'
|
|
9
|
+
opacity?: number
|
|
10
|
+
}
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
type Props = {
|
|
13
|
+
x?: string
|
|
14
|
+
y?: string
|
|
15
|
+
color?: string
|
|
16
|
+
pattern?: string
|
|
17
|
+
stat?: string
|
|
18
|
+
options?: Options
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { x, y, color, pattern, stat = 'identity', options = {} }: Props = $props()
|
|
22
|
+
|
|
23
|
+
const plotState = getContext<PlotState>('plot-state')
|
|
24
|
+
let id = $state<string | null>(null)
|
|
9
25
|
|
|
10
26
|
onMount(() => {
|
|
11
27
|
id = plotState.registerGeom({
|
|
@@ -35,7 +51,7 @@
|
|
|
35
51
|
const patterns = $derived(plotState.patterns)
|
|
36
52
|
|
|
37
53
|
const areas = $derived.by(() => {
|
|
38
|
-
if (!data?.length || !xScale || !yScale) return []
|
|
54
|
+
if (!data?.length || !xScale || !yScale || !x || !y) return []
|
|
39
55
|
const channels = { x, y, color, pattern }
|
|
40
56
|
if (options.stack) {
|
|
41
57
|
return buildStackedAreas(data, channels, xScale, yScale, colors, options.curve, patterns)
|
|
@@ -59,20 +75,22 @@
|
|
|
59
75
|
{/if}
|
|
60
76
|
{/each}
|
|
61
77
|
<!-- Invisible hit circles for tooltip: one per data point -->
|
|
62
|
-
{#
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
{#if x && y}
|
|
79
|
+
{#each data as d, i (`hover::${i}`)}
|
|
80
|
+
{@const px = typeof xScale?.bandwidth === 'function' ? (xScale(d[x]) ?? 0) + xScale.bandwidth() / 2 : (xScale?.(d[x]) ?? 0)}
|
|
81
|
+
{@const py = yScale?.(d[y]) ?? 0}
|
|
82
|
+
<circle
|
|
83
|
+
cx={px}
|
|
84
|
+
cy={py}
|
|
85
|
+
r="8"
|
|
86
|
+
fill="transparent"
|
|
87
|
+
stroke="none"
|
|
88
|
+
role="presentation"
|
|
89
|
+
data-plot-element="area-hover"
|
|
90
|
+
onmouseenter={() => plotState.setHovered(d)}
|
|
91
|
+
onmouseleave={() => plotState.clearHovered()}
|
|
92
|
+
/>
|
|
93
|
+
{/each}
|
|
94
|
+
{/if}
|
|
77
95
|
</g>
|
|
78
96
|
{/if}
|