@rokkit/chart 1.0.0-next.14 → 1.0.0-next.140

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 (150) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +150 -46
  3. package/dist/Plot/index.d.ts +5 -0
  4. package/dist/elements/index.d.ts +6 -0
  5. package/dist/index.d.ts +14 -0
  6. package/dist/lib/brewing/axes.svelte.d.ts +66 -0
  7. package/dist/lib/brewing/bars.svelte.d.ts +56 -0
  8. package/dist/lib/brewing/dimensions.svelte.d.ts +35 -0
  9. package/dist/lib/brewing/index.svelte.d.ts +118 -0
  10. package/dist/lib/brewing/legends.svelte.d.ts +48 -0
  11. package/dist/lib/brewing/scales.svelte.d.ts +24 -0
  12. package/dist/lib/brewing/types.d.ts +162 -0
  13. package/dist/lib/context.d.ts +13 -0
  14. package/dist/lib/scales.svelte.d.ts +35 -0
  15. package/dist/lib/utils.d.ts +60 -0
  16. package/dist/old_lib/brewer.d.ts +9 -0
  17. package/dist/old_lib/chart.d.ts +40 -0
  18. package/dist/old_lib/grid.d.ts +72 -0
  19. package/dist/old_lib/index.d.ts +4 -0
  20. package/dist/old_lib/plots.d.ts +3 -0
  21. package/dist/old_lib/swatch.d.ts +285 -0
  22. package/dist/old_lib/ticks.d.ts +36 -0
  23. package/dist/old_lib/utils.d.ts +1 -0
  24. package/dist/patterns/index.d.ts +9 -0
  25. package/dist/patterns/paths/constants.d.ts +1 -0
  26. package/dist/symbols/constants/index.d.ts +1 -0
  27. package/dist/symbols/index.d.ts +5 -0
  28. package/dist/template/constants.d.ts +43 -0
  29. package/dist/template/shapes/index.d.ts +4 -0
  30. package/package.json +34 -44
  31. package/src/Plot/Axis.svelte +95 -0
  32. package/src/Plot/Bar.svelte +96 -0
  33. package/src/Plot/Grid.svelte +68 -0
  34. package/src/Plot/Legend.svelte +127 -0
  35. package/src/Plot/Root.svelte +107 -0
  36. package/src/Plot/index.js +5 -0
  37. package/src/Symbol.svelte +21 -0
  38. package/src/Texture.svelte +18 -0
  39. package/src/elements/Bar.svelte +22 -24
  40. package/src/elements/ColorRamp.svelte +20 -22
  41. package/src/elements/ContinuousLegend.svelte +20 -17
  42. package/src/elements/DefinePatterns.svelte +24 -0
  43. package/src/elements/DiscreteLegend.svelte +15 -15
  44. package/src/elements/Label.svelte +4 -8
  45. package/src/elements/SymbolGrid.svelte +23 -0
  46. package/src/elements/index.js +6 -0
  47. package/src/examples/BarChartExample.svelte +81 -0
  48. package/src/index.js +18 -16
  49. package/src/lib/brewing/axes.svelte.js +265 -0
  50. package/src/lib/brewing/bars.svelte.js +177 -0
  51. package/src/lib/brewing/dimensions.svelte.js +56 -0
  52. package/src/lib/brewing/index.svelte.js +205 -0
  53. package/src/lib/brewing/legends.svelte.js +137 -0
  54. package/src/lib/brewing/scales.svelte.js +106 -0
  55. package/src/lib/brewing/types.js +73 -0
  56. package/src/lib/context.js +133 -0
  57. package/src/lib/scales.svelte.js +172 -0
  58. package/src/lib/utils.js +107 -120
  59. package/src/old_lib/brewer.js +25 -0
  60. package/src/old_lib/chart.js +213 -0
  61. package/src/old_lib/grid.js +85 -0
  62. package/src/old_lib/index.js +4 -0
  63. package/src/old_lib/plots.js +27 -0
  64. package/src/old_lib/swatch.js +16 -0
  65. package/src/old_lib/ticks.js +46 -0
  66. package/src/old_lib/utils.js +8 -0
  67. package/src/patterns/Brick.svelte +15 -0
  68. package/src/patterns/Circles.svelte +18 -0
  69. package/src/patterns/CrossHatch.svelte +12 -0
  70. package/src/patterns/CurvedWave.svelte +7 -0
  71. package/src/patterns/Dots.svelte +20 -0
  72. package/src/patterns/OutlineCircles.svelte +13 -0
  73. package/src/patterns/README.md +3 -0
  74. package/src/patterns/Tile.svelte +16 -0
  75. package/src/patterns/Triangles.svelte +13 -0
  76. package/src/patterns/Waves.svelte +9 -0
  77. package/src/patterns/index.js +14 -0
  78. package/src/patterns/paths/NamedPattern.svelte +9 -0
  79. package/src/patterns/paths/constants.js +4 -0
  80. package/src/symbols/RoundedSquare.svelte +33 -0
  81. package/src/symbols/Shape.svelte +37 -0
  82. package/src/symbols/constants/index.js +4 -0
  83. package/src/symbols/index.js +9 -0
  84. package/src/symbols/outline.svelte +60 -0
  85. package/src/symbols/solid.svelte +60 -0
  86. package/src/template/Texture.svelte +13 -0
  87. package/src/template/constants.js +43 -0
  88. package/src/template/shapes/Circles.svelte +15 -0
  89. package/src/template/shapes/Lines.svelte +16 -0
  90. package/src/template/shapes/Path.svelte +9 -0
  91. package/src/template/shapes/Polygons.svelte +15 -0
  92. package/src/template/shapes/index.js +4 -0
  93. package/src/chart/FacetGrid.svelte +0 -51
  94. package/src/chart/Grid.svelte +0 -34
  95. package/src/chart/Legend.svelte +0 -16
  96. package/src/chart/PatternDefs.svelte +0 -13
  97. package/src/chart/Swatch.svelte +0 -93
  98. package/src/chart/SwatchButton.svelte +0 -29
  99. package/src/chart/SwatchGrid.svelte +0 -55
  100. package/src/chart/Symbol.svelte +0 -37
  101. package/src/chart/Texture.svelte +0 -16
  102. package/src/chart/TexturedShape.svelte +0 -27
  103. package/src/chart/TimelapseChart.svelte +0 -97
  104. package/src/chart/Timer.svelte +0 -27
  105. package/src/chart.js +0 -9
  106. package/src/components/charts/Axis.svelte +0 -66
  107. package/src/components/charts/Chart.svelte +0 -35
  108. package/src/components/index.js +0 -23
  109. package/src/components/lib/axis.js +0 -0
  110. package/src/components/lib/chart.js +0 -187
  111. package/src/components/lib/color.js +0 -327
  112. package/src/components/lib/funnel.js +0 -204
  113. package/src/components/lib/index.js +0 -19
  114. package/src/components/lib/pattern.js +0 -190
  115. package/src/components/lib/rollup.js +0 -55
  116. package/src/components/lib/shape.js +0 -199
  117. package/src/components/lib/summary.js +0 -145
  118. package/src/components/lib/theme.js +0 -23
  119. package/src/components/lib/timer.js +0 -41
  120. package/src/components/lib/utils.js +0 -165
  121. package/src/components/plots/BarPlot.svelte +0 -36
  122. package/src/components/plots/BoxPlot.svelte +0 -54
  123. package/src/components/plots/ScatterPlot.svelte +0 -30
  124. package/src/components/store.js +0 -70
  125. package/src/constants.js +0 -66
  126. package/src/elements/PatternDefs.svelte +0 -13
  127. package/src/elements/PatternMask.svelte +0 -20
  128. package/src/elements/Symbol.svelte +0 -38
  129. package/src/elements/Tooltip.svelte +0 -23
  130. package/src/funnel.svelte +0 -35
  131. package/src/geom.js +0 -105
  132. package/src/lib/axis.js +0 -75
  133. package/src/lib/colors.js +0 -32
  134. package/src/lib/geom.js +0 -4
  135. package/src/lib/shapes.js +0 -144
  136. package/src/lib/timer.js +0 -44
  137. package/src/lookup.js +0 -29
  138. package/src/plots/BarPlot.svelte +0 -55
  139. package/src/plots/BoxPlot.svelte +0 -0
  140. package/src/plots/FunnelPlot.svelte +0 -33
  141. package/src/plots/HeatMap.svelte +0 -5
  142. package/src/plots/HeatMapCalendar.svelte +0 -129
  143. package/src/plots/LinePlot.svelte +0 -55
  144. package/src/plots/Plot.svelte +0 -25
  145. package/src/plots/RankBarPlot.svelte +0 -38
  146. package/src/plots/ScatterPlot.svelte +0 -20
  147. package/src/plots/ViolinPlot.svelte +0 -11
  148. package/src/plots/heatmap.js +0 -70
  149. package/src/plots/index.js +0 -10
  150. 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,53 @@
