@rokkit/chart 1.0.0-next.13 → 1.0.0-next.130

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.
Files changed (149) hide show
  1. package/README.md +140 -46
  2. package/dist/Plot/index.d.ts +5 -0
  3. package/dist/elements/index.d.ts +6 -0
  4. package/dist/index.d.ts +14 -0
  5. package/dist/lib/brewing/axes.svelte.d.ts +72 -0
  6. package/dist/lib/brewing/bars.svelte.d.ts +54 -0
  7. package/dist/lib/brewing/dimensions.svelte.d.ts +35 -0
  8. package/dist/lib/brewing/index.svelte.d.ts +118 -0
  9. package/dist/lib/brewing/legends.svelte.d.ts +54 -0
  10. package/dist/lib/brewing/scales.svelte.d.ts +29 -0
  11. package/dist/lib/brewing/types.d.ts +162 -0
  12. package/dist/lib/context.d.ts +13 -0
  13. package/dist/lib/scales.svelte.d.ts +35 -0
  14. package/dist/lib/utils.d.ts +58 -0
  15. package/dist/old_lib/brewer.d.ts +9 -0
  16. package/dist/old_lib/chart.d.ts +40 -0
  17. package/dist/old_lib/grid.d.ts +72 -0
  18. package/dist/old_lib/index.d.ts +4 -0
  19. package/dist/old_lib/plots.d.ts +3 -0
  20. package/dist/old_lib/swatch.d.ts +285 -0
  21. package/dist/old_lib/ticks.d.ts +36 -0
  22. package/dist/old_lib/utils.d.ts +1 -0
  23. package/dist/patterns/index.d.ts +9 -0
  24. package/dist/patterns/paths/constants.d.ts +1 -0
  25. package/dist/symbols/constants/index.d.ts +1 -0
  26. package/dist/symbols/index.d.ts +5 -0
  27. package/dist/template/constants.d.ts +43 -0
  28. package/dist/template/shapes/index.d.ts +4 -0
  29. package/package.json +28 -44
  30. package/src/Plot/Axis.svelte +95 -0
  31. package/src/Plot/Bar.svelte +96 -0
  32. package/src/Plot/Grid.svelte +68 -0
  33. package/src/Plot/Legend.svelte +127 -0
  34. package/src/Plot/Root.svelte +107 -0
  35. package/src/Plot/index.js +5 -0
  36. package/src/Symbol.svelte +21 -0
  37. package/src/Texture.svelte +10 -0
  38. package/src/elements/Bar.svelte +22 -24
  39. package/src/elements/ColorRamp.svelte +20 -22
  40. package/src/elements/ContinuousLegend.svelte +20 -17
  41. package/src/elements/DefinePatterns.svelte +24 -0
  42. package/src/elements/DiscreteLegend.svelte +15 -15
  43. package/src/elements/Label.svelte +11 -8
  44. package/src/elements/SymbolGrid.svelte +27 -0
  45. package/src/elements/index.js +6 -0
  46. package/src/examples/BarChartExample.svelte +81 -0
  47. package/src/index.js +18 -16
  48. package/src/lib/brewing/axes.svelte.js +177 -0
  49. package/src/lib/brewing/bars.svelte.js +114 -0
  50. package/src/lib/brewing/dimensions.svelte.js +56 -0
  51. package/src/lib/brewing/index.svelte.js +202 -0
  52. package/src/lib/brewing/legends.svelte.js +94 -0
  53. package/src/lib/brewing/scales.svelte.js +85 -0
  54. package/src/lib/brewing/types.js +73 -0
  55. package/src/lib/context.js +132 -0
  56. package/src/lib/scales.svelte.js +122 -0
  57. package/src/lib/utils.js +96 -120
  58. package/src/old_lib/brewer.js +25 -0
  59. package/src/old_lib/chart.js +213 -0
  60. package/src/old_lib/grid.js +85 -0
  61. package/src/old_lib/index.js +4 -0
  62. package/src/old_lib/plots.js +27 -0
  63. package/src/old_lib/swatch.js +16 -0
  64. package/src/old_lib/ticks.js +46 -0
  65. package/src/old_lib/utils.js +8 -0
  66. package/src/patterns/Brick.svelte +15 -0
  67. package/src/patterns/Circles.svelte +18 -0
  68. package/src/patterns/CrossHatch.svelte +12 -0
  69. package/src/patterns/CurvedWave.svelte +7 -0
  70. package/src/patterns/Dots.svelte +20 -0
  71. package/src/patterns/OutlineCircles.svelte +13 -0
  72. package/src/patterns/Tile.svelte +16 -0
  73. package/src/patterns/Triangles.svelte +13 -0
  74. package/src/patterns/Waves.svelte +9 -0
  75. package/src/patterns/index.js +14 -0
  76. package/src/patterns/paths/NamedPattern.svelte +9 -0
  77. package/src/patterns/paths/constants.js +4 -0
  78. package/src/symbols/RoundedSquare.svelte +22 -0
  79. package/src/symbols/Shape.svelte +24 -0
  80. package/src/symbols/constants/index.js +4 -0
  81. package/src/symbols/index.js +9 -0
  82. package/src/symbols/outline.svelte +60 -0
  83. package/src/symbols/solid.svelte +60 -0
  84. package/src/template/Texture.svelte +13 -0
  85. package/src/template/constants.js +43 -0
  86. package/src/template/shapes/Circles.svelte +13 -0
  87. package/src/template/shapes/Lines.svelte +14 -0
  88. package/src/template/shapes/Path.svelte +9 -0
  89. package/src/template/shapes/Polygons.svelte +9 -0
  90. package/src/template/shapes/index.js +4 -0
  91. package/LICENSE +0 -21
  92. package/src/chart/FacetGrid.svelte +0 -51
  93. package/src/chart/Grid.svelte +0 -34
  94. package/src/chart/Legend.svelte +0 -16
  95. package/src/chart/PatternDefs.svelte +0 -13
  96. package/src/chart/Swatch.svelte +0 -93
  97. package/src/chart/SwatchButton.svelte +0 -29
  98. package/src/chart/SwatchGrid.svelte +0 -55
  99. package/src/chart/Symbol.svelte +0 -37
  100. package/src/chart/Texture.svelte +0 -16
  101. package/src/chart/TexturedShape.svelte +0 -27
  102. package/src/chart/TimelapseChart.svelte +0 -97
  103. package/src/chart/Timer.svelte +0 -27
  104. package/src/chart.js +0 -9
  105. package/src/components/charts/Axis.svelte +0 -66
  106. package/src/components/charts/Chart.svelte +0 -35
  107. package/src/components/index.js +0 -23
  108. package/src/components/lib/axis.js +0 -0
  109. package/src/components/lib/chart.js +0 -187
  110. package/src/components/lib/color.js +0 -327
  111. package/src/components/lib/funnel.js +0 -204
  112. package/src/components/lib/index.js +0 -19
  113. package/src/components/lib/pattern.js +0 -190
  114. package/src/components/lib/rollup.js +0 -55
  115. package/src/components/lib/shape.js +0 -199
  116. package/src/components/lib/summary.js +0 -145
  117. package/src/components/lib/theme.js +0 -23
  118. package/src/components/lib/timer.js +0 -41
  119. package/src/components/lib/utils.js +0 -165
  120. package/src/components/plots/BarPlot.svelte +0 -36
  121. package/src/components/plots/BoxPlot.svelte +0 -54
  122. package/src/components/plots/ScatterPlot.svelte +0 -30
  123. package/src/components/store.js +0 -70
  124. package/src/constants.js +0 -66
  125. package/src/elements/PatternDefs.svelte +0 -13
  126. package/src/elements/PatternMask.svelte +0 -20
  127. package/src/elements/Symbol.svelte +0 -38
  128. package/src/elements/Tooltip.svelte +0 -23
  129. package/src/funnel.svelte +0 -35
  130. package/src/geom.js +0 -105
  131. package/src/lib/axis.js +0 -75
  132. package/src/lib/colors.js +0 -32
  133. package/src/lib/geom.js +0 -4
  134. package/src/lib/shapes.js +0 -144
  135. package/src/lib/timer.js +0 -44
  136. package/src/lookup.js +0 -29
  137. package/src/plots/BarPlot.svelte +0 -55
  138. package/src/plots/BoxPlot.svelte +0 -0
  139. package/src/plots/FunnelPlot.svelte +0 -33
  140. package/src/plots/HeatMap.svelte +0 -5
  141. package/src/plots/HeatMapCalendar.svelte +0 -129
  142. package/src/plots/LinePlot.svelte +0 -55
  143. package/src/plots/Plot.svelte +0 -25
  144. package/src/plots/RankBarPlot.svelte +0 -38
  145. package/src/plots/ScatterPlot.svelte +0 -20
  146. package/src/plots/ViolinPlot.svelte +0 -11
  147. package/src/plots/heatmap.js +0 -70
  148. package/src/plots/index.js +0 -10
  149. 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,5 @@
