@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/LICENSE +56 -0
- package/README.md +391 -0
- package/chart-schema.d.ts +91 -0
- package/chart-schema.json +1144 -0
- package/chartbuddy-embed.api.mjs +64 -0
- package/chartbuddy-embed.d.ts +488 -0
- package/chartbuddy-embed.global.js +135 -0
- package/chartbuddy-embed.mjs +135 -0
- package/chartbuddy-embed.single.global.js +4201 -0
- package/chartbuddy-embed.single.mjs +4201 -0
- package/element.d.ts +78 -0
- package/element.mjs +233 -0
- package/js/core/chart/labelPlacementWorker.js +2 -0
- package/labelPlacementAccel-QHVANOZO.wasm +0 -0
- package/llms.txt +341 -0
- package/package.json +101 -0
- package/react.d.ts +97 -0
- package/react.mjs +219 -0
- package/ts/shared/libraries/DOMPurify.min.js +2 -0
- package/ts/shared/libraries/d3.min.js +2 -0
- package/vue.d.ts +108 -0
- package/vue.mjs +253 -0
- package/webapp-entry.css +2 -0
- package/webapp-entry.js +4063 -0
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;
|
package/webapp-entry.css
ADDED
|
@@ -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 */
|