1
1
  {
2
2
  "name": "@rokkit/chart",
3
- "version": "1.0.0-next.14",
4
- "description": "Components for making interactive charts.",
3
+ "version": "1.0.0-next.140",
4
+ "type": "module",
5
+ "description": "Data-driven chart components",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/jerrythomas/rokkit.git"
9
+ },
5
10
  "author": "Jerry Thomas <me@jerrythomas.name>",
6
11
  "license": "MIT",
7
- "main": "src/index.js",
8
- "svelte": "src/index.js",
9
- "module": "src/index.js",
10
- "types": "dist/index.d.ts",
11
- "type": "module",
12
+ "npm": {
13
+ "publish": false
14
+ },
12
15
  "publishConfig": {
13
16
  "access": "public"
14
17
  },
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
- "shared-config": "1.0.0",
27
- "eslint-config-shared": "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.14"
18
+ "scripts": {
19
+ "prepublishOnly": "cp ../../LICENSE . && bun clean && bun tsc --project tsconfig.build.json",
20
+ "postpublish": "rm -f LICENSE",
21
+ "clean": "rm -rf dist",
22
+ "build": "bun prepublishOnly"
38
23
  },
39
24
  "files": [
40
25
  "src/**/*.js",
41
26
  "src/**/*.svelte",
42
- "!src/fixtures",
43
- "!src/mocks",
44
- "!src/**/*.spec.js"
27
+ "dist/**/*.d.ts",
28
+ "README.md",
29
+ "package.json",
30
+ "LICENSE"
45
31
  ],
46
32
  "exports": {
47
- "./src": "./src",
48
33
  "./package.json": "./package.json",
49
34
  ".": {
50
35
  "types": "./dist/index.d.ts",
51
- "import": "./src/index.js"
36
+ "import": "./src/index.js",
37
+ "svelte": "./src/index.js"
52
38
  }
53
39
  },
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"
40
+ "dependencies": {
41
+ "@rokkit/core": "workspace:latest",
42
+ "@rokkit/data": "workspace:latest",
43
+ "@rokkit/states": "workspace:latest",
44
+ "d3-array": "^3.2.4",
45
+ "d3-format": "^3.1.2",
46
+ "d3-axis": "^3.0.0",
47
+ "d3-scale": "^4.0.2",
48
+ "d3-scale-chromatic": "^3.1.0",
49
+ "d3-selection": "^3.0.0",
50
+ "d3-transition": "^3.0.1",
51
+ "ramda": "^0.32.0"
62
52
  }
63
- }
53
+ }
@@ -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>