@chartbuddy.io/embed 1.7.52

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/vue.d.ts ADDED
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Type declarations for `@chartbuddy.io/embed/vue`.
3
+ *
4
+ * `vue` is a peer dependency — install it yourself. Vue 3.
5
+ */
6
+
7
+ import type { DefineComponent, Ref, ShallowRef } from 'vue';
8
+ import type {
9
+ ChartData,
10
+ ChartDataInput,
11
+ EmbedMode,
12
+ Insight,
13
+ InsightOptions,
14
+ } from './chartbuddy-embed';
15
+
16
+ /** A value the composable accepts as either a plain value, a ref, or a getter. */
17
+ export type MaybeReactive<T> = T | Ref<T> | (() => T);
18
+
19
+ export interface UseInsightOptions extends Omit<InsightOptions, 'chartData'> {
20
+ /**
21
+ * Chart config to render. Compared **by identity**, not deeply: assign a new
22
+ * object to trigger an `update()` on the live instance. Mutating a nested
23
+ * property in place will not be picked up.
24
+ */
25
+ chartData?: MaybeReactive<ChartDataInput | undefined>;
26
+ /** Called once the insight has finished booting. */
27
+ onReady?: (insight: Insight) => void;
28
+ /** Called after any meaningful config change, including live edits. */
29
+ onChange?: (chartData: ChartData | null) => void;
30
+ /** Called when the insight switches between view and edit. */
31
+ onModeChange?: (mode: EmbedMode) => void;
32
+ }
33
+
34
+ export interface UseInsightResult {
35
+ /** Bind to the element that should hold the chart. Size it yourself. */
36
+ containerRef: Ref<HTMLElement | null>;
37
+ /** The live instance, or null before mount / after unmount. */
38
+ insight: ShallowRef<Insight | null>;
39
+ /** True once `insight.ready` has resolved. */
40
+ ready: Ref<boolean>;
41
+ /**
42
+ * The last validation or boot failure, or null. Invalid `chartData` surfaces
43
+ * here as a `ChartDataValidationError` — read `error.issues` for the paths.
44
+ */
45
+ error: Ref<Error | null>;
46
+ }
47
+
48
+ /**
49
+ * Mount an Insight and keep it alive for the component's lifetime.
50
+ *
51
+ * Changing `instanceId`, `editable`, `toolbar`, `persist`, `assetBase`, or
52
+ * `hostOverrides` remounts the chart; changing `chartData` patches it in place.
53
+ *
54
+ * @example
55
+ * const chartData = ref({ chartType: 'line', seriesData: grid });
56
+ * const { containerRef, insight, ready } = useInsight({ chartData });
57
+ * // <div ref="containerRef" style="height: 400px" />
58
+ */
59
+ export function useInsight(options?: UseInsightOptions): UseInsightResult;
60
+
61
+ export interface InsightChartProps {
62
+ chartData?: ChartDataInput;
63
+ instanceId?: string;
64
+ editable?: boolean;
65
+ toolbar?: boolean;
66
+ persist?: boolean;
67
+ assetBase?: string;
68
+ hostOverrides?: InsightOptions['hostOverrides'];
69
+ }
70
+
71
+ export interface InsightChartSlotProps {
72
+ insight: Insight | null;
73
+ ready: boolean;
74
+ error: Error | null;
75
+ }
76
+
77
+ /**
78
+ * Declarative single-div wrapper around `useInsight`.
79
+ *
80
+ * Emits `ready`, `change`, `mode`, and `error`. Exposes `{ insight, ready, error }`
81
+ * via a template ref.
82
+ *
83
+ * @example
84
+ * <InsightChart
85
+ * :chart-data="chartData"
86
+ * editable
87
+ * style="height: 400px"
88
+ * @change="onChange"
89
+ * @error="onError"
90
+ * />
91
+ */
92
+ export declare const InsightChart: DefineComponent<
93
+ InsightChartProps,
94
+ { insight: ShallowRef<Insight | null>; ready: Ref<boolean>; error: Ref<Error | null> },
95
+ {},
96
+ {},
97
+ {},
98
+ {},
99
+ {},
100
+ {
101
+ ready: (insight: Insight) => void;
102
+ change: (chartData: ChartData | null) => void;
103
+ mode: (mode: EmbedMode) => void;
104
+ error: (error: Error) => void;
105
+ }
106
+ >;
107
+
108
+ export default InsightChart;
package/vue.mjs ADDED
@@ -0,0 +1,253 @@
1
+ /**
2
+ * @chartbuddy.io/embed/vue — Vue 3 bindings for the imperative Insight API.
3
+ *
4
+ * Same job as the React bindings: `new Insight()` owns a DOM node and lives
5
+ * across renders, so consumers should not each rewrite the same lifecycle —
6
+ * mounting once, patching on data changes instead of remounting, keeping
7
+ * handlers stable, and destroying on unmount.
8
+ *
9
+ * Vue's reactivity makes most of this simpler than React (no StrictMode double
10
+ * mount, no dependency arrays), so this file is mostly `watch` wiring.
11
+ *
12
+ * Written without SFC/JSX so the package ships no build step and no bundler
13
+ * assumptions. `vue` is a peer dependency, imported here and never bundled.
14
+ *
15
+ * @example
16
+ * <script setup>
17
+ * import { InsightChart } from '@chartbuddy.io/embed/vue';
18
+ * const chartData = { chartType: 'clusteredBar', seriesData: grid };
19
+ * </script>
20
+ *
21
+ * <template>
22
+ * <InsightChart :chart-data="chartData" editable style="height: 400px" />
23
+ * </template>
24
+ */
25
+
26
+ import {
27
+ defineComponent,
28
+ h,
29
+ onBeforeUnmount,
30
+ onMounted,
31
+ ref,
32
+ shallowRef,
33
+ toRaw,
34
+ watch,
35
+ } from 'vue';
36
+ import { Insight } from './chartbuddy-embed.api.mjs';
37
+
38
+ /** Options consumed at construction time; changing one requires a remount. */
39
+ const CONSTRUCTION_KEYS = [
40
+ 'instanceId',
41
+ 'editable',
42
+ 'toolbar',
43
+ 'persist',
44
+ 'assetBase',
45
+ 'hostOverrides',
46
+ ];
47
+
48
+ /**
49
+ * Unwrap a value that may be a ref, so options accept both plain values and
50
+ * refs — `useInsight({ chartData })` works with either.
51
+ */
52
+ function read(value) {
53
+ return value && typeof value === 'object' && 'value' in value && value.__v_isRef
54
+ ? value.value
55
+ : value;
56
+ }
57
+
58
+ /** Pick just the construction options out of the caller's option bag. */
59
+ function constructionOptions(options) {
60
+ const out = {};
61
+ for (const key of CONSTRUCTION_KEYS) {
62
+ const value = read(options[key]);
63
+ if (value !== undefined) out[key] = value;
64
+ }
65
+ return out;
66
+ }
67
+
68
+ /** Shallow-compare construction options so unrelated changes never remount. */
69
+ function sameConstructionOptions(a, b) {
70
+ for (const key of CONSTRUCTION_KEYS) {
71
+ if (a[key] !== b[key]) return false;
72
+ }
73
+ return true;
74
+ }
75
+
76
+ /**
77
+ * Mount an Insight into a template ref and keep it alive for the component's life.
78
+ *
79
+ * Returns `{ containerRef, insight, ready, error }`. Bind `containerRef` to an
80
+ * element you have sized; `insight` is a `shallowRef` holding null until mount
81
+ * resolves, so guard imperative calls on it.
82
+ *
83
+ * `chartData` may be a plain object, a ref, or a getter. Whenever it changes the
84
+ * hook calls `update()` on the live instance rather than remounting. Comparison
85
+ * is by identity — assign a new object to trigger an update.
86
+ */
87
+ export function useInsight(options = {}) {
88
+ const containerRef = ref(null);
89
+ const insight = shallowRef(null);
90
+ const ready = ref(false);
91
+ const error = ref(null);
92
+
93
+ let current = null;
94
+ let openedWith;
95
+ let disposed = false;
96
+ const stops = [];
97
+
98
+ const readChartData = () =>
99
+ typeof options.chartData === 'function' ? options.chartData() : read(options.chartData);
100
+
101
+ function teardown() {
102
+ stops.splice(0).forEach((stop) => stop());
103
+ ready.value = false;
104
+ insight.value = null;
105
+ if (current) {
106
+ current.destroy();
107
+ current = null;
108
+ }
109
+ }
110
+
111
+ function mount() {
112
+ const container = containerRef.value;
113
+ if (!container) return;
114
+
115
+ teardown();
116
+
117
+ const chartData = readChartData();
118
+ let instance;
119
+ try {
120
+ instance = new Insight(container, {
121
+ ...constructionOptions(options),
122
+ // toRaw so the engine never receives a reactive proxy of the config.
123
+ chartData: chartData === undefined ? undefined : toRaw(chartData),
124
+ });
125
+ } catch (err) {
126
+ // Invalid chartData throws synchronously (ChartDataValidationError).
127
+ error.value = err instanceof Error ? err : new Error(String(err));
128
+ return;
129
+ }
130
+
131
+ current = instance;
132
+ openedWith = chartData;
133
+ insight.value = instance;
134
+ error.value = null;
135
+
136
+ stops.push(
137
+ instance.on('change', (cd) => options.onChange?.(cd)),
138
+ instance.on('mode', (mode) => options.onModeChange?.(mode)),
139
+ );
140
+
141
+ instance.ready.then(
142
+ () => {
143
+ // The component may have unmounted, or remounted onto a new instance,
144
+ // before this resolved.
145
+ if (disposed || current !== instance) return;
146
+ ready.value = true;
147
+ options.onReady?.(instance);
148
+ },
149
+ (err) => {
150
+ if (disposed || current !== instance) return;
151
+ error.value = err instanceof Error ? err : new Error(String(err));
152
+ },
153
+ );
154
+ }
155
+
156
+ onMounted(mount);
157
+
158
+ onBeforeUnmount(() => {
159
+ disposed = true;
160
+ teardown();
161
+ });
162
+
163
+ // Data changes patch the live instance. Skipped when the value is the one the
164
+ // instance already opened with, so mounting does not also fire an update.
165
+ watch(readChartData, (chartData) => {
166
+ if (!current || chartData === undefined || chartData === openedWith) return;
167
+ openedWith = chartData;
168
+ try {
169
+ current.update(toRaw(chartData));
170
+ error.value = null;
171
+ } catch (err) {
172
+ error.value = err instanceof Error ? err : new Error(String(err));
173
+ }
174
+ });
175
+
176
+ // A real construction-option change does need a fresh mount.
177
+ watch(
178
+ () => constructionOptions(options),
179
+ (next, prev) => {
180
+ if (!current || sameConstructionOptions(next, prev)) return;
181
+ mount();
182
+ },
183
+ { deep: false },
184
+ );
185
+
186
+ return { containerRef, insight, ready, error };
187
+ }
188
+
189
+ /**
190
+ * Declarative wrapper around `useInsight`.
191
+ *
192
+ * Renders a single div. Size it via `style` / `class` — the chart fills its
193
+ * container, so a container with no height renders nothing visible.
194
+ *
195
+ * Emits `ready`, `change`, `mode`, and `error`. The default slot receives
196
+ * `{ insight, ready, error }` for overlay content.
197
+ */
198
+ export const InsightChart = defineComponent({
199
+ name: 'InsightChart',
200
+ props: {
201
+ chartData: { type: Object, default: undefined },
202
+ instanceId: { type: String, default: undefined },
203
+ editable: { type: Boolean, default: undefined },
204
+ toolbar: { type: Boolean, default: undefined },
205
+ persist: { type: Boolean, default: undefined },
206
+ assetBase: { type: String, default: undefined },
207
+ hostOverrides: { type: Object, default: undefined },
208
+ },
209
+ emits: ['ready', 'change', 'mode', 'error'],
210
+ setup(props, { emit, slots, expose }) {
211
+ const { containerRef, insight, ready, error } = useInsight({
212
+ // Getters, so the composable tracks prop changes reactively.
213
+ chartData: () => props.chartData,
214
+ get instanceId() {
215
+ return props.instanceId;
216
+ },
217
+ get editable() {
218
+ return props.editable;
219
+ },
220
+ get toolbar() {
221
+ return props.toolbar;
222
+ },
223
+ get persist() {
224
+ return props.persist;
225
+ },
226
+ get assetBase() {
227
+ return props.assetBase;
228
+ },
229
+ get hostOverrides() {
230
+ return props.hostOverrides;
231
+ },
232
+ onReady: (instance) => emit('ready', instance),
233
+ onChange: (cd) => emit('change', cd),
234
+ onModeChange: (mode) => emit('mode', mode),
235
+ });
236
+
237
+ watch(error, (err) => {
238
+ if (err) emit('error', err);
239
+ });
240
+
241
+ // Parent components can reach the instance via a template ref.
242
+ expose({ insight, ready, error });
243
+
244
+ return () =>
245
+ h('div', { ref: containerRef }, slots.default?.({
246
+ insight: insight.value,
247
+ ready: ready.value,
248
+ error: error.value,
249
+ }));
250
+ },
251
+ });
252
+
253
+ export default InsightChart;
@@ -0,0 +1,2 @@
1
+ .tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
2
+ /*# sourceMappingURL=webapp-entry.css.map */