@rokkit/chart 1.0.0-next.13 → 1.0.0-next.131
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 +140 -46
- package/dist/Plot/index.d.ts +5 -0
- package/dist/elements/index.d.ts +6 -0
- package/dist/index.d.ts +14 -0
- package/dist/lib/brewing/axes.svelte.d.ts +72 -0
- package/dist/lib/brewing/bars.svelte.d.ts +54 -0
- package/dist/lib/brewing/dimensions.svelte.d.ts +35 -0
- package/dist/lib/brewing/index.svelte.d.ts +118 -0
- package/dist/lib/brewing/legends.svelte.d.ts +54 -0
- package/dist/lib/brewing/scales.svelte.d.ts +29 -0
- package/dist/lib/brewing/types.d.ts +162 -0
- package/dist/lib/context.d.ts +13 -0
- package/dist/lib/scales.svelte.d.ts +35 -0
- package/dist/lib/utils.d.ts +58 -0
- package/dist/old_lib/brewer.d.ts +9 -0
- package/dist/old_lib/chart.d.ts +40 -0
- package/dist/old_lib/grid.d.ts +72 -0
- package/dist/old_lib/index.d.ts +4 -0
- package/dist/old_lib/plots.d.ts +3 -0
- package/dist/old_lib/swatch.d.ts +285 -0
- package/dist/old_lib/ticks.d.ts +36 -0
- package/dist/old_lib/utils.d.ts +1 -0
- package/dist/patterns/index.d.ts +9 -0
- package/dist/patterns/paths/constants.d.ts +1 -0
- package/dist/symbols/constants/index.d.ts +1 -0
- package/dist/symbols/index.d.ts +5 -0
- package/dist/template/constants.d.ts +43 -0
- package/dist/template/shapes/index.d.ts +4 -0
- package/package.json +28 -44
- package/src/Plot/Axis.svelte +95 -0
- package/src/Plot/Bar.svelte +96 -0
- package/src/Plot/Grid.svelte +68 -0
- package/src/Plot/Legend.svelte +127 -0
- package/src/Plot/Root.svelte +107 -0
- package/src/Plot/index.js +5 -0
- package/src/Symbol.svelte +21 -0
- package/src/Texture.svelte +10 -0
- package/src/elements/Bar.svelte +22 -24
- package/src/elements/ColorRamp.svelte +20 -22
- package/src/elements/ContinuousLegend.svelte +20 -17
- package/src/elements/DefinePatterns.svelte +24 -0
- package/src/elements/DiscreteLegend.svelte +15 -15
- package/src/elements/Label.svelte +11 -8
- package/src/elements/SymbolGrid.svelte +27 -0
- package/src/elements/index.js +6 -0
- package/src/examples/BarChartExample.svelte +81 -0
- package/src/index.js +18 -16
- package/src/lib/brewing/axes.svelte.js +177 -0
- package/src/lib/brewing/bars.svelte.js +114 -0
- package/src/lib/brewing/dimensions.svelte.js +56 -0
- package/src/lib/brewing/index.svelte.js +202 -0
- package/src/lib/brewing/legends.svelte.js +94 -0
- package/src/lib/brewing/scales.svelte.js +85 -0
- package/src/lib/brewing/types.js +73 -0
- package/src/lib/context.js +132 -0
- package/src/lib/scales.svelte.js +122 -0
- package/src/lib/utils.js +96 -120
- package/src/old_lib/brewer.js +25 -0
- package/src/old_lib/chart.js +213 -0
- package/src/old_lib/grid.js +85 -0
- package/src/old_lib/index.js +4 -0
- package/src/old_lib/plots.js +27 -0
- package/src/old_lib/swatch.js +16 -0
- package/src/old_lib/ticks.js +46 -0
- package/src/old_lib/utils.js +8 -0
- package/src/patterns/Brick.svelte +15 -0
- package/src/patterns/Circles.svelte +18 -0
- package/src/patterns/CrossHatch.svelte +12 -0
- package/src/patterns/CurvedWave.svelte +7 -0
- package/src/patterns/Dots.svelte +20 -0
- package/src/patterns/OutlineCircles.svelte +13 -0
- package/src/patterns/Tile.svelte +16 -0
- package/src/patterns/Triangles.svelte +13 -0
- package/src/patterns/Waves.svelte +9 -0
- package/src/patterns/index.js +14 -0
- package/src/patterns/paths/NamedPattern.svelte +9 -0
- package/src/patterns/paths/constants.js +4 -0
- package/src/symbols/RoundedSquare.svelte +22 -0
- package/src/symbols/Shape.svelte +24 -0
- package/src/symbols/constants/index.js +4 -0
- package/src/symbols/index.js +9 -0
- package/src/symbols/outline.svelte +60 -0
- package/src/symbols/solid.svelte +60 -0
- package/src/template/Texture.svelte +13 -0
- package/src/template/constants.js +43 -0
- package/src/template/shapes/Circles.svelte +13 -0
- package/src/template/shapes/Lines.svelte +14 -0
- package/src/template/shapes/Path.svelte +9 -0
- package/src/template/shapes/Polygons.svelte +9 -0
- package/src/template/shapes/index.js +4 -0
- package/LICENSE +0 -21
- package/src/chart/FacetGrid.svelte +0 -51
- package/src/chart/Grid.svelte +0 -34
- package/src/chart/Legend.svelte +0 -16
- package/src/chart/PatternDefs.svelte +0 -13
- package/src/chart/Swatch.svelte +0 -93
- package/src/chart/SwatchButton.svelte +0 -29
- package/src/chart/SwatchGrid.svelte +0 -55
- package/src/chart/Symbol.svelte +0 -37
- package/src/chart/Texture.svelte +0 -16
- package/src/chart/TexturedShape.svelte +0 -27
- package/src/chart/TimelapseChart.svelte +0 -97
- package/src/chart/Timer.svelte +0 -27
- package/src/chart.js +0 -9
- package/src/components/charts/Axis.svelte +0 -66
- package/src/components/charts/Chart.svelte +0 -35
- package/src/components/index.js +0 -23
- package/src/components/lib/axis.js +0 -0
- package/src/components/lib/chart.js +0 -187
- package/src/components/lib/color.js +0 -327
- package/src/components/lib/funnel.js +0 -204
- package/src/components/lib/index.js +0 -19
- package/src/components/lib/pattern.js +0 -190
- package/src/components/lib/rollup.js +0 -55
- package/src/components/lib/shape.js +0 -199
- package/src/components/lib/summary.js +0 -145
- package/src/components/lib/theme.js +0 -23
- package/src/components/lib/timer.js +0 -41
- package/src/components/lib/utils.js +0 -165
- package/src/components/plots/BarPlot.svelte +0 -36
- package/src/components/plots/BoxPlot.svelte +0 -54
- package/src/components/plots/ScatterPlot.svelte +0 -30
- package/src/components/store.js +0 -70
- package/src/constants.js +0 -66
- package/src/elements/PatternDefs.svelte +0 -13
- package/src/elements/PatternMask.svelte +0 -20
- package/src/elements/Symbol.svelte +0 -38
- package/src/elements/Tooltip.svelte +0 -23
- package/src/funnel.svelte +0 -35
- package/src/geom.js +0 -105
- package/src/lib/axis.js +0 -75
- package/src/lib/colors.js +0 -32
- package/src/lib/geom.js +0 -4
- package/src/lib/shapes.js +0 -144
- package/src/lib/timer.js +0 -44
- package/src/lookup.js +0 -29
- package/src/plots/BarPlot.svelte +0 -55
- package/src/plots/BoxPlot.svelte +0 -0
- package/src/plots/FunnelPlot.svelte +0 -33
- package/src/plots/HeatMap.svelte +0 -5
- package/src/plots/HeatMapCalendar.svelte +0 -129
- package/src/plots/LinePlot.svelte +0 -55
- package/src/plots/Plot.svelte +0 -25
- package/src/plots/RankBarPlot.svelte +0 -38
- package/src/plots/ScatterPlot.svelte +0 -20
- package/src/plots/ViolinPlot.svelte +0 -11
- package/src/plots/heatmap.js +0 -70
- package/src/plots/index.js +0 -10
- package/src/swatch.js +0 -11
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} TickSteps
|
|
3
|
+
* @property {number} major - count of major ticks
|
|
4
|
+
* @property {number} minor - count of minor ticks
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Generate an array of ticks for a given axis and the tick type
|
|
8
|
+
*
|
|
9
|
+
* @param {number} lower - The lower bound of the axis
|
|
10
|
+
* @param {number} upper - The upper bound of the axis
|
|
11
|
+
* @param {TickSteps} steps - The number of steps between major and minor ticks
|
|
12
|
+
* @param {string} type - The type of tick to generate
|
|
13
|
+
*
|
|
14
|
+
* @returns {Array} - An array of objects representing the ticks
|
|
15
|
+
*/
|
|
16
|
+
export function ticksByType(lower: number, upper: number, steps: TickSteps, type: string): any[];
|
|
17
|
+
/**
|
|
18
|
+
* Generate an array of ticks for a given axis
|
|
19
|
+
*
|
|
20
|
+
* @param {number} lower - The lower bound of the axis
|
|
21
|
+
* @param {number} upper - The upper bound of the axis
|
|
22
|
+
* @param {TickSteps} steps - The number of steps between major and minor ticks
|
|
23
|
+
*
|
|
24
|
+
* @returns {Array} - An array of objects representing the ticks
|
|
25
|
+
*/
|
|
26
|
+
export function getTicks(lower: number, upper: number, steps?: TickSteps): any[];
|
|
27
|
+
export type TickSteps = {
|
|
28
|
+
/**
|
|
29
|
+
* - count of major ticks
|
|
30
|
+
*/
|
|
31
|
+
major: number;
|
|
32
|
+
/**
|
|
33
|
+
* - count of minor ticks
|
|
34
|
+
*/
|
|
35
|
+
minor: number;
|
|
36
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function scaledPathCollection(paths: any): {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as Brick } from "./Brick.svelte";
|
|
2
|
+
export { default as Circles } from "./Circles.svelte";
|
|
3
|
+
export { default as Dots } from "./Dots.svelte";
|
|
4
|
+
export { default as CrossHatch } from "./CrossHatch.svelte";
|
|
5
|
+
export { default as Waves } from "./Waves.svelte";
|
|
6
|
+
export { default as Tile } from "./Tile.svelte";
|
|
7
|
+
export { default as Triangles } from "./Triangles.svelte";
|
|
8
|
+
export { default as CurvedWave } from "./CurvedWave.svelte";
|
|
9
|
+
export { default as OutlineCircles } from "./OutlineCircles.svelte";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const patterns: {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const namedShapes: {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export namespace library {
|
|
2
|
+
namespace Brick {
|
|
3
|
+
let component: string;
|
|
4
|
+
let allowed: string[];
|
|
5
|
+
let data: {
|
|
6
|
+
x1: number;
|
|
7
|
+
y1: number;
|
|
8
|
+
x2: number;
|
|
9
|
+
y2: number;
|
|
10
|
+
}[];
|
|
11
|
+
}
|
|
12
|
+
namespace Circles {
|
|
13
|
+
let component_1: string;
|
|
14
|
+
export { component_1 as component };
|
|
15
|
+
let allowed_1: string[];
|
|
16
|
+
export { allowed_1 as allowed };
|
|
17
|
+
let data_1: {
|
|
18
|
+
cx: number;
|
|
19
|
+
cy: number;
|
|
20
|
+
r: number;
|
|
21
|
+
}[];
|
|
22
|
+
export { data_1 as data };
|
|
23
|
+
}
|
|
24
|
+
namespace Dominoes {
|
|
25
|
+
let component_2: string;
|
|
26
|
+
export { component_2 as component };
|
|
27
|
+
let allowed_2: string[];
|
|
28
|
+
export { allowed_2 as allowed };
|
|
29
|
+
let data_2: {
|
|
30
|
+
cx: number;
|
|
31
|
+
cy: number;
|
|
32
|
+
r: number;
|
|
33
|
+
}[];
|
|
34
|
+
export { data_2 as data };
|
|
35
|
+
}
|
|
36
|
+
namespace Waves {
|
|
37
|
+
let component_3: string;
|
|
38
|
+
export { component_3 as component };
|
|
39
|
+
export let stroke: boolean;
|
|
40
|
+
let data_3: (string | number)[][];
|
|
41
|
+
export { data_3 as data };
|
|
42
|
+
}
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,63 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokkit/chart",
|
|
3
|
-
"version": "1.0.0-next.
|
|
4
|
-
"
|
|
3
|
+
"version": "1.0.0-next.131",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Data-driven chart components",
|
|
5
6
|
"author": "Jerry Thomas <me@jerrythomas.name>",
|
|
6
7
|
"license": "MIT",
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"types": "dist/index.d.ts",
|
|
11
|
-
"type": "module",
|
|
8
|
+
"npm": {
|
|
9
|
+
"publish": false
|
|
10
|
+
},
|
|
12
11
|
"publishConfig": {
|
|
13
12
|
"access": "public"
|
|
14
13
|
},
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"eslint": "^8.33.0",
|
|
20
|
-
"js-yaml": "^4.1.0",
|
|
21
|
-
"jsdom": "^19.0.0",
|
|
22
|
-
"svelte": "^3.55.1",
|
|
23
|
-
"typescript": "^4.9.5",
|
|
24
|
-
"vite": "^4.1.1",
|
|
25
|
-
"vitest": "~0.19.1",
|
|
26
|
-
"eslint-config-shared": "1.0.0",
|
|
27
|
-
"shared-config": "1.0.0"
|
|
28
|
-
},
|
|
29
|
-
"dependencies": {
|
|
30
|
-
"d3-array": "^3.2.2",
|
|
31
|
-
"d3-collection": "^1.0.7",
|
|
32
|
-
"d3-scale": "^4.0.2",
|
|
33
|
-
"d3-shape": "^3.2.0",
|
|
34
|
-
"date-fns": "^2.29.3",
|
|
35
|
-
"ramda": "^0.28.0",
|
|
36
|
-
"yootils": "^0.3.1",
|
|
37
|
-
"@rokkit/core": "1.0.0-next.13"
|
|
14
|
+
"scripts": {
|
|
15
|
+
"prepublishOnly": "bun clean && bun tsc --project tsconfig.build.json",
|
|
16
|
+
"clean": "rm -rf dist",
|
|
17
|
+
"build": "bun prepublishOnly"
|
|
38
18
|
},
|
|
39
19
|
"files": [
|
|
40
20
|
"src/**/*.js",
|
|
41
21
|
"src/**/*.svelte",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
22
|
+
"dist/**/*.d.ts",
|
|
23
|
+
"README.md",
|
|
24
|
+
"package.json"
|
|
45
25
|
],
|
|
46
26
|
"exports": {
|
|
47
|
-
"./src": "./src",
|
|
48
27
|
"./package.json": "./package.json",
|
|
49
28
|
".": {
|
|
50
29
|
"types": "./dist/index.d.ts",
|
|
51
|
-
"import": "./src/index.js"
|
|
30
|
+
"import": "./src/index.js",
|
|
31
|
+
"svelte": "./src/index.js"
|
|
52
32
|
}
|
|
53
33
|
},
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@rokkit/core": "latest",
|
|
36
|
+
"@rokkit/data": "latest",
|
|
37
|
+
"@rokkit/states": "latest",
|
|
38
|
+
"d3-array": "^3.2.4",
|
|
39
|
+
"d3-format": "^3.1.2",
|
|
40
|
+
"d3-axis": "^3.0.0",
|
|
41
|
+
"d3-scale": "^4.0.2",
|
|
42
|
+
"d3-scale-chromatic": "^3.1.0",
|
|
43
|
+
"d3-selection": "^3.0.0",
|
|
44
|
+
"d3-transition": "^3.0.1",
|
|
45
|
+
"ramda": "^0.32.0"
|
|
62
46
|
}
|
|
63
|
-
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from 'svelte'
|
|
3
|
+
import { createXAxis, createYAxis, createTickAttributes } from '../lib/brewing/axes.svelte.js'
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
type = 'x',
|
|
7
|
+
field = null,
|
|
8
|
+
label = '',
|
|
9
|
+
ticks = null,
|
|
10
|
+
tickFormat = null,
|
|
11
|
+
grid = false
|
|
12
|
+
} = $props()
|
|
13
|
+
|
|
14
|
+
// Get brewer from context
|
|
15
|
+
const brewer = getContext('chart-brewer')
|
|
16
|
+
|
|
17
|
+
// Set field mappings in the brewer
|
|
18
|
+
$effect(() => {
|
|
19
|
+
if (field) {
|
|
20
|
+
brewer.setFields({
|
|
21
|
+
[type]: field
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// Ensure scales are updated
|
|
25
|
+
brewer.createScales()
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// Compute axis data whenever scales change
|
|
30
|
+
let axisData = $derived(
|
|
31
|
+
type === 'x'
|
|
32
|
+
? brewer.createXAxis({ tickCount: ticks, tickFormat, label })
|
|
33
|
+
: brewer.createYAxis({ tickCount: ticks, tickFormat, label })
|
|
34
|
+
)
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<g class="axis {type}-axis" transform={axisData.transform} data-plot-axis={type}>
|
|
38
|
+
{#if axisData.ticks.length > 0}
|
|
39
|
+
<!-- Axis line -->
|
|
40
|
+
<line
|
|
41
|
+
data-plot-axis-line
|
|
42
|
+
x1={type === 'x' ? 0 : 0}
|
|
43
|
+
y1={type === 'x' ? 0 : 0}
|
|
44
|
+
x2={type === 'x' ? brewer.getDimensions().innerWidth : 0}
|
|
45
|
+
y2={type === 'x' ? 0 : brewer.getDimensions().innerHeight}
|
|
46
|
+
stroke="currentColor"
|
|
47
|
+
/>
|
|
48
|
+
|
|
49
|
+
<!-- Ticks -->
|
|
50
|
+
{#each axisData.ticks as tick, index (index)}
|
|
51
|
+
{@const attrs = createTickAttributes(tick, type)}
|
|
52
|
+
<g {...attrs}>
|
|
53
|
+
<line
|
|
54
|
+
x1="0"
|
|
55
|
+
y1="0"
|
|
56
|
+
x2={type === 'x' ? 0 : -6}
|
|
57
|
+
y2={type === 'x' ? 6 : 0}
|
|
58
|
+
stroke="currentColor"
|
|
59
|
+
/>
|
|
60
|
+
<text x={type === 'x' ? 0 : -9} y={type === 'x' ? 9 : 0} data-plot-tick-label>
|
|
61
|
+
{tick.formattedValue}
|
|
62
|
+
</text>
|
|
63
|
+
</g>
|
|
64
|
+
{/each}
|
|
65
|
+
|
|
66
|
+
<!-- Axis label -->
|
|
67
|
+
{#if label}
|
|
68
|
+
<text
|
|
69
|
+
class="axis-label {type}-axis-label"
|
|
70
|
+
transform={axisData.labelTransform}
|
|
71
|
+
text-anchor="middle"
|
|
72
|
+
data-plot-axis-label
|
|
73
|
+
>
|
|
74
|
+
{label}
|
|
75
|
+
</text>
|
|
76
|
+
{/if}
|
|
77
|
+
{/if}
|
|
78
|
+
</g>
|
|
79
|
+
|
|
80
|
+
<style>
|
|
81
|
+
.axis {
|
|
82
|
+
font-size: 12px;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.axis-label {
|
|
86
|
+
font-size: 14px;
|
|
87
|
+
font-weight: 500;
|
|
88
|
+
fill: currentColor;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
[data-plot-tick-label] {
|
|
92
|
+
font-size: 11px;
|
|
93
|
+
fill: currentColor;
|
|
94
|
+
}
|
|
95
|
+
</style>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from 'svelte'
|
|
3
|
+
import { ChartBrewer } from '../lib/brewing/index.svelte.js'
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
x = null,
|
|
7
|
+
y = null,
|
|
8
|
+
fill = null,
|
|
9
|
+
color = '#4682b4',
|
|
10
|
+
opacity = 1,
|
|
11
|
+
animationDuration = 300,
|
|
12
|
+
onClick = null
|
|
13
|
+
} = $props()
|
|
14
|
+
|
|
15
|
+
// Get brewer from context
|
|
16
|
+
const brewer = getContext('chart-brewer')
|
|
17
|
+
|
|
18
|
+
// Set field mappings in the brewer
|
|
19
|
+
$effect(() => {
|
|
20
|
+
brewer.setFields({
|
|
21
|
+
x,
|
|
22
|
+
y,
|
|
23
|
+
color: fill
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// Ensure scales are updated
|
|
27
|
+
brewer.createScales()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// Compute bars whenever data or fields change
|
|
31
|
+
let bars = $derived(brewer.createBars())
|
|
32
|
+
|
|
33
|
+
// Animation transition values
|
|
34
|
+
let initialY = $state(0)
|
|
35
|
+
let initialHeight = $state(0)
|
|
36
|
+
|
|
37
|
+
// Handle resetting animation state for new bars
|
|
38
|
+
$effect(() => {
|
|
39
|
+
if (bars && bars.length > 0) {
|
|
40
|
+
initialY = brewer.getDimensions().innerHeight
|
|
41
|
+
initialHeight = 0
|
|
42
|
+
|
|
43
|
+
// Reset to actual positions after a delay
|
|
44
|
+
setTimeout(() => {
|
|
45
|
+
initialY = 0
|
|
46
|
+
initialHeight = 0
|
|
47
|
+
}, 10)
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// Handle bar click
|
|
52
|
+
function handleClick(event, bar) {
|
|
53
|
+
if (onClick) onClick(bar.data, event)
|
|
54
|
+
}
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
{#if bars && bars.length > 0}
|
|
58
|
+
<g class="chart-bars" data-plot-type="bar">
|
|
59
|
+
{#each bars as bar, i (bar.data[x])}
|
|
60
|
+
{@const barY = initialY > 0 ? brewer.getDimensions().innerHeight : bar.y}
|
|
61
|
+
{@const barHeight = initialHeight > 0 ? 0 : bar.height}
|
|
62
|
+
|
|
63
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions, a11y_click_events_have_key_events -->
|
|
64
|
+
<rect
|
|
65
|
+
class="bar"
|
|
66
|
+
x={bar.x}
|
|
67
|
+
y={barY}
|
|
68
|
+
width={bar.width}
|
|
69
|
+
height={barHeight}
|
|
70
|
+
fill={bar.color}
|
|
71
|
+
{opacity}
|
|
72
|
+
onclick={(event) => handleClick(event, bar)}
|
|
73
|
+
onmouseenter={(event) => {
|
|
74
|
+
event.target.setAttribute('opacity', Math.min(opacity + 0.2, 1))
|
|
75
|
+
}}
|
|
76
|
+
onmouseleave={(event) => {
|
|
77
|
+
event.target.setAttribute('opacity', opacity)
|
|
78
|
+
}}
|
|
79
|
+
style="transition: y {animationDuration}ms ease, height {animationDuration}ms ease;"
|
|
80
|
+
role="graphics-symbol"
|
|
81
|
+
aria-label="Bar representing {bar.data[x]} with value {bar.data[y]}"
|
|
82
|
+
data-plot-element="bar"
|
|
83
|
+
data-plot-value={bar.data[y]}
|
|
84
|
+
data-plot-category={bar.data[x]}
|
|
85
|
+
>
|
|
86
|
+
<title>{bar.data[x]}: {bar.data[y]}</title>
|
|
87
|
+
</rect>
|
|
88
|
+
{/each}
|
|
89
|
+
</g>
|
|
90
|
+
{/if}
|
|
91
|
+
|
|
92
|
+
<style>
|
|
93
|
+
.chart-bars .bar {
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
}
|
|
96
|
+
</style>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from 'svelte'
|
|
3
|
+
import { createGrid } from '../lib/brewing/axes.svelte.js'
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
direction = 'both',
|
|
7
|
+
xTicks = null,
|
|
8
|
+
yTicks = null,
|
|
9
|
+
color = 'currentColor',
|
|
10
|
+
opacity = 0.1,
|
|
11
|
+
lineStyle = 'solid'
|
|
12
|
+
} = $props()
|
|
13
|
+
|
|
14
|
+
// Get brewer from context
|
|
15
|
+
const brewer = getContext('chart-brewer')
|
|
16
|
+
|
|
17
|
+
// Get grid data
|
|
18
|
+
let gridData = $derived(
|
|
19
|
+
brewer.createGrid({
|
|
20
|
+
direction,
|
|
21
|
+
xTickCount: xTicks,
|
|
22
|
+
yTickCount: yTicks
|
|
23
|
+
})
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
// Convert lineStyle to stroke-dasharray
|
|
27
|
+
let strokeDasharray = $derived(
|
|
28
|
+
lineStyle === 'dashed' ? '5,5' : lineStyle === 'dotted' ? '1,3' : 'none'
|
|
29
|
+
)
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<g class="chart-grid" data-plot-grid={direction}>
|
|
33
|
+
{#if direction === 'x' || direction === 'both'}
|
|
34
|
+
{#each gridData.xLines as line, index (index)}
|
|
35
|
+
<line
|
|
36
|
+
data-plot-grid-line="x"
|
|
37
|
+
x1={line.x1}
|
|
38
|
+
y1={line.y1}
|
|
39
|
+
x2={line.x2}
|
|
40
|
+
y2={line.y2}
|
|
41
|
+
stroke={color}
|
|
42
|
+
stroke-opacity={opacity}
|
|
43
|
+
stroke-dasharray={strokeDasharray}
|
|
44
|
+
/>
|
|
45
|
+
{/each}
|
|
46
|
+
{/if}
|
|
47
|
+
|
|
48
|
+
{#if direction === 'y' || direction === 'both'}
|
|
49
|
+
{#each gridData.yLines as line, index (index)}
|
|
50
|
+
<line
|
|
51
|
+
data-plot-grid-line="y"
|
|
52
|
+
x1={line.x1}
|
|
53
|
+
y1={line.y1}
|
|
54
|
+
x2={line.x2}
|
|
55
|
+
y2={line.y2}
|
|
56
|
+
stroke={color}
|
|
57
|
+
stroke-opacity={opacity}
|
|
58
|
+
stroke-dasharray={strokeDasharray}
|
|
59
|
+
/>
|
|
60
|
+
{/each}
|
|
61
|
+
{/if}
|
|
62
|
+
</g>
|
|
63
|
+
|
|
64
|
+
<style>
|
|
65
|
+
.chart-grid {
|
|
66
|
+
pointer-events: none;
|
|
67
|
+
}
|
|
68
|
+
</style>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script>
|
|
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
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
{#if legendData.items.length > 0}
|
|
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>
|
|
104
|
+
{/if}
|
|
105
|
+
|
|
106
|
+
<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>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script>
|
|
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
|
+
children
|
|
14
|
+
} = $props()
|
|
15
|
+
|
|
16
|
+
// Create chart brewer instance
|
|
17
|
+
let brewer = $state(new ChartBrewer())
|
|
18
|
+
|
|
19
|
+
$effect(() => {
|
|
20
|
+
brewer.setDimensions({ width, height, margin })
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// Chart dimensions derived from brewer
|
|
24
|
+
let dimensions = $derived(brewer.getDimensions())
|
|
25
|
+
|
|
26
|
+
// Process data
|
|
27
|
+
$effect(() => {
|
|
28
|
+
// If data has a select method (dataset object), call it to get actual data
|
|
29
|
+
const chartData = data.select && typeof data.select === 'function' ? data.select() : data
|
|
30
|
+
|
|
31
|
+
// Update brewer with data and fields
|
|
32
|
+
brewer.setData(chartData)
|
|
33
|
+
brewer.setFields({ color: fill })
|
|
34
|
+
|
|
35
|
+
// Create scales after setting data
|
|
36
|
+
brewer.createScales()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Provide chart context to child components
|
|
40
|
+
setContext('chart-brewer', brewer)
|
|
41
|
+
|
|
42
|
+
// Handle responsive behavior
|
|
43
|
+
let container
|
|
44
|
+
|
|
45
|
+
$effect(() => {
|
|
46
|
+
if (!responsive || !container || !document) return
|
|
47
|
+
|
|
48
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
49
|
+
const entry = entries[0]
|
|
50
|
+
if (!entry) return
|
|
51
|
+
|
|
52
|
+
const containerWidth = entry.contentRect.width
|
|
53
|
+
const aspectRatio = height / width
|
|
54
|
+
|
|
55
|
+
// Update chart dimensions while maintaining aspect ratio
|
|
56
|
+
brewer.setDimensions({
|
|
57
|
+
width: containerWidth,
|
|
58
|
+
height: containerWidth * aspectRatio
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// Update scales after dimensions change
|
|
62
|
+
brewer.createScales()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// Start observing container size
|
|
66
|
+
resizeObserver.observe(container)
|
|
67
|
+
|
|
68
|
+
return () => {
|
|
69
|
+
resizeObserver.disconnect()
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<div class="chart-container" bind:this={container} data-plot-root>
|
|
75
|
+
<svg
|
|
76
|
+
width={dimensions.width}
|
|
77
|
+
height={dimensions.height}
|
|
78
|
+
viewBox="0 0 {dimensions.width} {dimensions.height}"
|
|
79
|
+
role="img"
|
|
80
|
+
aria-label="Chart visualization"
|
|
81
|
+
>
|
|
82
|
+
<g
|
|
83
|
+
class="chart-area"
|
|
84
|
+
transform="translate({dimensions.margin.left}, {dimensions.margin.top})"
|
|
85
|
+
data-plot-canvas
|
|
86
|
+
>
|
|
87
|
+
{@render children?.()}
|
|
88
|
+
</g>
|
|
89
|
+
</svg>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<style>
|
|
93
|
+
.chart-container {
|
|
94
|
+
position: relative;
|
|
95
|
+
width: 100%;
|
|
96
|
+
height: auto;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
svg {
|
|
100
|
+
display: block;
|
|
101
|
+
overflow: visible;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.chart-area {
|
|
105
|
+
pointer-events: all;
|
|
106
|
+
}
|
|
107
|
+
</style>
|