1
+ export const shapes: string[];
2
+ export const components: {
3
+ default: import("svelte/legacy").LegacyComponentType;
4
+ 'rounded-square': import("svelte/legacy").LegacyComponentType;
5
+ };
@@ -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
+ }
@@ -0,0 +1,4 @@
1
+ export { default as Circles } from "./Circles.svelte";
2
+ export { default as Lines } from "./Lines.svelte";
3
+ export { default as Path } from "./Path.svelte";
4
+ export { default as Polygons } from "./Polygons.svelte";
package/package.json CHANGED
@@ -1,63 +1,47 @@
1
1
  {
2
2
  "name": "@rokkit/chart",
3
- "version": "1.0.0-next.13",
4
- "description": "Components for making interactive charts.",
3
+ "version": "1.0.0-next.130",
4
+ "type": "module",
5
+ "description": "Data-driven chart components",
5
6
  "author": "Jerry Thomas <me@jerrythomas.name>",
6
7
  "license": "MIT",
7
- "main": "index.js",
8
- "svelte": "src/index.js",
9
- "module": "src/index.js",
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
- "devDependencies": {
16
- "@sveltejs/vite-plugin-svelte": "^2.0.2",
17
- "@testing-library/svelte": "^3.2.2",
18
- "@vitest/ui": "~0.12.10",
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
- "!src/fixtures",
43
- "!src/mocks",
44
- "!src/**/*.spec.js"
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
- "scripts": {
55
- "lint": "prettier --check --plugin-search-dir=. . && eslint .",
56
- "format": "prettier --write --plugin-search-dir=. .",
57
- "test:ci": "vitest run",
58
- "test:ui": "vitest --ui",
59
- "test": "vitest",
60
- "coverage": "vitest run --coverage",
61
- "upgrade": "pnpm upgrade"
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>