@opendata-ai/openchart-vue 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/dist/index.js ADDED
@@ -0,0 +1,715 @@
1
+ // src/Chart.ts
2
+ import { createChart } from "@opendata-ai/openchart-vanilla";
3
+ import {
4
+ defineComponent,
5
+ getCurrentInstance,
6
+ h,
7
+ inject as inject2,
8
+ onMounted,
9
+ onUnmounted,
10
+ ref,
11
+ watch
12
+ } from "vue";
13
+
14
+ // src/context.ts
15
+ import { computed, inject } from "vue";
16
+ var VizThemeKey = /* @__PURE__ */ Symbol("VizTheme");
17
+ var VizDarkModeKey = /* @__PURE__ */ Symbol("VizDarkMode");
18
+ function useVizTheme() {
19
+ const theme = inject(VizThemeKey, void 0);
20
+ return computed(() => theme?.value);
21
+ }
22
+ function useVizDarkMode() {
23
+ const darkMode = inject(VizDarkModeKey, void 0);
24
+ return computed(() => darkMode?.value);
25
+ }
26
+
27
+ // src/Chart.ts
28
+ var Chart = defineComponent({
29
+ name: "Chart",
30
+ props: {
31
+ spec: {
32
+ type: Object,
33
+ required: true
34
+ },
35
+ theme: {
36
+ type: Object,
37
+ default: void 0
38
+ },
39
+ darkMode: {
40
+ type: String,
41
+ default: void 0
42
+ },
43
+ class: {
44
+ type: String,
45
+ default: void 0
46
+ },
47
+ style: {
48
+ type: [String, Object],
49
+ default: void 0
50
+ }
51
+ },
52
+ emits: {
53
+ "mark-click": (_event) => true,
54
+ "mark-hover": (_event) => true,
55
+ "mark-leave": () => true,
56
+ "legend-toggle": (_series, _visible) => true,
57
+ "annotation-click": (_annotation, _event) => true,
58
+ "annotation-edit": (_annotation, _updatedOffset) => true,
59
+ edit: (_edit) => true,
60
+ "data-point-click": (_data) => true
61
+ },
62
+ setup(props, { emit }) {
63
+ const containerRef = ref(null);
64
+ let instance = null;
65
+ let prevSpec = "";
66
+ const contextTheme = inject2(VizThemeKey, void 0);
67
+ const contextDarkMode = inject2(VizDarkModeKey, void 0);
68
+ const vm = getCurrentInstance();
69
+ const vnodeProps = vm?.vnode.props ?? {};
70
+ const hasAnnotationEditListener = "onAnnotation-edit" in vnodeProps || "onAnnotationEdit" in vnodeProps;
71
+ const hasEditListener = "onEdit" in vnodeProps;
72
+ function resolveTheme() {
73
+ return props.theme ?? contextTheme?.value;
74
+ }
75
+ function resolveDarkMode() {
76
+ return props.darkMode ?? contextDarkMode?.value;
77
+ }
78
+ function mountChart() {
79
+ const container = containerRef.value;
80
+ if (!container) return;
81
+ const options = {
82
+ theme: resolveTheme(),
83
+ darkMode: resolveDarkMode(),
84
+ onDataPointClick: (data) => emit("data-point-click", data),
85
+ onMarkClick: (event) => emit("mark-click", event),
86
+ onMarkHover: (event) => emit("mark-hover", event),
87
+ onMarkLeave: () => emit("mark-leave"),
88
+ onLegendToggle: (series, visible) => emit("legend-toggle", series, visible),
89
+ onAnnotationClick: (annotation, event) => emit("annotation-click", annotation, event),
90
+ // Only include editing callbacks when the parent binds a listener.
91
+ // Without this gate, every chart gets drag editing wired up.
92
+ ...hasAnnotationEditListener ? {
93
+ onAnnotationEdit: (annotation, updatedOffset) => emit("annotation-edit", annotation, updatedOffset)
94
+ } : {},
95
+ ...hasEditListener ? { onEdit: (edit) => emit("edit", edit) } : {},
96
+ responsive: true
97
+ };
98
+ instance = createChart(container, props.spec, options);
99
+ prevSpec = JSON.stringify(props.spec);
100
+ }
101
+ function destroyChart() {
102
+ instance?.destroy();
103
+ instance = null;
104
+ prevSpec = "";
105
+ }
106
+ onMounted(() => {
107
+ mountChart();
108
+ });
109
+ onUnmounted(() => {
110
+ destroyChart();
111
+ });
112
+ watch(
113
+ () => JSON.stringify(props.spec),
114
+ (newVal) => {
115
+ if (!instance) return;
116
+ if (newVal !== prevSpec) {
117
+ prevSpec = newVal;
118
+ instance.update(props.spec);
119
+ }
120
+ }
121
+ );
122
+ watch(
123
+ [
124
+ () => props.theme,
125
+ () => props.darkMode,
126
+ () => contextTheme?.value,
127
+ () => contextDarkMode?.value
128
+ ],
129
+ () => {
130
+ if (!containerRef.value) return;
131
+ destroyChart();
132
+ mountChart();
133
+ }
134
+ );
135
+ const rootClass = () => {
136
+ const base = "viz-chart-root";
137
+ return props.class ? `${base} ${props.class}` : base;
138
+ };
139
+ return () => h("div", {
140
+ ref: containerRef,
141
+ class: rootClass(),
142
+ style: props.style
143
+ });
144
+ }
145
+ });
146
+
147
+ // src/composables/useChart.ts
148
+ import { createChart as createChart2 } from "@opendata-ai/openchart-vanilla";
149
+ import { onMounted as onMounted2, onUnmounted as onUnmounted2, ref as ref2, shallowRef, watch as watch2 } from "vue";
150
+ function useChart(spec, options) {
151
+ const containerRef = ref2(null);
152
+ const chart = shallowRef(null);
153
+ const layout = shallowRef(null);
154
+ function mount() {
155
+ const container = containerRef.value;
156
+ if (!container) return;
157
+ const mountOpts = {
158
+ theme: options?.theme,
159
+ darkMode: options?.darkMode,
160
+ onDataPointClick: options?.onDataPointClick,
161
+ responsive: options?.responsive
162
+ };
163
+ const instance = createChart2(container, spec.value, mountOpts);
164
+ chart.value = instance;
165
+ layout.value = instance.layout;
166
+ }
167
+ function destroy() {
168
+ chart.value?.destroy();
169
+ chart.value = null;
170
+ layout.value = null;
171
+ }
172
+ onMounted2(() => {
173
+ mount();
174
+ });
175
+ onUnmounted2(() => {
176
+ destroy();
177
+ });
178
+ watch2(spec, (newSpec) => {
179
+ const instance = chart.value;
180
+ if (!instance) return;
181
+ instance.update(newSpec);
182
+ layout.value = instance.layout;
183
+ });
184
+ return {
185
+ containerRef,
186
+ chart,
187
+ layout
188
+ };
189
+ }
190
+
191
+ // src/composables/useDarkMode.ts
192
+ import { onUnmounted as onUnmounted3, ref as ref3, watch as watch3 } from "vue";
193
+ function useDarkMode(mode) {
194
+ const isDark = ref3(resolveInitial(mode.value));
195
+ let cleanup = null;
196
+ function setup(currentMode) {
197
+ cleanup?.();
198
+ cleanup = null;
199
+ if (currentMode !== "auto") {
200
+ isDark.value = currentMode === "force";
201
+ return;
202
+ }
203
+ if (typeof window === "undefined" || !window.matchMedia) {
204
+ isDark.value = false;
205
+ return;
206
+ }
207
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
208
+ isDark.value = mq.matches;
209
+ const handler = (e) => {
210
+ isDark.value = e.matches;
211
+ };
212
+ mq.addEventListener("change", handler);
213
+ cleanup = () => mq.removeEventListener("change", handler);
214
+ }
215
+ setup(mode.value);
216
+ watch3(mode, (newMode) => {
217
+ setup(newMode);
218
+ });
219
+ onUnmounted3(() => {
220
+ cleanup?.();
221
+ cleanup = null;
222
+ });
223
+ return isDark;
224
+ }
225
+ function resolveInitial(mode) {
226
+ if (mode === "force") return true;
227
+ if (mode === "off" || mode === void 0) return false;
228
+ if (typeof window !== "undefined" && window.matchMedia) {
229
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
230
+ }
231
+ return false;
232
+ }
233
+
234
+ // src/composables/useGraph.ts
235
+ import { ref as ref4 } from "vue";
236
+ function useGraph() {
237
+ const graphRef = ref4(null);
238
+ function search(query) {
239
+ graphRef.value?.search(query);
240
+ }
241
+ function clearSearch() {
242
+ graphRef.value?.clearSearch();
243
+ }
244
+ function zoomToFit() {
245
+ graphRef.value?.zoomToFit();
246
+ }
247
+ function zoomToNode(nodeId) {
248
+ graphRef.value?.zoomToNode(nodeId);
249
+ }
250
+ function selectNode(nodeId) {
251
+ graphRef.value?.selectNode(nodeId);
252
+ }
253
+ function getSelectedNodes() {
254
+ return graphRef.value?.getSelectedNodes() ?? [];
255
+ }
256
+ return {
257
+ graphRef,
258
+ search,
259
+ clearSearch,
260
+ zoomToFit,
261
+ zoomToNode,
262
+ selectNode,
263
+ getSelectedNodes
264
+ };
265
+ }
266
+
267
+ // src/composables/useTable.ts
268
+ import {
269
+ createTable
270
+ } from "@opendata-ai/openchart-vanilla";
271
+ import { onMounted as onMounted3, onUnmounted as onUnmounted4, ref as ref5, shallowRef as shallowRef2, watch as watch4 } from "vue";
272
+ function useTable(spec, options) {
273
+ const containerRef = ref5(null);
274
+ const table = shallowRef2(null);
275
+ const state = ref5({
276
+ sort: null,
277
+ search: "",
278
+ page: 0
279
+ });
280
+ const originalOnStateChange = options?.onStateChange;
281
+ function handleStateChange(newState) {
282
+ state.value = newState;
283
+ originalOnStateChange?.(newState);
284
+ }
285
+ function mount() {
286
+ const container = containerRef.value;
287
+ if (!container) return;
288
+ const mountOpts = {
289
+ ...options,
290
+ onStateChange: handleStateChange
291
+ };
292
+ const instance = createTable(container, spec.value, mountOpts);
293
+ table.value = instance;
294
+ state.value = instance.getState();
295
+ }
296
+ function destroy() {
297
+ table.value?.destroy();
298
+ table.value = null;
299
+ }
300
+ onMounted3(() => {
301
+ mount();
302
+ });
303
+ onUnmounted4(() => {
304
+ destroy();
305
+ });
306
+ watch4(spec, (newSpec) => {
307
+ const instance = table.value;
308
+ if (!instance) return;
309
+ instance.update(newSpec);
310
+ state.value = instance.getState();
311
+ });
312
+ return {
313
+ containerRef,
314
+ table,
315
+ state
316
+ };
317
+ }
318
+
319
+ // src/composables/useTableState.ts
320
+ import { ref as ref6 } from "vue";
321
+ function useTableState(initialState) {
322
+ const sort = ref6(initialState?.sort ?? null);
323
+ const search = ref6(initialState?.search ?? "");
324
+ const page = ref6(initialState?.page ?? 0);
325
+ function setSort(newSort) {
326
+ sort.value = newSort;
327
+ }
328
+ function setSearch(query) {
329
+ search.value = query;
330
+ }
331
+ function setPage(newPage) {
332
+ page.value = newPage;
333
+ }
334
+ function resetState() {
335
+ sort.value = initialState?.sort ?? null;
336
+ search.value = initialState?.search ?? "";
337
+ page.value = initialState?.page ?? 0;
338
+ }
339
+ return {
340
+ sort,
341
+ setSort,
342
+ search,
343
+ setSearch,
344
+ page,
345
+ setPage,
346
+ resetState
347
+ };
348
+ }
349
+
350
+ // src/DataTable.ts
351
+ import {
352
+ createTable as createTable2
353
+ } from "@opendata-ai/openchart-vanilla";
354
+ import {
355
+ defineComponent as defineComponent2,
356
+ h as h2,
357
+ inject as inject3,
358
+ onMounted as onMounted4,
359
+ onUnmounted as onUnmounted5,
360
+ ref as ref7,
361
+ watch as watch5
362
+ } from "vue";
363
+ var DataTable = defineComponent2({
364
+ name: "DataTable",
365
+ props: {
366
+ spec: {
367
+ type: Object,
368
+ required: true
369
+ },
370
+ theme: {
371
+ type: Object,
372
+ default: void 0
373
+ },
374
+ darkMode: {
375
+ type: String,
376
+ default: void 0
377
+ },
378
+ class: {
379
+ type: String,
380
+ default: void 0
381
+ },
382
+ style: {
383
+ type: [String, Object],
384
+ default: void 0
385
+ },
386
+ sort: {
387
+ type: [Object, null],
388
+ default: void 0
389
+ },
390
+ search: {
391
+ type: String,
392
+ default: void 0
393
+ },
394
+ page: {
395
+ type: Number,
396
+ default: void 0
397
+ }
398
+ },
399
+ emits: {
400
+ "row-click": (_row) => true,
401
+ "update:sort": (_sort) => true,
402
+ "update:search": (_query) => true,
403
+ "update:page": (_page) => true
404
+ },
405
+ setup(props, { emit }) {
406
+ const containerRef = ref7(null);
407
+ let instance = null;
408
+ const contextTheme = inject3(VizThemeKey, void 0);
409
+ const contextDarkMode = inject3(VizDarkModeKey, void 0);
410
+ function resolveTheme() {
411
+ return props.theme ?? contextTheme?.value;
412
+ }
413
+ function resolveDarkMode() {
414
+ return props.darkMode ?? contextDarkMode?.value;
415
+ }
416
+ function isControlled() {
417
+ return props.sort !== void 0 || props.search !== void 0 || props.page !== void 0;
418
+ }
419
+ let prevSpec = "";
420
+ function mountTable() {
421
+ const container = containerRef.value;
422
+ if (!container) return;
423
+ const mountOptions = {
424
+ theme: resolveTheme(),
425
+ darkMode: resolveDarkMode(),
426
+ onRowClick: (row) => emit("row-click", row),
427
+ responsive: true,
428
+ onStateChange: (state) => {
429
+ if (state.sort !== void 0) emit("update:sort", state.sort);
430
+ if (state.search !== void 0) emit("update:search", state.search);
431
+ if (state.page !== void 0) emit("update:page", state.page);
432
+ }
433
+ };
434
+ if (isControlled()) {
435
+ mountOptions.externalState = {
436
+ sort: props.sort ?? null,
437
+ search: props.search ?? "",
438
+ page: props.page ?? 0
439
+ };
440
+ }
441
+ instance = createTable2(container, props.spec, mountOptions);
442
+ prevSpec = JSON.stringify(props.spec);
443
+ }
444
+ function destroyTable() {
445
+ instance?.destroy();
446
+ instance = null;
447
+ prevSpec = "";
448
+ }
449
+ onMounted4(() => {
450
+ mountTable();
451
+ });
452
+ onUnmounted5(() => {
453
+ destroyTable();
454
+ });
455
+ watch5(
456
+ () => JSON.stringify(props.spec),
457
+ (newVal) => {
458
+ if (!instance) return;
459
+ if (newVal !== prevSpec) {
460
+ prevSpec = newVal;
461
+ instance.update(props.spec);
462
+ }
463
+ }
464
+ );
465
+ watch5(
466
+ [
467
+ () => props.theme,
468
+ () => props.darkMode,
469
+ () => contextTheme?.value,
470
+ () => contextDarkMode?.value
471
+ ],
472
+ () => {
473
+ if (!containerRef.value) return;
474
+ destroyTable();
475
+ mountTable();
476
+ }
477
+ );
478
+ watch5([() => props.sort, () => props.search, () => props.page], () => {
479
+ if (!instance || !isControlled()) return;
480
+ instance.setState({
481
+ sort: props.sort ?? null,
482
+ search: props.search ?? "",
483
+ page: props.page ?? 0
484
+ });
485
+ });
486
+ const rootClass = () => {
487
+ const base = "viz-table-root";
488
+ return props.class ? `${base} ${props.class}` : base;
489
+ };
490
+ return () => h2("div", {
491
+ ref: containerRef,
492
+ class: rootClass(),
493
+ style: props.style
494
+ });
495
+ }
496
+ });
497
+
498
+ // src/Graph.ts
499
+ import {
500
+ createGraph
501
+ } from "@opendata-ai/openchart-vanilla";
502
+ import {
503
+ defineComponent as defineComponent3,
504
+ h as h3,
505
+ inject as inject4,
506
+ onMounted as onMounted5,
507
+ onUnmounted as onUnmounted6,
508
+ ref as ref8,
509
+ watch as watch6
510
+ } from "vue";
511
+ var Graph = defineComponent3({
512
+ name: "Graph",
513
+ props: {
514
+ spec: {
515
+ type: Object,
516
+ required: true
517
+ },
518
+ theme: {
519
+ type: Object,
520
+ default: void 0
521
+ },
522
+ darkMode: {
523
+ type: String,
524
+ default: void 0
525
+ },
526
+ class: {
527
+ type: String,
528
+ default: void 0
529
+ },
530
+ style: {
531
+ type: [String, Object],
532
+ default: void 0
533
+ }
534
+ },
535
+ emits: {
536
+ "node-click": (_node) => true,
537
+ "node-double-click": (_node) => true,
538
+ "selection-change": (_nodeIds) => true
539
+ },
540
+ setup(props, { emit, expose }) {
541
+ const containerRef = ref8(null);
542
+ let instance = null;
543
+ let prevSpec = "";
544
+ const contextTheme = inject4(VizThemeKey, void 0);
545
+ const contextDarkMode = inject4(VizDarkModeKey, void 0);
546
+ function resolveTheme() {
547
+ return props.theme ?? contextTheme?.value;
548
+ }
549
+ function resolveDarkMode() {
550
+ return props.darkMode ?? contextDarkMode?.value;
551
+ }
552
+ function mountGraph() {
553
+ const container = containerRef.value;
554
+ if (!container) return;
555
+ const options = {
556
+ theme: resolveTheme(),
557
+ darkMode: resolveDarkMode(),
558
+ onNodeClick: (node) => emit("node-click", node),
559
+ onNodeDoubleClick: (node) => emit("node-double-click", node),
560
+ onSelectionChange: (nodeIds) => emit("selection-change", nodeIds),
561
+ responsive: true
562
+ };
563
+ instance = createGraph(container, props.spec, options);
564
+ prevSpec = JSON.stringify(props.spec);
565
+ }
566
+ function destroyGraph() {
567
+ instance?.destroy();
568
+ instance = null;
569
+ prevSpec = "";
570
+ }
571
+ expose({
572
+ search(query) {
573
+ instance?.search(query);
574
+ },
575
+ clearSearch() {
576
+ instance?.clearSearch();
577
+ },
578
+ zoomToFit() {
579
+ instance?.zoomToFit();
580
+ },
581
+ zoomToNode(nodeId) {
582
+ instance?.zoomToNode(nodeId);
583
+ },
584
+ selectNode(nodeId) {
585
+ instance?.selectNode(nodeId);
586
+ },
587
+ getSelectedNodes() {
588
+ return instance?.getSelectedNodes() ?? [];
589
+ },
590
+ get instance() {
591
+ return instance;
592
+ }
593
+ });
594
+ onMounted5(() => {
595
+ mountGraph();
596
+ });
597
+ onUnmounted6(() => {
598
+ destroyGraph();
599
+ });
600
+ watch6(
601
+ () => JSON.stringify(props.spec),
602
+ (newVal) => {
603
+ if (!instance) return;
604
+ if (newVal !== prevSpec) {
605
+ prevSpec = newVal;
606
+ instance.update(props.spec);
607
+ }
608
+ }
609
+ );
610
+ watch6(
611
+ [
612
+ () => props.theme,
613
+ () => props.darkMode,
614
+ () => contextTheme?.value,
615
+ () => contextDarkMode?.value
616
+ ],
617
+ () => {
618
+ if (!containerRef.value) return;
619
+ destroyGraph();
620
+ mountGraph();
621
+ }
622
+ );
623
+ const rootClass = () => {
624
+ const base = "viz-graph-root";
625
+ return props.class ? `${base} ${props.class}` : base;
626
+ };
627
+ return () => h3("div", {
628
+ ref: containerRef,
629
+ class: rootClass(),
630
+ style: props.style
631
+ });
632
+ }
633
+ });
634
+
635
+ // src/ThemeProvider.ts
636
+ import { computed as computed2, defineComponent as defineComponent4, provide } from "vue";
637
+ var VizThemeProvider = defineComponent4({
638
+ name: "VizThemeProvider",
639
+ props: {
640
+ theme: {
641
+ type: Object,
642
+ default: void 0
643
+ },
644
+ darkMode: {
645
+ type: String,
646
+ default: void 0
647
+ }
648
+ },
649
+ setup(props, { slots }) {
650
+ const themeRef = computed2(() => props.theme);
651
+ const darkModeRef = computed2(() => props.darkMode);
652
+ provide(VizThemeKey, themeRef);
653
+ provide(VizDarkModeKey, darkModeRef);
654
+ return () => slots.default?.();
655
+ }
656
+ });
657
+
658
+ // src/Visualization.ts
659
+ import { isGraphSpec, isTableSpec } from "@opendata-ai/openchart-core";
660
+ import { defineComponent as defineComponent5, h as h4 } from "vue";
661
+ var Visualization = defineComponent5({
662
+ name: "Visualization",
663
+ props: {
664
+ spec: {
665
+ type: Object,
666
+ required: true
667
+ },
668
+ theme: {
669
+ type: Object,
670
+ default: void 0
671
+ },
672
+ darkMode: {
673
+ type: String,
674
+ default: void 0
675
+ },
676
+ class: {
677
+ type: String,
678
+ default: void 0
679
+ },
680
+ style: {
681
+ type: [String, Object],
682
+ default: void 0
683
+ }
684
+ },
685
+ setup(props) {
686
+ return () => {
687
+ const { spec, theme, darkMode, class: className, style } = props;
688
+ const sharedProps = { theme, darkMode, class: className, style };
689
+ if (isTableSpec(spec)) {
690
+ return h4(DataTable, { ...sharedProps, spec });
691
+ }
692
+ if (isGraphSpec(spec)) {
693
+ return h4(Graph, { ...sharedProps, spec });
694
+ }
695
+ return h4(Chart, { ...sharedProps, spec });
696
+ };
697
+ }
698
+ });
699
+ export {
700
+ Chart,
701
+ DataTable,
702
+ Graph,
703
+ Visualization,
704
+ VizDarkModeKey,
705
+ VizThemeKey,
706
+ VizThemeProvider,
707
+ useChart,
708
+ useDarkMode,
709
+ useGraph,
710
+ useTable,
711
+ useTableState,
712
+ useVizDarkMode,
713
+ useVizTheme
714
+ };
715
+ //# sourceMappingURL=index.js.map