@opendata-ai/openchart-svelte 2.0.0

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 (66) hide show
  1. package/README.md +94 -0
  2. package/dist/Chart.svelte +132 -0
  3. package/dist/Chart.svelte.d.ts +20 -0
  4. package/dist/Chart.svelte.d.ts.map +1 -0
  5. package/dist/DataTable.svelte +132 -0
  6. package/dist/DataTable.svelte.d.ts +19 -0
  7. package/dist/DataTable.svelte.d.ts.map +1 -0
  8. package/dist/Graph.svelte +119 -0
  9. package/dist/Graph.svelte.d.ts +22 -0
  10. package/dist/Graph.svelte.d.ts.map +1 -0
  11. package/dist/ThemeProvider.svelte +27 -0
  12. package/dist/ThemeProvider.svelte.d.ts +11 -0
  13. package/dist/ThemeProvider.svelte.d.ts.map +1 -0
  14. package/dist/Visualization.svelte +35 -0
  15. package/dist/Visualization.svelte.d.ts +12 -0
  16. package/dist/Visualization.svelte.d.ts.map +1 -0
  17. package/dist/__tests__/Chart.test.js +108 -0
  18. package/dist/__tests__/DataTable.test.js +101 -0
  19. package/dist/__tests__/Graph.test.js +104 -0
  20. package/dist/__tests__/ThemeContext.test.js +89 -0
  21. package/dist/__tests__/composables.test.js +84 -0
  22. package/dist/__tests__/useTableState.test.js +105 -0
  23. package/dist/composables/useChart.svelte.d.ts +41 -0
  24. package/dist/composables/useChart.svelte.d.ts.map +1 -0
  25. package/dist/composables/useChart.svelte.js +73 -0
  26. package/dist/composables/useDarkMode.svelte.d.ts +15 -0
  27. package/dist/composables/useDarkMode.svelte.d.ts.map +1 -0
  28. package/dist/composables/useDarkMode.svelte.js +47 -0
  29. package/dist/composables/useGraph.svelte.d.ts +53 -0
  30. package/dist/composables/useGraph.svelte.d.ts.map +1 -0
  31. package/dist/composables/useGraph.svelte.js +67 -0
  32. package/dist/composables/useTable.svelte.d.ts +30 -0
  33. package/dist/composables/useTable.svelte.d.ts.map +1 -0
  34. package/dist/composables/useTable.svelte.js +59 -0
  35. package/dist/composables/useTableState.svelte.d.ts +45 -0
  36. package/dist/composables/useTableState.svelte.d.ts.map +1 -0
  37. package/dist/composables/useTableState.svelte.js +58 -0
  38. package/dist/context.d.ts +21 -0
  39. package/dist/context.d.ts.map +1 -0
  40. package/dist/context.js +28 -0
  41. package/dist/index.d.ts +25 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +21 -0
  44. package/dist/types.d.ts +60 -0
  45. package/dist/types.d.ts.map +1 -0
  46. package/dist/types.js +7 -0
  47. package/package.json +65 -0
  48. package/src/Chart.svelte +132 -0
  49. package/src/DataTable.svelte +132 -0
  50. package/src/Graph.svelte +119 -0
  51. package/src/ThemeProvider.svelte +27 -0
  52. package/src/Visualization.svelte +35 -0
  53. package/src/__tests__/Chart.test.ts +134 -0
  54. package/src/__tests__/DataTable.test.ts +126 -0
  55. package/src/__tests__/Graph.test.ts +130 -0
  56. package/src/__tests__/ThemeContext.test.ts +106 -0
  57. package/src/__tests__/composables.test.ts +101 -0
  58. package/src/__tests__/useTableState.test.ts +134 -0
  59. package/src/composables/useChart.svelte.ts +108 -0
  60. package/src/composables/useDarkMode.svelte.ts +51 -0
  61. package/src/composables/useGraph.svelte.ts +115 -0
  62. package/src/composables/useTable.svelte.ts +85 -0
  63. package/src/composables/useTableState.svelte.ts +78 -0
  64. package/src/context.ts +35 -0
  65. package/src/index.ts +46 -0
  66. package/src/types.ts +78 -0
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # @opendata-ai/openchart-svelte
2
+
3
+ Svelte 5 components for OpenChart. Renders chart specs as SVG and table specs as DOM, using Svelte's rune-based reactivity.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @opendata-ai/openchart-svelte @opendata-ai/openchart-core
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```svelte
14
+ <script lang="ts">
15
+ import { Chart } from '@opendata-ai/openchart-svelte';
16
+ import { lineChart } from '@opendata-ai/openchart-core';
17
+
18
+ const data = [
19
+ { date: '2024-01', value: 100 },
20
+ { date: '2024-02', value: 150 },
21
+ { date: '2024-03', value: 130 },
22
+ ];
23
+
24
+ const spec = lineChart(data, 'date', 'value');
25
+ </script>
26
+
27
+ <Chart {spec} />
28
+ ```
29
+
30
+ See the [core README](../core/README.md) for all available spec builders.
31
+
32
+ ## Components
33
+
34
+ | Component | Purpose |
35
+ |-----------|---------|
36
+ | `Chart` | Renders any chart spec (line, bar, column, pie, scatter, etc.) |
37
+ | `DataTable` | Renders table specs with sorting, search, and pagination |
38
+ | `Graph` | Renders network graph specs with force-directed layout |
39
+ | `Visualization` | Routes to the correct component based on spec type |
40
+ | `VizThemeProvider` | Provides theme and dark mode context to child components |
41
+
42
+ ## Visualization
43
+
44
+ When you're rendering arbitrary `VizSpec` values and don't know the type ahead of time, `Visualization` inspects the spec and routes to the correct component.
45
+
46
+ ```svelte
47
+ <script lang="ts">
48
+ import { Visualization } from '@opendata-ai/openchart-svelte';
49
+ import type { VizSpec } from '@opendata-ai/openchart-core';
50
+
51
+ let { spec }: { spec: VizSpec } = $props();
52
+ </script>
53
+
54
+ <!-- Renders Chart, DataTable, or Graph based on spec.type -->
55
+ <Visualization {spec} />
56
+ ```
57
+
58
+ If you need event handlers or component-specific props, use the specific component directly instead.
59
+
60
+ ## Dark mode and theming
61
+
62
+ Wrap components with `VizThemeProvider` to set theme and dark mode for all child visualizations. It uses Svelte's context API, so all `Chart`, `DataTable`, and `Graph` components inside the provider inherit its values.
63
+
64
+ ```svelte
65
+ <script lang="ts">
66
+ import { VizThemeProvider, Chart } from '@opendata-ai/openchart-svelte';
67
+ </script>
68
+
69
+ <VizThemeProvider theme={myTheme} darkMode="auto">
70
+ <Chart spec={spec1} />
71
+ <Chart spec={spec2} />
72
+ </VizThemeProvider>
73
+ ```
74
+
75
+ `darkMode` accepts `"auto"` (follows system preference), `"force"` (always dark), or `"off"` (always light).
76
+
77
+ For one-off overrides, pass `darkMode` or `theme` directly on an individual component. Component-level props take priority over the provider.
78
+
79
+ ## Composables
80
+
81
+ For lower-level control or custom rendering:
82
+
83
+ - `useChart(spec, options?)` - Returns `{ action, chart, layout }`. Use with `use:action` directive
84
+ - `useGraph(spec, options?)` - Returns `{ action, search, zoomToFit, ... }`. Use with `use:action`
85
+ - `useTable(spec, options?)` - Returns `{ action, table, layout }`. Use with `use:action` directive
86
+ - `useTableState(options?)` - Manages table sorting, pagination, and search state
87
+ - `useDarkMode(preference?)` - Resolves dark mode preference against system settings
88
+
89
+ Context helpers for reading provider values directly:
90
+
91
+ - `getVizTheme()` - Returns the theme from the nearest `VizThemeProvider`
92
+ - `getVizDarkMode()` - Returns the dark mode preference from the nearest provider
93
+ - `setVizTheme(getter)` - Sets theme context (used internally by `VizThemeProvider`)
94
+ - `setVizDarkMode(getter)` - Sets dark mode context (used internally by `VizThemeProvider`)
@@ -0,0 +1,132 @@
1
+ <!--
2
+ Chart component: Svelte 5 wrapper around the vanilla adapter.
3
+
4
+ Mounts a chart instance on render, updates when spec/options change,
5
+ and cleans up on unmount. All heavy lifting is done by the vanilla
6
+ createChart() function.
7
+ -->
8
+ <script lang="ts">
9
+ import type {
10
+ Annotation,
11
+ AnnotationOffset,
12
+ ChartSpec,
13
+ DarkMode,
14
+ ElementEdit,
15
+ GraphSpec,
16
+ MarkEvent,
17
+ TextAnnotation,
18
+ ThemeConfig,
19
+ } from '@opendata-ai/openchart-core';
20
+ import { type ChartInstance, createChart, type MountOptions } from '@opendata-ai/openchart-vanilla';
21
+ import { onMount, untrack } from 'svelte';
22
+ import { getVizDarkMode, getVizTheme } from './context.js';
23
+
24
+ let {
25
+ spec,
26
+ theme,
27
+ darkMode,
28
+ onmarkclick,
29
+ onmarkhover,
30
+ onmarkleave,
31
+ onlegendtoggle,
32
+ onannotationclick,
33
+ onannotationedit,
34
+ onedit,
35
+ ondatapointclick,
36
+ class: className,
37
+ style,
38
+ }: {
39
+ spec: ChartSpec | GraphSpec;
40
+ theme?: ThemeConfig;
41
+ darkMode?: DarkMode;
42
+ onmarkclick?: (event: MarkEvent) => void;
43
+ onmarkhover?: (event: MarkEvent) => void;
44
+ onmarkleave?: () => void;
45
+ onlegendtoggle?: (series: string, visible: boolean) => void;
46
+ onannotationclick?: (annotation: Annotation, event: MouseEvent) => void;
47
+ onannotationedit?: (annotation: TextAnnotation, offset: AnnotationOffset) => void;
48
+ onedit?: (edit: ElementEdit) => void;
49
+ ondatapointclick?: (data: Record<string, unknown>) => void;
50
+ class?: string;
51
+ style?: string;
52
+ } = $props();
53
+
54
+ let containerEl: HTMLDivElement;
55
+ let instance: ChartInstance | null = null;
56
+
57
+ const ctxTheme = getVizTheme();
58
+ const ctxDarkMode = getVizDarkMode();
59
+
60
+ onMount(() => {
61
+ return () => {
62
+ instance?.destroy();
63
+ instance = null;
64
+ };
65
+ });
66
+
67
+ // Stable callback wrappers that read current handler props without
68
+ // creating reactive dependencies. This prevents callback prop changes
69
+ // from triggering a full chart destroy/recreate cycle.
70
+ const stableHandlers: MountOptions = {
71
+ onMarkClick: (event: MarkEvent) => untrack(() => onmarkclick)?.(event),
72
+ onMarkHover: (event: MarkEvent) => untrack(() => onmarkhover)?.(event),
73
+ onMarkLeave: () => untrack(() => onmarkleave)?.(),
74
+ onLegendToggle: (series: string, visible: boolean) =>
75
+ untrack(() => onlegendtoggle)?.(series, visible),
76
+ onAnnotationClick: (annotation: Annotation, event: MouseEvent) =>
77
+ untrack(() => onannotationclick)?.(annotation, event),
78
+ onDataPointClick: (data: Record<string, unknown>) => untrack(() => ondatapointclick)?.(data),
79
+ };
80
+
81
+ // Editing callbacks - only defined as stable wrappers, but only
82
+ // included in options when the consumer provides the prop.
83
+ const stableOnAnnotationEdit = (annotation: TextAnnotation, offset: AnnotationOffset) =>
84
+ untrack(() => onannotationedit)?.(annotation, offset);
85
+ const stableOnEdit = (edit: ElementEdit) => untrack(() => onedit)?.(edit);
86
+
87
+ let prevSpec = '';
88
+
89
+ // Effect 1: Mount/recreate chart on theme/darkMode changes.
90
+ // Reads spec via untrack() so spec changes don't trigger full recreate.
91
+ $effect(() => {
92
+ const resolvedTheme = theme ?? ctxTheme?.();
93
+ const resolvedDarkMode = darkMode ?? ctxDarkMode?.();
94
+ // Read spec without tracking - spec changes handled in Effect 2
95
+ const currentSpec = untrack(() => spec);
96
+
97
+ instance?.destroy();
98
+
99
+ const hasAnnotationEdit = untrack(() => onannotationedit) !== undefined;
100
+ const hasEdit = untrack(() => onedit) !== undefined;
101
+
102
+ const options: MountOptions = {
103
+ theme: resolvedTheme,
104
+ darkMode: resolvedDarkMode,
105
+ responsive: true,
106
+ ...stableHandlers,
107
+ ...(hasAnnotationEdit ? { onAnnotationEdit: stableOnAnnotationEdit } : {}),
108
+ ...(hasEdit ? { onEdit: stableOnEdit } : {}),
109
+ };
110
+
111
+ instance = createChart(containerEl, currentSpec, options);
112
+ prevSpec = JSON.stringify(currentSpec);
113
+ });
114
+
115
+ // Effect 2: Update chart when spec changes (no destroy/recreate).
116
+ $effect(() => {
117
+ const currentSpec = spec;
118
+ if (!instance) return;
119
+
120
+ const specString = JSON.stringify(currentSpec);
121
+ if (specString !== prevSpec) {
122
+ prevSpec = specString;
123
+ instance.update(currentSpec);
124
+ }
125
+ });
126
+ </script>
127
+
128
+ <div
129
+ bind:this={containerEl}
130
+ class={className ? `viz-chart-root ${className}` : 'viz-chart-root'}
131
+ {style}
132
+ ></div>
@@ -0,0 +1,20 @@
1
+ import type { Annotation, AnnotationOffset, ChartSpec, DarkMode, ElementEdit, GraphSpec, MarkEvent, TextAnnotation, ThemeConfig } from '@opendata-ai/openchart-core';
2
+ type $$ComponentProps = {
3
+ spec: ChartSpec | GraphSpec;
4
+ theme?: ThemeConfig;
5
+ darkMode?: DarkMode;
6
+ onmarkclick?: (event: MarkEvent) => void;
7
+ onmarkhover?: (event: MarkEvent) => void;
8
+ onmarkleave?: () => void;
9
+ onlegendtoggle?: (series: string, visible: boolean) => void;
10
+ onannotationclick?: (annotation: Annotation, event: MouseEvent) => void;
11
+ onannotationedit?: (annotation: TextAnnotation, offset: AnnotationOffset) => void;
12
+ onedit?: (edit: ElementEdit) => void;
13
+ ondatapointclick?: (data: Record<string, unknown>) => void;
14
+ class?: string;
15
+ style?: string;
16
+ };
17
+ declare const Chart: import("svelte").Component<$$ComponentProps, {}, "">;
18
+ type Chart = ReturnType<typeof Chart>;
19
+ export default Chart;
20
+ //# sourceMappingURL=Chart.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Chart.svelte.d.ts","sourceRoot":"","sources":["../src/Chart.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,QAAQ,EACR,WAAW,EACX,SAAS,EACT,SAAS,EACT,cAAc,EACd,WAAW,EACZ,MAAM,6BAA6B,CAAC;AAKpC,KAAK,gBAAgB,GAAI;IACxB,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5D,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACxE,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClF,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACrC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAsGF,QAAA,MAAM,KAAK,sDAAwC,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
@@ -0,0 +1,132 @@
1
+ <!--
2
+ DataTable component: Svelte 5 wrapper around the vanilla table adapter.
3
+
4
+ Mounts a table instance on render, updates when spec changes,
5
+ and cleans up on unmount. Supports both controlled and uncontrolled modes
6
+ for sort, search, and pagination state.
7
+ -->
8
+ <script lang="ts">
9
+ import type { DarkMode, SortState, TableSpec, ThemeConfig } from '@opendata-ai/openchart-core';
10
+ import {
11
+ createTable,
12
+ type TableInstance,
13
+ type TableMountOptions,
14
+ } from '@opendata-ai/openchart-vanilla';
15
+ import { onMount, untrack } from 'svelte';
16
+ import { getVizDarkMode, getVizTheme } from './context.js';
17
+
18
+ let {
19
+ spec,
20
+ theme,
21
+ darkMode,
22
+ onrowclick,
23
+ onsortchange,
24
+ onsearchchange,
25
+ onpagechange,
26
+ sort,
27
+ search,
28
+ page,
29
+ class: className,
30
+ style,
31
+ }: {
32
+ spec: TableSpec;
33
+ theme?: ThemeConfig;
34
+ darkMode?: DarkMode;
35
+ onrowclick?: (row: Record<string, unknown>) => void;
36
+ onsortchange?: (sort: SortState | null) => void;
37
+ onsearchchange?: (query: string) => void;
38
+ onpagechange?: (page: number) => void;
39
+ sort?: SortState | null;
40
+ search?: string;
41
+ page?: number;
42
+ class?: string;
43
+ style?: string;
44
+ } = $props();
45
+
46
+ let containerEl: HTMLDivElement;
47
+ let instance: TableInstance | null = null;
48
+
49
+ const ctxTheme = getVizTheme();
50
+ const ctxDarkMode = getVizDarkMode();
51
+
52
+ const isControlled = $derived(sort !== undefined || search !== undefined || page !== undefined);
53
+
54
+ onMount(() => {
55
+ return () => {
56
+ instance?.destroy();
57
+ instance = null;
58
+ };
59
+ });
60
+
61
+ let prevSpec = '';
62
+
63
+ // Effect 1: Mount/recreate table on theme/darkMode changes.
64
+ $effect(() => {
65
+ const resolvedTheme = theme ?? ctxTheme?.();
66
+ const resolvedDarkMode = darkMode ?? ctxDarkMode?.();
67
+ // Read spec and controlled state without tracking
68
+ const currentSpec = untrack(() => spec);
69
+ const currentIsControlled = untrack(() => isControlled);
70
+ const currentSort = untrack(() => sort);
71
+ const currentSearch = untrack(() => search);
72
+ const currentPage = untrack(() => page);
73
+
74
+ instance?.destroy();
75
+
76
+ const mountOptions: TableMountOptions = {
77
+ theme: resolvedTheme,
78
+ darkMode: resolvedDarkMode,
79
+ onRowClick: (row: Record<string, unknown>) => untrack(() => onrowclick)?.(row),
80
+ responsive: true,
81
+ onStateChange: (state) => {
82
+ if (state.sort !== undefined) untrack(() => onsortchange)?.(state.sort);
83
+ if (state.search !== undefined) untrack(() => onsearchchange)?.(state.search);
84
+ if (state.page !== undefined) untrack(() => onpagechange)?.(state.page);
85
+ },
86
+ };
87
+
88
+ if (currentIsControlled) {
89
+ mountOptions.externalState = {
90
+ sort: currentSort ?? null,
91
+ search: currentSearch ?? '',
92
+ page: currentPage ?? 0,
93
+ };
94
+ }
95
+
96
+ instance = createTable(containerEl, currentSpec, mountOptions);
97
+ prevSpec = JSON.stringify(currentSpec);
98
+ });
99
+
100
+ // Effect 2: Update table when spec changes (no destroy/recreate).
101
+ $effect(() => {
102
+ const currentSpec = spec;
103
+ if (!instance) return;
104
+
105
+ const specString = JSON.stringify(currentSpec);
106
+ if (specString !== prevSpec) {
107
+ prevSpec = specString;
108
+ instance.update(currentSpec);
109
+ }
110
+ });
111
+
112
+ // Effect 3: Sync controlled state without remounting.
113
+ $effect(() => {
114
+ const currentIsControlled = isControlled;
115
+ const currentSort = sort;
116
+ const currentSearch = search;
117
+ const currentPage = page;
118
+ if (!instance || !currentIsControlled) return;
119
+
120
+ instance.setState({
121
+ sort: currentSort ?? null,
122
+ search: currentSearch ?? '',
123
+ page: currentPage ?? 0,
124
+ });
125
+ });
126
+ </script>
127
+
128
+ <div
129
+ bind:this={containerEl}
130
+ class={className ? `viz-table-root ${className}` : 'viz-table-root'}
131
+ {style}
132
+ ></div>
@@ -0,0 +1,19 @@
1
+ import type { DarkMode, SortState, TableSpec, ThemeConfig } from '@opendata-ai/openchart-core';
2
+ type $$ComponentProps = {
3
+ spec: TableSpec;
4
+ theme?: ThemeConfig;
5
+ darkMode?: DarkMode;
6
+ onrowclick?: (row: Record<string, unknown>) => void;
7
+ onsortchange?: (sort: SortState | null) => void;
8
+ onsearchchange?: (query: string) => void;
9
+ onpagechange?: (page: number) => void;
10
+ sort?: SortState | null;
11
+ search?: string;
12
+ page?: number;
13
+ class?: string;
14
+ style?: string;
15
+ };
16
+ declare const DataTable: import("svelte").Component<$$ComponentProps, {}, "">;
17
+ type DataTable = ReturnType<typeof DataTable>;
18
+ export default DataTable;
19
+ //# sourceMappingURL=DataTable.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataTable.svelte.d.ts","sourceRoot":"","sources":["../src/DataTable.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAS9F,KAAK,gBAAgB,GAAI;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA6GF,QAAA,MAAM,SAAS,sDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -0,0 +1,119 @@
1
+ <!--
2
+ Graph component: Svelte 5 wrapper around the vanilla adapter.
3
+
4
+ Mounts a graph instance on render, updates when spec changes,
5
+ and cleans up on unmount. All heavy lifting is done by the vanilla
6
+ createGraph() function.
7
+
8
+ Exposes imperative methods via component exports for programmatic
9
+ control (search, zoom, select).
10
+ -->
11
+ <script lang="ts">
12
+ import type { DarkMode, GraphSpec, ThemeConfig } from '@opendata-ai/openchart-core';
13
+ import {
14
+ createGraph,
15
+ type GraphInstance,
16
+ type GraphMountOptions,
17
+ } from '@opendata-ai/openchart-vanilla';
18
+ import { onMount, untrack } from 'svelte';
19
+ import { getVizDarkMode, getVizTheme } from './context.js';
20
+
21
+ let {
22
+ spec,
23
+ theme,
24
+ darkMode,
25
+ onnodeclick,
26
+ onnodedoubleclick,
27
+ onselectionchange,
28
+ class: className,
29
+ style,
30
+ }: {
31
+ spec: GraphSpec;
32
+ theme?: ThemeConfig;
33
+ darkMode?: DarkMode;
34
+ onnodeclick?: (node: Record<string, unknown>) => void;
35
+ onnodedoubleclick?: (node: Record<string, unknown>) => void;
36
+ onselectionchange?: (nodeIds: string[]) => void;
37
+ class?: string;
38
+ style?: string;
39
+ } = $props();
40
+
41
+ let containerEl: HTMLDivElement;
42
+ let instance: GraphInstance | null = null;
43
+
44
+ const ctxTheme = getVizTheme();
45
+ const ctxDarkMode = getVizDarkMode();
46
+
47
+ onMount(() => {
48
+ return () => {
49
+ instance?.destroy();
50
+ instance = null;
51
+ };
52
+ });
53
+
54
+ let prevSpec = '';
55
+
56
+ // Effect 1: Mount/recreate graph on theme/darkMode changes.
57
+ $effect(() => {
58
+ const resolvedTheme = theme ?? ctxTheme?.();
59
+ const resolvedDarkMode = darkMode ?? ctxDarkMode?.();
60
+ const currentSpec = untrack(() => spec);
61
+
62
+ instance?.destroy();
63
+
64
+ const options: GraphMountOptions = {
65
+ theme: resolvedTheme,
66
+ darkMode: resolvedDarkMode,
67
+ onNodeClick: (node: Record<string, unknown>) => untrack(() => onnodeclick)?.(node),
68
+ onNodeDoubleClick: (node: Record<string, unknown>) => untrack(() => onnodedoubleclick)?.(node),
69
+ onSelectionChange: (nodeIds: string[]) => untrack(() => onselectionchange)?.(nodeIds),
70
+ responsive: true,
71
+ };
72
+
73
+ instance = createGraph(containerEl, currentSpec, options);
74
+ prevSpec = JSON.stringify(currentSpec);
75
+ });
76
+
77
+ // Effect 2: Update graph when spec changes (no destroy/recreate).
78
+ $effect(() => {
79
+ const currentSpec = spec;
80
+ if (!instance) return;
81
+
82
+ const specString = JSON.stringify(currentSpec);
83
+ if (specString !== prevSpec) {
84
+ prevSpec = specString;
85
+ instance.update(currentSpec);
86
+ }
87
+ });
88
+
89
+ // Imperative methods exposed via component exports
90
+ export function search(query: string): void {
91
+ instance?.search(query);
92
+ }
93
+
94
+ export function clearSearch(): void {
95
+ instance?.clearSearch();
96
+ }
97
+
98
+ export function zoomToFit(): void {
99
+ instance?.zoomToFit();
100
+ }
101
+
102
+ export function zoomToNode(nodeId: string): void {
103
+ instance?.zoomToNode(nodeId);
104
+ }
105
+
106
+ export function selectNode(nodeId: string): void {
107
+ instance?.selectNode(nodeId);
108
+ }
109
+
110
+ export function getSelectedNodes(): string[] {
111
+ return instance?.getSelectedNodes() ?? [];
112
+ }
113
+ </script>
114
+
115
+ <div
116
+ bind:this={containerEl}
117
+ class={className ? `viz-graph-root ${className}` : 'viz-graph-root'}
118
+ {style}
119
+ ></div>
@@ -0,0 +1,22 @@
1
+ import type { DarkMode, GraphSpec, ThemeConfig } from '@opendata-ai/openchart-core';
2
+ type $$ComponentProps = {
3
+ spec: GraphSpec;
4
+ theme?: ThemeConfig;
5
+ darkMode?: DarkMode;
6
+ onnodeclick?: (node: Record<string, unknown>) => void;
7
+ onnodedoubleclick?: (node: Record<string, unknown>) => void;
8
+ onselectionchange?: (nodeIds: string[]) => void;
9
+ class?: string;
10
+ style?: string;
11
+ };
12
+ declare const Graph: import("svelte").Component<$$ComponentProps, {
13
+ search: (query: string) => void;
14
+ clearSearch: () => void;
15
+ zoomToFit: () => void;
16
+ zoomToNode: (nodeId: string) => void;
17
+ selectNode: (nodeId: string) => void;
18
+ getSelectedNodes: () => string[];
19
+ }, "">;
20
+ type Graph = ReturnType<typeof Graph>;
21
+ export default Graph;
22
+ //# sourceMappingURL=Graph.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Graph.svelte.d.ts","sourceRoot":"","sources":["../src/Graph.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AASnF,KAAK,gBAAgB,GAAI;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACtD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC5D,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAiGF,QAAA,MAAM,KAAK;oBA9Ba,MAAM,KAAG,IAAI;uBAIZ,IAAI;qBAIN,IAAI;yBAIE,MAAM,KAAG,IAAI;yBAIb,MAAM,KAAG,IAAI;4BAIZ,MAAM,EAAE;MAUa,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
@@ -0,0 +1,27 @@
1
+ <!--
2
+ VizThemeProvider: provides a theme and dark mode preference to all
3
+ descendant Chart, DataTable, and Graph components.
4
+
5
+ Components use the context values as fallbacks when no explicit
6
+ `theme` or `darkMode` prop is passed.
7
+ -->
8
+ <script lang="ts">
9
+ import type { DarkMode, ThemeConfig } from '@opendata-ai/openchart-core';
10
+ import type { Snippet } from 'svelte';
11
+ import { setVizDarkMode, setVizTheme } from './context.js';
12
+
13
+ let {
14
+ theme,
15
+ darkMode,
16
+ children,
17
+ }: {
18
+ theme: ThemeConfig | undefined;
19
+ darkMode?: DarkMode;
20
+ children: Snippet;
21
+ } = $props();
22
+
23
+ setVizTheme(() => theme);
24
+ setVizDarkMode(() => darkMode);
25
+ </script>
26
+
27
+ {@render children()}
@@ -0,0 +1,11 @@
1
+ import type { DarkMode, ThemeConfig } from '@opendata-ai/openchart-core';
2
+ import type { Snippet } from 'svelte';
3
+ type $$ComponentProps = {
4
+ theme: ThemeConfig | undefined;
5
+ darkMode?: DarkMode;
6
+ children: Snippet;
7
+ };
8
+ declare const ThemeProvider: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type ThemeProvider = ReturnType<typeof ThemeProvider>;
10
+ export default ThemeProvider;
11
+ //# sourceMappingURL=ThemeProvider.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeProvider.svelte.d.ts","sourceRoot":"","sources":["../src/ThemeProvider.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGrC,KAAK,gBAAgB,GAAI;IACxB,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;IAC/B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAqBF,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,35 @@
1
+ <!--
2
+ Visualization routing component: renders Chart, DataTable, or Graph
3
+ based on the spec type. Use this when rendering arbitrary VizSpec values.
4
+
5
+ For event handlers, use the specific component (Chart, DataTable, Graph) directly.
6
+ -->
7
+ <script lang="ts">
8
+ import type { DarkMode, ThemeConfig, VizSpec } from '@opendata-ai/openchart-core';
9
+ import { isGraphSpec, isTableSpec } from '@opendata-ai/openchart-core';
10
+ import Chart from './Chart.svelte';
11
+ import DataTable from './DataTable.svelte';
12
+ import Graph from './Graph.svelte';
13
+
14
+ let {
15
+ spec,
16
+ theme,
17
+ darkMode,
18
+ class: className,
19
+ style,
20
+ }: {
21
+ spec: VizSpec;
22
+ theme?: ThemeConfig;
23
+ darkMode?: DarkMode;
24
+ class?: string;
25
+ style?: string;
26
+ } = $props();
27
+ </script>
28
+
29
+ {#if isTableSpec(spec)}
30
+ <DataTable {spec} {theme} {darkMode} class={className} {style} />
31
+ {:else if isGraphSpec(spec)}
32
+ <Graph {spec} {theme} {darkMode} class={className} {style} />
33
+ {:else}
34
+ <Chart {spec} {theme} {darkMode} class={className} {style} />
35
+ {/if}
@@ -0,0 +1,12 @@
1
+ import type { DarkMode, ThemeConfig, VizSpec } from '@opendata-ai/openchart-core';
2
+ type $$ComponentProps = {
3
+ spec: VizSpec;
4
+ theme?: ThemeConfig;
5
+ darkMode?: DarkMode;
6
+ class?: string;
7
+ style?: string;
8
+ };
9
+ declare const Visualization: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type Visualization = ReturnType<typeof Visualization>;
11
+ export default Visualization;
12
+ //# sourceMappingURL=Visualization.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Visualization.svelte.d.ts","sourceRoot":"","sources":["../src/Visualization.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAMjF,KAAK,gBAAgB,GAAI;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4BF,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}