@rokkit/chart 1.3.15 → 1.4.1
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.svelte +25 -9
- package/dist/PlotState.svelte.d.ts +1 -0
- package/dist/PlotState.svelte.js +19 -8
- package/dist/Spark.svelte +165 -0
- package/dist/Spark.svelte.d.ts +31 -0
- package/dist/SparkState.svelte.d.ts +138 -0
- package/dist/SparkState.svelte.js +331 -0
- package/dist/Sparkline.svelte +62 -237
- package/dist/Sparkline.svelte.d.ts +2 -0
- package/dist/charts/RadarChart.svelte +106 -0
- package/dist/charts/RadarChart.svelte.d.ts +43 -0
- package/dist/geoms/Area.svelte +24 -9
- package/dist/geoms/Area.svelte.d.ts +2 -0
- package/dist/geoms/Bar.svelte +9 -16
- package/dist/geoms/Highlight.svelte +61 -40
- package/dist/geoms/Line.svelte +10 -9
- package/dist/geoms/Point.svelte +9 -15
- package/dist/geoms/Radar.svelte +352 -0
- package/dist/geoms/Radar.svelte.d.ts +42 -0
- package/dist/geoms/Trend.svelte +46 -23
- package/dist/geoms/lib/aesthetics.d.ts +14 -0
- package/dist/geoms/lib/aesthetics.js +21 -0
- package/dist/geoms/lib/areas.d.ts +9 -2
- package/dist/geoms/lib/areas.js +53 -8
- package/dist/geoms/lib/marks/area.d.ts +3 -0
- package/dist/geoms/lib/marks/area.js +11 -2
- package/dist/geoms/lib/marks/radar.d.ts +68 -0
- package/dist/geoms/lib/marks/radar.js +153 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -1
- package/dist/lib/brewing/marks/boxes.js +19 -30
- package/dist/lib/brewing/polar.d.ts +444 -0
- package/dist/lib/brewing/polar.js +645 -0
- package/dist/lib/highlight.js +84 -33
- package/dist/lib/plot/scales.js +8 -5
- package/dist/lib/preset.d.ts +1 -0
- package/dist/lib/preset.js +2 -1
- package/dist/lib/select.d.ts +25 -11
- package/dist/lib/select.js +14 -7
- package/dist/lib/trend.js +55 -39
- package/package.json +1 -1
- package/dist/examples/BarChartExample.svelte +0 -78
- package/dist/examples/BarChartExample.svelte.d.ts +0 -18
- package/dist/lib/brewing/types.d.ts +0 -162
- package/dist/lib/brewing/types.js +0 -73
- package/dist/lib/xscale.d.ts +0 -11
- package/dist/lib/xscale.js +0 -31
package/dist/lib/highlight.js
CHANGED
|
@@ -1,43 +1,94 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Highlight-selector resolution.
|
|
3
|
+
*
|
|
4
|
+
* One small resolver per selector kind, plus a dispatcher. The kinds share
|
|
5
|
+
* nothing but their signature, so keeping them separate means each can be read
|
|
6
|
+
* and tested on its own instead of as one branch of a 24-complexity chain.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Indices where a predicate selector matches.
|
|
3
11
|
* @param {Array<Record<string, unknown>>} rows
|
|
4
|
-
* @param {
|
|
5
|
-
* @param {{ y?: string }} [opts]
|
|
12
|
+
* @param {(row: any, i: number) => boolean} predicate
|
|
6
13
|
* @returns {number[]}
|
|
7
14
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
function byPredicate(rows, predicate) {
|
|
16
|
+
const out = []
|
|
17
|
+
for (let i = 0; i < rows.length; i++) if (predicate(rows[i], i)) out.push(i)
|
|
18
|
+
return out
|
|
19
|
+
}
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
/**
|
|
22
|
+
* A single index. Negative values count back from the end (`-1` is the last
|
|
23
|
+
* row); anything out of range resolves to nothing.
|
|
24
|
+
* @param {Array<Record<string, unknown>>} rows
|
|
25
|
+
* @param {number} index
|
|
26
|
+
* @returns {number[]}
|
|
27
|
+
*/
|
|
28
|
+
function byIndex(rows, index) {
|
|
29
|
+
const i = index < 0 ? rows.length + index : index
|
|
30
|
+
return i >= 0 && i < rows.length ? [i] : []
|
|
31
|
+
}
|
|
18
32
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
/**
|
|
34
|
+
* The index of the row whose numeric `y` is smallest (`min`) or largest (`max`).
|
|
35
|
+
*
|
|
36
|
+
* Non-numeric values are dropped rather than compared — `NaN` loses every
|
|
37
|
+
* comparison, so leaving them in would make the result depend on iteration
|
|
38
|
+
* order. Ties keep the earliest row. Candidates are collected before reducing
|
|
39
|
+
* so an all-`Infinity` column still resolves to its first row, which a
|
|
40
|
+
* strict-improvement scan seeded with `Infinity` would reject.
|
|
41
|
+
*
|
|
42
|
+
* @param {Array<Record<string, unknown>>} rows
|
|
43
|
+
* @param {string | undefined} y
|
|
44
|
+
* @param {'min'|'max'} kind
|
|
45
|
+
* @returns {number[]}
|
|
46
|
+
*/
|
|
47
|
+
function byExtreme(rows, y, kind) {
|
|
48
|
+
if (!y) return []
|
|
49
|
+
// Searching for the smallest `sign * value` finds the max when sign is -1,
|
|
50
|
+
// which collapses two mirror-image scans into one.
|
|
51
|
+
const sign = kind === 'min' ? 1 : -1
|
|
52
|
+
const candidates = rows
|
|
53
|
+
.map((row, i) => ({ i, value: sign * Number(row[y]) }))
|
|
54
|
+
.filter(({ value }) => !Number.isNaN(value))
|
|
55
|
+
if (candidates.length === 0) return []
|
|
56
|
+
return [candidates.reduce((best, c) => (c.value < best.value ? c : best)).i]
|
|
57
|
+
}
|
|
23
58
|
|
|
24
|
-
|
|
25
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Named selectors, all sharing one signature so the dispatcher can call any of
|
|
61
|
+
* them uniformly. `rows` is guaranteed non-empty by the time these run.
|
|
62
|
+
* @type {Record<string, (rows: Array<Record<string, unknown>>, y?: string) => number[]>}
|
|
63
|
+
*/
|
|
64
|
+
const NAMED = {
|
|
65
|
+
first: () => [0],
|
|
66
|
+
last: (rows) => [rows.length - 1],
|
|
67
|
+
min: (rows, y) => byExtreme(rows, y, 'min'),
|
|
68
|
+
max: (rows, y) => byExtreme(rows, y, 'max')
|
|
69
|
+
}
|
|
26
70
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (bestVal === null || (selector === 'min' ? v < bestVal : v > bestVal)) {
|
|
35
|
-
bestVal = v
|
|
36
|
-
best = i
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return best === -1 ? [] : [best]
|
|
40
|
-
}
|
|
71
|
+
/**
|
|
72
|
+
* True when the inputs cannot resolve to any row, whatever the selector means.
|
|
73
|
+
* @param {unknown} rows
|
|
74
|
+
* @param {unknown} selector
|
|
75
|
+
*/
|
|
76
|
+
const isUnresolvable = (rows, selector) =>
|
|
77
|
+
!Array.isArray(rows) || rows.length === 0 || selector === null || selector === undefined
|
|
41
78
|
|
|
42
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Resolve a highlight selector to zero or more row indices.
|
|
81
|
+
* @param {Array<Record<string, unknown>>} rows
|
|
82
|
+
* @param {'first'|'last'|'min'|'max'|number|((row:any,i:number)=>boolean)} selector
|
|
83
|
+
* @param {{ y?: string }} [opts]
|
|
84
|
+
* @returns {number[]}
|
|
85
|
+
*/
|
|
86
|
+
export function resolveHighlight(rows, selector, opts = {}) {
|
|
87
|
+
if (isUnresolvable(rows, selector)) return []
|
|
88
|
+
if (typeof selector === 'function') return byPredicate(rows, selector)
|
|
89
|
+
if (typeof selector === 'number') return byIndex(rows, selector)
|
|
90
|
+
// hasOwn, not a bare lookup: `NAMED['constructor']` would otherwise resolve to
|
|
91
|
+
// Object's constructor and get called. Same guard as PATTERNS in SparkState.
|
|
92
|
+
const named = Object.hasOwn(NAMED, selector) ? NAMED[selector] : null
|
|
93
|
+
return named ? named(rows, opts.y) : []
|
|
43
94
|
}
|
package/dist/lib/plot/scales.js
CHANGED
|
@@ -42,10 +42,10 @@ export function buildUnifiedXScale(datasets, field, width, opts = {}) {
|
|
|
42
42
|
const numericValues = allValues.map(Number)
|
|
43
43
|
const [minVal, maxVal] = extent(numericValues)
|
|
44
44
|
const domainMin = (opts.includeZero ?? false) ? 0 : (minVal ?? 0)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
const s = scaleLinear().domain([domainMin, maxVal ?? 0]).range(range)
|
|
46
|
+
// Same opts.nice escape hatch as the opts.domain branch above (e.g. a spark's x-axis,
|
|
47
|
+
// which must span the full width edge-to-edge, not stop short at a rounded tick).
|
|
48
|
+
return opts.nice === false ? s : s.nice()
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const domain = [...new Set(allValues)].filter((v) => v !== null && v !== undefined)
|
|
@@ -58,7 +58,10 @@ export function buildUnifiedXScale(datasets, field, width, opts = {}) {
|
|
|
58
58
|
export function buildUnifiedYScale(datasets, field, height, opts = {}) {
|
|
59
59
|
const yrange = opts.range ?? [height, 0]
|
|
60
60
|
if (opts.domain) {
|
|
61
|
-
|
|
61
|
+
const s = scaleLinear().domain(opts.domain).range(yrange)
|
|
62
|
+
// nice() pads the domain outward to round numbers — wrong for a caller (e.g. a spark)
|
|
63
|
+
// whose domain is already the exact value it wants rendered edge-to-edge.
|
|
64
|
+
return opts.nice === false ? s : s.nice()
|
|
62
65
|
}
|
|
63
66
|
const rawValues = datasets.flatMap((d) => d.map((r) => r[field])).filter((v) => v !== null && v !== undefined)
|
|
64
67
|
const isNumeric = rawValues.length > 0 && rawValues.every(
|
package/dist/lib/preset.d.ts
CHANGED
package/dist/lib/preset.js
CHANGED
|
@@ -23,7 +23,8 @@ export const defaultPreset = {
|
|
|
23
23
|
hexbin: 1,
|
|
24
24
|
ribbon: 0.5,
|
|
25
25
|
waterfall: 1,
|
|
26
|
-
rule: 1
|
|
26
|
+
rule: 1,
|
|
27
|
+
radar: 0.25
|
|
27
28
|
},
|
|
28
29
|
patterns: ['diagonal', 'dots', 'triangles', 'hatch', 'lattice', 'swell',
|
|
29
30
|
'checkerboard', 'waves', 'petals', 'brick', 'diamonds', 'tile',
|
package/dist/lib/select.d.ts
CHANGED
|
@@ -1,22 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Assemble the SelectDetail handed to a chart's onselect callback.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Takes one named bag rather than six positionals: the tail of the old
|
|
5
|
+
* signature read `…, { x, y }, 'line', seg?.key, event)` at every call site,
|
|
6
|
+
* where nothing but the argument order told you which of the trailing values
|
|
7
|
+
* was the geom and which was the series.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} args
|
|
10
|
+
* @param {Record<string, unknown>} args.datum
|
|
11
|
+
* @param {number} args.index index within the geom's rendered data
|
|
12
|
+
* @param {{ x?: string, y?: string }} args.channels
|
|
13
|
+
* @param {'line'|'point'|'area'|'bar'|'radar'} args.geom
|
|
14
|
+
* @param {unknown} [args.series] color/group value, or undefined
|
|
15
|
+
* @param {MouseEvent|KeyboardEvent} args.event
|
|
9
16
|
*/
|
|
10
|
-
export function buildSelectDetail(datum
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
export function buildSelectDetail({ datum, index, channels, geom, series, event }: {
|
|
18
|
+
datum: Record<string, unknown>;
|
|
19
|
+
index: number;
|
|
20
|
+
channels: {
|
|
21
|
+
x?: string;
|
|
22
|
+
y?: string;
|
|
23
|
+
};
|
|
24
|
+
geom: "line" | "point" | "area" | "bar" | "radar";
|
|
25
|
+
series?: unknown;
|
|
26
|
+
event: MouseEvent | KeyboardEvent;
|
|
27
|
+
}): {
|
|
14
28
|
datum: Record<string, unknown>;
|
|
15
29
|
index: number;
|
|
16
30
|
series: {} | undefined;
|
|
17
31
|
value: unknown;
|
|
18
32
|
x: unknown;
|
|
19
33
|
y: unknown;
|
|
20
|
-
geom: "bar" | "line" | "area" | "point";
|
|
34
|
+
geom: "bar" | "line" | "area" | "point" | "radar";
|
|
21
35
|
event: MouseEvent | KeyboardEvent;
|
|
22
36
|
};
|
package/dist/lib/select.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Assemble the SelectDetail handed to a chart's onselect callback.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Takes one named bag rather than six positionals: the tail of the old
|
|
5
|
+
* signature read `…, { x, y }, 'line', seg?.key, event)` at every call site,
|
|
6
|
+
* where nothing but the argument order told you which of the trailing values
|
|
7
|
+
* was the geom and which was the series.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} args
|
|
10
|
+
* @param {Record<string, unknown>} args.datum
|
|
11
|
+
* @param {number} args.index index within the geom's rendered data
|
|
12
|
+
* @param {{ x?: string, y?: string }} args.channels
|
|
13
|
+
* @param {'line'|'point'|'area'|'bar'|'radar'} args.geom
|
|
14
|
+
* @param {unknown} [args.series] color/group value, or undefined
|
|
15
|
+
* @param {MouseEvent|KeyboardEvent} args.event
|
|
9
16
|
*/
|
|
10
|
-
export function buildSelectDetail(datum, index, channels, geom, series, event) {
|
|
17
|
+
export function buildSelectDetail({ datum, index, channels, geom, series, event }) {
|
|
11
18
|
const { x, y } = channels
|
|
12
19
|
return {
|
|
13
20
|
datum,
|
package/dist/lib/trend.js
CHANGED
|
@@ -58,14 +58,63 @@ export function expRegression(xs, ys) {
|
|
|
58
58
|
return { a: Math.exp(reg.b), b: reg.m }
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/** `mean` is an accepted alias for `avg`. */
|
|
62
|
+
const canonicalType = (type) => (type === 'mean' ? 'avg' : type)
|
|
63
|
+
|
|
64
|
+
/** Accept a number, a bare type name, or a full config object; produce a config or null. */
|
|
61
65
|
function normalize(method) {
|
|
62
66
|
if (typeof method === 'number') return { type: 'value', value: method }
|
|
63
|
-
if (typeof method === 'string') return { type: method
|
|
67
|
+
if (typeof method === 'string') return { type: canonicalType(method) }
|
|
64
68
|
if (method && typeof method === 'object' && method.type)
|
|
65
|
-
return { ...method, type: method.type
|
|
69
|
+
return { ...method, type: canonicalType(method.type) }
|
|
66
70
|
return null
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Numeric series for the two channels. A non-finite x falls back to the row
|
|
75
|
+
* index, which is what makes trends work on a categorical/band axis.
|
|
76
|
+
* @param {Array<Record<string, unknown>>} rows
|
|
77
|
+
* @param {{x?: string, y?: string}} channels
|
|
78
|
+
*/
|
|
79
|
+
function toSeries(rows, { x, y }) {
|
|
80
|
+
return {
|
|
81
|
+
ys: rows.map((r) => Number(r[y])),
|
|
82
|
+
xs: rows.map((r, i) => {
|
|
83
|
+
const v = Number(r[x])
|
|
84
|
+
return Number.isFinite(v) ? v : i
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* One builder per trend type, replacing a nine-case switch. Each takes the
|
|
91
|
+
* prepared series plus the normalized config and returns a trend or null, so a
|
|
92
|
+
* new trend type is a new entry rather than another branch.
|
|
93
|
+
* @type {Record<string, (ctx: {xs: number[], ys: number[], cfg: Record<string, any>}) => object | null>}
|
|
94
|
+
*/
|
|
95
|
+
const BUILDERS = {
|
|
96
|
+
avg: ({ ys }) => ({ kind: 'constant', value: mean(ys) }),
|
|
97
|
+
median: ({ ys }) => ({ kind: 'constant', value: median(ys) }),
|
|
98
|
+
min: ({ ys }) => ({ kind: 'constant', value: Math.min(...ys) }),
|
|
99
|
+
max: ({ ys }) => ({ kind: 'constant', value: Math.max(...ys) }),
|
|
100
|
+
value: ({ cfg }) =>
|
|
101
|
+
typeof cfg.value === 'number' ? { kind: 'constant', value: cfg.value } : null,
|
|
102
|
+
linear: ({ xs, ys }) => {
|
|
103
|
+
const reg = linearRegression(xs, ys)
|
|
104
|
+
return reg ? { kind: 'series', values: xs.map((v) => reg.m * v + reg.b) } : null
|
|
105
|
+
},
|
|
106
|
+
ma: ({ ys, cfg }) =>
|
|
107
|
+
cfg.window ? { kind: 'series', values: movingAverage(ys, cfg.window) } : null,
|
|
108
|
+
ema: ({ ys, cfg }) => ({
|
|
109
|
+
kind: 'series',
|
|
110
|
+
values: ema(ys, { span: cfg.span, alpha: cfg.alpha })
|
|
111
|
+
}),
|
|
112
|
+
exp: ({ xs, ys }) => {
|
|
113
|
+
const reg = expRegression(xs, ys)
|
|
114
|
+
return reg ? { kind: 'series', values: xs.map((v) => reg.a * Math.exp(reg.b * v)) } : null
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
69
118
|
/**
|
|
70
119
|
* Compute a trend for `rows` on channels {x, y}.
|
|
71
120
|
* @returns {{kind:'constant', value:number} | {kind:'series', values:number[]} | null}
|
|
@@ -73,41 +122,8 @@ function normalize(method) {
|
|
|
73
122
|
export function computeTrend(rows, channels, method) {
|
|
74
123
|
const cfg = normalize(method)
|
|
75
124
|
if (!cfg || !Array.isArray(rows) || rows.length === 0) return null
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return Number.isFinite(v) ? v : i
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
switch (cfg.type) {
|
|
84
|
-
case 'avg':
|
|
85
|
-
return { kind: 'constant', value: mean(ys) }
|
|
86
|
-
case 'median':
|
|
87
|
-
return { kind: 'constant', value: median(ys) }
|
|
88
|
-
case 'min':
|
|
89
|
-
return { kind: 'constant', value: Math.min(...ys) }
|
|
90
|
-
case 'max':
|
|
91
|
-
return { kind: 'constant', value: Math.max(...ys) }
|
|
92
|
-
case 'value':
|
|
93
|
-
return typeof cfg.value === 'number' ? { kind: 'constant', value: cfg.value } : null
|
|
94
|
-
case 'linear': {
|
|
95
|
-
const reg = linearRegression(xs, ys)
|
|
96
|
-
if (!reg) return null
|
|
97
|
-
return { kind: 'series', values: xs.map((v) => reg.m * v + reg.b) }
|
|
98
|
-
}
|
|
99
|
-
case 'ma': {
|
|
100
|
-
if (!cfg.window) return null
|
|
101
|
-
return { kind: 'series', values: movingAverage(ys, cfg.window) }
|
|
102
|
-
}
|
|
103
|
-
case 'ema':
|
|
104
|
-
return { kind: 'series', values: ema(ys, { span: cfg.span, alpha: cfg.alpha }) }
|
|
105
|
-
case 'exp': {
|
|
106
|
-
const reg = expRegression(xs, ys)
|
|
107
|
-
if (!reg) return null
|
|
108
|
-
return { kind: 'series', values: xs.map((v) => reg.a * Math.exp(reg.b * v)) }
|
|
109
|
-
}
|
|
110
|
-
default:
|
|
111
|
-
return null
|
|
112
|
-
}
|
|
125
|
+
// hasOwn, not a bare lookup: an unknown type must fall through to null, and a
|
|
126
|
+
// bare `BUILDERS[cfg.type]` would resolve inherited keys like 'constructor'.
|
|
127
|
+
if (!Object.hasOwn(BUILDERS, cfg.type)) return null
|
|
128
|
+
return BUILDERS[cfg.type]({ ...toSeries(rows, channels), cfg })
|
|
113
129
|
}
|
package/package.json
CHANGED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { Plot } from '../index.js'
|
|
3
|
-
import { dataset } from '@rokkit/data'
|
|
4
|
-
|
|
5
|
-
type Row = Record<string, unknown>
|
|
6
|
-
|
|
7
|
-
// Sample data
|
|
8
|
-
const sampleData = [
|
|
9
|
-
{ model: 'Model A', name: 'Product 1', category: 'Electronics', count: 45 },
|
|
10
|
-
{ model: 'Model B', name: 'Product 2', category: 'Clothing', count: 32 },
|
|
11
|
-
{ model: 'Model C', name: 'Product 3', category: 'Electronics', count: 62 },
|
|
12
|
-
{ model: 'Model D', name: 'Product 4', category: 'Home', count: 28 },
|
|
13
|
-
{ model: 'Model E', name: 'Product 5', category: 'Electronics', count: 53 },
|
|
14
|
-
{ model: 'Model F', name: 'Product 6', category: 'Clothing', count: 24 },
|
|
15
|
-
{ model: 'Model G', name: 'Product 7', category: 'Home', count: 35 }
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
// Use the dataset class to process the data
|
|
19
|
-
const data: Row[] = dataset(sampleData)
|
|
20
|
-
.groupBy('category')
|
|
21
|
-
.summarize('name', { count: (values: unknown[]) => values.length })
|
|
22
|
-
.rollup()
|
|
23
|
-
.select()
|
|
24
|
-
|
|
25
|
-
// Chart dimensions
|
|
26
|
-
const width = 600
|
|
27
|
-
const height = 400
|
|
28
|
-
const margin = { top: 20, right: 100, bottom: 60, left: 60 }
|
|
29
|
-
</script>
|
|
30
|
-
|
|
31
|
-
<div class="example">
|
|
32
|
-
<h2>Bar Chart Example</h2>
|
|
33
|
-
|
|
34
|
-
<div class="chart-wrapper">
|
|
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 x="category" y="count" fill="category" />
|
|
40
|
-
<Plot.Legend labels={{ category: 'Categories' }} />
|
|
41
|
-
</Plot.Root>
|
|
42
|
-
</div>
|
|
43
|
-
|
|
44
|
-
<div class="code-example">
|
|
45
|
-
<h3>Example Code:</h3>
|
|
46
|
-
<pre>
|
|
47
|
-
<!-- <script>
|
|
48
|
-
import { Plot } from '@rokkit/chart';
|
|
49
|
-
import { dataset } from '@rokkit/data';
|
|
50
|
-
|
|
51
|
-
// Sample data
|
|
52
|
-
const sampleData = [
|
|
53
|
-
{ model: 'Model A', name: 'Product 1', category: 'Electronics', count: 45 },
|
|
54
|
-
{ model: 'Model B', name: 'Product 2', category: 'Clothing', count: 32 },
|
|
55
|
-
{ model: 'Model C', name: 'Product 3', category: 'Electronics', count: 62 },
|
|
56
|
-
{ model: 'Model D', name: 'Product 4', category: 'Home', count: 28 },
|
|
57
|
-
{ model: 'Model E', name: 'Product 5', category: 'Electronics', count: 53 },
|
|
58
|
-
{ model: 'Model F', name: 'Product 6', category: 'Clothing', count: 24 },
|
|
59
|
-
{ model: 'Model G', name: 'Product 7', category: 'Home', count: 35 },
|
|
60
|
-
];
|
|
61
|
-
|
|
62
|
-
// Use the dataset class to process the data
|
|
63
|
-
const data = dataset(sampleData)
|
|
64
|
-
.groupBy('category')
|
|
65
|
-
.summarize('name', { count: values => values.length })
|
|
66
|
-
.rollup();
|
|
67
|
-
</script>
|
|
68
|
-
|
|
69
|
-
<Plot.Root {data} width={600} height={400} margin={{ top: 20, right: 100, bottom: 60, left: 60 }} fill="category">
|
|
70
|
-
<Plot.Grid direction="y" lineStyle="dashed" />
|
|
71
|
-
<Plot.Axis type="x" field="category" label="Product Category" />
|
|
72
|
-
<Plot.Axis type="y" field="count" label="Number of Products" />
|
|
73
|
-
<Plot.Bar x="category" y="count" fill="category" onClick={handleBarClick} />
|
|
74
|
-
<Plot.Legend title="Categories" />
|
|
75
|
-
</Plot.Root> -->
|
|
76
|
-
</pre>
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
-
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
-
$$bindings?: Bindings;
|
|
4
|
-
} & Exports;
|
|
5
|
-
(internal: unknown, props: {
|
|
6
|
-
$$events?: Events;
|
|
7
|
-
$$slots?: Slots;
|
|
8
|
-
}): Exports & {
|
|
9
|
-
$set?: any;
|
|
10
|
-
$on?: any;
|
|
11
|
-
};
|
|
12
|
-
z_$$bindings?: Bindings;
|
|
13
|
-
}
|
|
14
|
-
declare const BarChartExample: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
-
[evt: string]: CustomEvent<any>;
|
|
16
|
-
}, {}, {}, string>;
|
|
17
|
-
type BarChartExample = InstanceType<typeof BarChartExample>;
|
|
18
|
-
export default BarChartExample;
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
export type ChartMargin = {
|
|
2
|
-
/**
|
|
3
|
-
* - Top margin
|
|
4
|
-
*/
|
|
5
|
-
top: number;
|
|
6
|
-
/**
|
|
7
|
-
* - Right margin
|
|
8
|
-
*/
|
|
9
|
-
right: number;
|
|
10
|
-
/**
|
|
11
|
-
* - Bottom margin
|
|
12
|
-
*/
|
|
13
|
-
bottom: number;
|
|
14
|
-
/**
|
|
15
|
-
* - Left margin
|
|
16
|
-
*/
|
|
17
|
-
left: number;
|
|
18
|
-
};
|
|
19
|
-
export type ChartDimensions = {
|
|
20
|
-
/**
|
|
21
|
-
* - Total chart width
|
|
22
|
-
*/
|
|
23
|
-
width: number;
|
|
24
|
-
/**
|
|
25
|
-
* - Total chart height
|
|
26
|
-
*/
|
|
27
|
-
height: number;
|
|
28
|
-
/**
|
|
29
|
-
* - Chart margins
|
|
30
|
-
*/
|
|
31
|
-
margin: ChartMargin;
|
|
32
|
-
/**
|
|
33
|
-
* - Chart width without margins
|
|
34
|
-
*/
|
|
35
|
-
innerWidth: number;
|
|
36
|
-
/**
|
|
37
|
-
* - Chart height without margins
|
|
38
|
-
*/
|
|
39
|
-
innerHeight: number;
|
|
40
|
-
};
|
|
41
|
-
export type ChartScales = {
|
|
42
|
-
/**
|
|
43
|
-
* - X-axis scale function
|
|
44
|
-
*/
|
|
45
|
-
x: Function;
|
|
46
|
-
/**
|
|
47
|
-
* - Y-axis scale function
|
|
48
|
-
*/
|
|
49
|
-
y: Function;
|
|
50
|
-
/**
|
|
51
|
-
* - Color scale function
|
|
52
|
-
*/
|
|
53
|
-
color: Function;
|
|
54
|
-
};
|
|
55
|
-
export type ScaleFields = {
|
|
56
|
-
/**
|
|
57
|
-
* - X-axis field
|
|
58
|
-
*/
|
|
59
|
-
x: string;
|
|
60
|
-
/**
|
|
61
|
-
* - Y-axis field
|
|
62
|
-
*/
|
|
63
|
-
y: string;
|
|
64
|
-
/**
|
|
65
|
-
* - Color field
|
|
66
|
-
*/
|
|
67
|
-
color: string;
|
|
68
|
-
};
|
|
69
|
-
export type TickData = {
|
|
70
|
-
/**
|
|
71
|
-
* - Tick value
|
|
72
|
-
*/
|
|
73
|
-
value: any;
|
|
74
|
-
/**
|
|
75
|
-
* - Tick position in pixels
|
|
76
|
-
*/
|
|
77
|
-
position: number;
|
|
78
|
-
/**
|
|
79
|
-
* - Formatted tick label
|
|
80
|
-
*/
|
|
81
|
-
formattedValue: string;
|
|
82
|
-
};
|
|
83
|
-
export type AxisData = {
|
|
84
|
-
/**
|
|
85
|
-
* - Tick data
|
|
86
|
-
*/
|
|
87
|
-
ticks: TickData[];
|
|
88
|
-
/**
|
|
89
|
-
* - Axis label
|
|
90
|
-
*/
|
|
91
|
-
label: string;
|
|
92
|
-
/**
|
|
93
|
-
* - SVG transform attribute value
|
|
94
|
-
*/
|
|
95
|
-
transform: string;
|
|
96
|
-
/**
|
|
97
|
-
* - SVG transform for the label
|
|
98
|
-
*/
|
|
99
|
-
labelTransform: string;
|
|
100
|
-
};
|
|
101
|
-
export type BarData = {
|
|
102
|
-
/**
|
|
103
|
-
* - Original data point
|
|
104
|
-
*/
|
|
105
|
-
data: Object;
|
|
106
|
-
/**
|
|
107
|
-
* - X position
|
|
108
|
-
*/
|
|
109
|
-
x: number;
|
|
110
|
-
/**
|
|
111
|
-
* - Y position
|
|
112
|
-
*/
|
|
113
|
-
y: number;
|
|
114
|
-
/**
|
|
115
|
-
* - Width of the bar
|
|
116
|
-
*/
|
|
117
|
-
width: number;
|
|
118
|
-
/**
|
|
119
|
-
* - Height of the bar
|
|
120
|
-
*/
|
|
121
|
-
height: number;
|
|
122
|
-
/**
|
|
123
|
-
* - Color of the bar
|
|
124
|
-
*/
|
|
125
|
-
color: string;
|
|
126
|
-
};
|
|
127
|
-
export type LegendItem = {
|
|
128
|
-
/**
|
|
129
|
-
* - Legend item value
|
|
130
|
-
*/
|
|
131
|
-
value: any;
|
|
132
|
-
/**
|
|
133
|
-
* - Item color
|
|
134
|
-
*/
|
|
135
|
-
color: string;
|
|
136
|
-
/**
|
|
137
|
-
* - Y position
|
|
138
|
-
*/
|
|
139
|
-
y: number;
|
|
140
|
-
/**
|
|
141
|
-
* - Shape type ('rect' or 'circle')
|
|
142
|
-
*/
|
|
143
|
-
shape: string;
|
|
144
|
-
/**
|
|
145
|
-
* - Size of the marker
|
|
146
|
-
*/
|
|
147
|
-
markerSize: number;
|
|
148
|
-
};
|
|
149
|
-
export type LegendData = {
|
|
150
|
-
/**
|
|
151
|
-
* - Legend items
|
|
152
|
-
*/
|
|
153
|
-
items: LegendItem[];
|
|
154
|
-
/**
|
|
155
|
-
* - Legend title
|
|
156
|
-
*/
|
|
157
|
-
title: string;
|
|
158
|
-
/**
|
|
159
|
-
* - SVG transform attribute value
|
|
160
|
-
*/
|
|
161
|
-
transform: string;
|
|
162
|
-
};
|