@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.
- package/README.md +94 -0
- package/dist/Chart.svelte +132 -0
- package/dist/Chart.svelte.d.ts +20 -0
- package/dist/Chart.svelte.d.ts.map +1 -0
- package/dist/DataTable.svelte +132 -0
- package/dist/DataTable.svelte.d.ts +19 -0
- package/dist/DataTable.svelte.d.ts.map +1 -0
- package/dist/Graph.svelte +119 -0
- package/dist/Graph.svelte.d.ts +22 -0
- package/dist/Graph.svelte.d.ts.map +1 -0
- package/dist/ThemeProvider.svelte +27 -0
- package/dist/ThemeProvider.svelte.d.ts +11 -0
- package/dist/ThemeProvider.svelte.d.ts.map +1 -0
- package/dist/Visualization.svelte +35 -0
- package/dist/Visualization.svelte.d.ts +12 -0
- package/dist/Visualization.svelte.d.ts.map +1 -0
- package/dist/__tests__/Chart.test.js +108 -0
- package/dist/__tests__/DataTable.test.js +101 -0
- package/dist/__tests__/Graph.test.js +104 -0
- package/dist/__tests__/ThemeContext.test.js +89 -0
- package/dist/__tests__/composables.test.js +84 -0
- package/dist/__tests__/useTableState.test.js +105 -0
- package/dist/composables/useChart.svelte.d.ts +41 -0
- package/dist/composables/useChart.svelte.d.ts.map +1 -0
- package/dist/composables/useChart.svelte.js +73 -0
- package/dist/composables/useDarkMode.svelte.d.ts +15 -0
- package/dist/composables/useDarkMode.svelte.d.ts.map +1 -0
- package/dist/composables/useDarkMode.svelte.js +47 -0
- package/dist/composables/useGraph.svelte.d.ts +53 -0
- package/dist/composables/useGraph.svelte.d.ts.map +1 -0
- package/dist/composables/useGraph.svelte.js +67 -0
- package/dist/composables/useTable.svelte.d.ts +30 -0
- package/dist/composables/useTable.svelte.d.ts.map +1 -0
- package/dist/composables/useTable.svelte.js +59 -0
- package/dist/composables/useTableState.svelte.d.ts +45 -0
- package/dist/composables/useTableState.svelte.d.ts.map +1 -0
- package/dist/composables/useTableState.svelte.js +58 -0
- package/dist/context.d.ts +21 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +28 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/package.json +65 -0
- package/src/Chart.svelte +132 -0
- package/src/DataTable.svelte +132 -0
- package/src/Graph.svelte +119 -0
- package/src/ThemeProvider.svelte +27 -0
- package/src/Visualization.svelte +35 -0
- package/src/__tests__/Chart.test.ts +134 -0
- package/src/__tests__/DataTable.test.ts +126 -0
- package/src/__tests__/Graph.test.ts +130 -0
- package/src/__tests__/ThemeContext.test.ts +106 -0
- package/src/__tests__/composables.test.ts +101 -0
- package/src/__tests__/useTableState.test.ts +134 -0
- package/src/composables/useChart.svelte.ts +108 -0
- package/src/composables/useDarkMode.svelte.ts +51 -0
- package/src/composables/useGraph.svelte.ts +115 -0
- package/src/composables/useTable.svelte.ts +85 -0
- package/src/composables/useTableState.svelte.ts +78 -0
- package/src/context.ts +35 -0
- package/src/index.ts +46 -0
- 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"}
|