visio-solid-viewer 0.7.4 → 0.8.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 CHANGED
@@ -1,6 +1,8 @@
1
- import { mountViewer, propertyKeys } from "ooxml-ui/visio";
2
- import { className, effect, setAttribute, style, template, use } from "solid-js/web";
3
- import { createEffect, createSignal, onCleanup, onMount } from "solid-js";
1
+ import * as ui from "ooxml-ui/visio";
2
+ import { ViewerController, createWorkerParser, mountViewer, propertyKeys } from "ooxml-ui/visio";
3
+ import { addEventListener, className, createComponent, delegateEvents, effect, insert, setAttribute, style, template, use } from "solid-js/web";
4
+ import { For, createEffect, createMemo, createSignal, onCleanup, onMount } from "solid-js";
5
+ import * as officeTheme from "ooxml-ui/theme";
4
6
  export * from "ooxml-ui/visio";
5
7
  export * from "visio-core";
6
8
  //#region packages/bindings/src/common.ts
@@ -145,7 +147,7 @@ function viewerStateSource(current) {
145
147
  }
146
148
  //#endregion
147
149
  //#region packages/bindings/src/solid.tsx
148
- var _tmpl$ = /*#__PURE__*/ template(`<div>`);
150
+ var _tmpl$$1 = /*#__PURE__*/ template(`<div>`);
149
151
  /** Native Solid host and ownership; all document work remains in the shared binding. */
150
152
  function VisioViewer(props) {
151
153
  let host;
@@ -181,7 +183,7 @@ function VisioViewer(props) {
181
183
  }
182
184
  });
183
185
  return (() => {
184
- var _el$ = _tmpl$();
186
+ var _el$ = _tmpl$$1();
185
187
  var _ref$ = host;
186
188
  typeof _ref$ === "function" ? use(_ref$, _el$) : host = _el$;
187
189
  effect((_p$) => {
@@ -213,4 +215,174 @@ function createVisioViewerState(handle) {
213
215
  return state;
214
216
  }
215
217
  //#endregion
216
- export { VisioViewer, createVisioViewerState };
218
+ //#region packages/bindings/src/building-blocks.ts
219
+ function buildingBlock(key) {
220
+ const value = Reflect.get(ui, key);
221
+ if (typeof value !== "function") throw new Error("Update ooxml-ui to use Visio building blocks.");
222
+ return value;
223
+ }
224
+ /** Framework-neutral ownership only; each adapter supplies its own reactive state. */
225
+ function createBuildingBlockSession() {
226
+ const controller = new ViewerController(createWorkerParser());
227
+ let options = {};
228
+ let source;
229
+ let host = null;
230
+ let disposeCanvas;
231
+ let destroyed = false;
232
+ const stopEvents = controller.onEvent((name, detail) => {
233
+ if (name === "document-change") {
234
+ const change = detail;
235
+ options.onDocumentChange?.(change.document);
236
+ options.onDirtyChange?.(change.dirty);
237
+ } else if (name === "document-error") options.onDocumentError?.(detail);
238
+ });
239
+ const fit = () => {
240
+ if (host) buildingBlock("fitDiagramCanvas")(host, controller);
241
+ };
242
+ return {
243
+ controller,
244
+ mount(node) {
245
+ if (destroyed || node === host) return;
246
+ disposeCanvas?.();
247
+ disposeCanvas = void 0;
248
+ host = node;
249
+ if (node) disposeCanvas = buildingBlock("mountDiagramCanvas")(node, controller);
250
+ },
251
+ toolbar(locale = options.locale) {
252
+ return buildingBlock("diagramToolbarProps")(controller, fit, locale);
253
+ },
254
+ update(next) {
255
+ if (destroyed) return;
256
+ options = next;
257
+ controller.canEdit = next.canEdit !== false;
258
+ if (source === next.content) return;
259
+ source = next.content;
260
+ controller.cancelLoad();
261
+ if (!source) return;
262
+ const input = source;
263
+ controller.loadSource(async () => {
264
+ return input instanceof Blob ? new Uint8Array(await input.arrayBuffer()) : input;
265
+ }).catch(() => {});
266
+ },
267
+ destroy() {
268
+ if (destroyed) return;
269
+ destroyed = true;
270
+ disposeCanvas?.();
271
+ disposeCanvas = void 0;
272
+ host = null;
273
+ stopEvents();
274
+ controller.destroy();
275
+ }
276
+ };
277
+ }
278
+ /** Resolve the new optional-control theme API only when that control is mounted. */
279
+ function prepareDiagramBlock(host, kind) {
280
+ if (!host) return;
281
+ const prepare = Reflect.get(officeTheme, "prepareOfficeUiBlock");
282
+ if (typeof prepare !== "function") throw new Error("Update ooxml-ui to theme Visio building blocks.");
283
+ prepare(host, kind, "visio");
284
+ }
285
+ //#endregion
286
+ //#region packages/solid/src/building-blocks.tsx
287
+ var _tmpl$ = /*#__PURE__*/ template(`<div>`);
288
+ var _tmpl$2 = /*#__PURE__*/ template(`<div role=toolbar><button type=button></button><button type=button></button><button type=button>`);
289
+ var _tmpl$3 = /*#__PURE__*/ template(`<select>`);
290
+ var _tmpl$4 = /*#__PURE__*/ template(`<option>`);
291
+ function createViewerBuildingBlocks(options = () => ({})) {
292
+ const session = createBuildingBlockSession();
293
+ const [state, setState] = createSignal(session.controller.state);
294
+ const stop = session.controller.subscribe((value) => setState(() => value));
295
+ createEffect(() => session.update(options()));
296
+ onCleanup(() => {
297
+ stop();
298
+ session.destroy();
299
+ });
300
+ const toolbarProps = createMemo(() => {
301
+ state();
302
+ return session.toolbar(options().locale);
303
+ });
304
+ return {
305
+ controller: session.controller,
306
+ state,
307
+ canvasProps: { mount: session.mount },
308
+ toolbarProps,
309
+ inspectorProps: { controller: session.controller },
310
+ loading: () => state().loading,
311
+ error: () => state().error ?? state().edit.error,
312
+ mode: () => state().canEdit ? "edit" : "view"
313
+ };
314
+ }
315
+ function DiagramCanvas(props) {
316
+ let host;
317
+ createEffect(() => {
318
+ const mount = props.mount;
319
+ mount(host);
320
+ onCleanup(() => mount(null));
321
+ });
322
+ return (() => {
323
+ var _el$ = _tmpl$();
324
+ var _ref$ = host;
325
+ typeof _ref$ === "function" ? use(_ref$, _el$) : host = _el$;
326
+ effect(() => className(_el$, props.class));
327
+ return _el$;
328
+ })();
329
+ }
330
+ function Toolbar(props) {
331
+ return (() => {
332
+ var _el$2 = _tmpl$2(), _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, _el$5 = _el$4.nextSibling;
333
+ use((host) => prepareDiagramBlock(host, "toolbar"), _el$2);
334
+ _el$3.$$click = () => void props.onUndo();
335
+ insert(_el$3, () => props.labels.Undo);
336
+ _el$4.$$click = () => void props.onRedo();
337
+ insert(_el$4, () => props.labels.Redo);
338
+ addEventListener(_el$5, "click", props.onFit, true);
339
+ insert(_el$5, () => props.labels["Fit to Window"]);
340
+ effect((_p$) => {
341
+ var _v$ = !props.canUndo, _v$2 = !props.canRedo;
342
+ _v$ !== _p$.e && (_el$3.disabled = _p$.e = _v$);
343
+ _v$2 !== _p$.t && (_el$4.disabled = _p$.t = _v$2);
344
+ return _p$;
345
+ }, {
346
+ e: void 0,
347
+ t: void 0
348
+ });
349
+ return _el$2;
350
+ })();
351
+ }
352
+ function PageNavigation(props) {
353
+ return (() => {
354
+ var _el$6 = _tmpl$3();
355
+ _el$6.addEventListener("change", (event) => props.onPageChange(Number(event.currentTarget.value)));
356
+ use((host) => prepareDiagramBlock(host, "tabs"), _el$6);
357
+ insert(_el$6, createComponent(For, {
358
+ get each() {
359
+ return props.pages;
360
+ },
361
+ children: (page, index) => (() => {
362
+ var _el$7 = _tmpl$4();
363
+ insert(_el$7, () => page.name);
364
+ effect(() => _el$7.value = index());
365
+ return _el$7;
366
+ })()
367
+ }));
368
+ effect(() => setAttribute(_el$6, "aria-label", props.labels.Page));
369
+ effect(() => _el$6.value = props.pageIndex);
370
+ return _el$6;
371
+ })();
372
+ }
373
+ function Inspector(props) {
374
+ let host;
375
+ createEffect(() => {
376
+ const stop = buildingBlock("mountDiagramInspector")(host, props.controller);
377
+ onCleanup(stop);
378
+ });
379
+ return (() => {
380
+ var _el$8 = _tmpl$();
381
+ var _ref$2 = host;
382
+ typeof _ref$2 === "function" ? use(_ref$2, _el$8) : host = _el$8;
383
+ return _el$8;
384
+ })();
385
+ }
386
+ delegateEvents(["click"]);
387
+ //#endregion
388
+ export { DiagramCanvas, Inspector, PageNavigation, Toolbar, VisioViewer, createViewerBuildingBlocks, createVisioViewerState };
@@ -1,6 +1,8 @@
1
- import { mountViewer, propertyKeys } from "ooxml-ui/visio";
2
- import { escape, ssr, ssrAttribute, ssrHydrationKey, ssrStyle } from "solid-js/web";
3
- import { createEffect, createSignal, onCleanup, onMount } from "solid-js";
1
+ import * as ui from "ooxml-ui/visio";
2
+ import { ViewerController, createWorkerParser, mountViewer, propertyKeys } from "ooxml-ui/visio";
3
+ import { createComponent, escape, ssr, ssrAttribute, ssrHydrationKey, ssrStyle } from "solid-js/web";
4
+ import { For, createEffect, createMemo, createSignal, onCleanup, onMount } from "solid-js";
5
+ import "ooxml-ui/theme";
4
6
  export * from "ooxml-ui/visio";
5
7
  export * from "visio-core";
6
8
  //#region packages/bindings/src/common.ts
@@ -145,7 +147,7 @@ function viewerStateSource(current) {
145
147
  }
146
148
  //#endregion
147
149
  //#region packages/bindings/src/solid.tsx
148
- var _tmpl$ = [
150
+ var _tmpl$$1 = [
149
151
  "<div",
150
152
  " style=\"",
151
153
  "\"",
@@ -185,7 +187,7 @@ function VisioViewer(props) {
185
187
  mounted?.destroy();
186
188
  }
187
189
  });
188
- return ssr(_tmpl$, ssrHydrationKey() + ssrAttribute("class", escape(props.class, true), false), ssrStyle(props.style), ssrAttribute("aria-label", escape(props["aria-label"], true), false));
190
+ return ssr(_tmpl$$1, ssrHydrationKey() + ssrAttribute("class", escape(props.class, true), false), ssrStyle(props.style), ssrAttribute("aria-label", escape(props["aria-label"], true), false));
189
191
  }
190
192
  /**
191
193
  * Reactive viewer state as a Solid accessor for a handle accessor (for example a signal set by
@@ -202,4 +204,139 @@ function createVisioViewerState(handle) {
202
204
  return state;
203
205
  }
204
206
  //#endregion
205
- export { VisioViewer, createVisioViewerState };
207
+ //#region packages/bindings/src/building-blocks.ts
208
+ function buildingBlock(key) {
209
+ const value = Reflect.get(ui, key);
210
+ if (typeof value !== "function") throw new Error("Update ooxml-ui to use Visio building blocks.");
211
+ return value;
212
+ }
213
+ /** Framework-neutral ownership only; each adapter supplies its own reactive state. */
214
+ function createBuildingBlockSession() {
215
+ const controller = new ViewerController(createWorkerParser());
216
+ let options = {};
217
+ let source;
218
+ let host = null;
219
+ let disposeCanvas;
220
+ let destroyed = false;
221
+ const stopEvents = controller.onEvent((name, detail) => {
222
+ if (name === "document-change") {
223
+ const change = detail;
224
+ options.onDocumentChange?.(change.document);
225
+ options.onDirtyChange?.(change.dirty);
226
+ } else if (name === "document-error") options.onDocumentError?.(detail);
227
+ });
228
+ const fit = () => {
229
+ if (host) buildingBlock("fitDiagramCanvas")(host, controller);
230
+ };
231
+ return {
232
+ controller,
233
+ mount(node) {
234
+ if (destroyed || node === host) return;
235
+ disposeCanvas?.();
236
+ disposeCanvas = void 0;
237
+ host = node;
238
+ if (node) disposeCanvas = buildingBlock("mountDiagramCanvas")(node, controller);
239
+ },
240
+ toolbar(locale = options.locale) {
241
+ return buildingBlock("diagramToolbarProps")(controller, fit, locale);
242
+ },
243
+ update(next) {
244
+ if (destroyed) return;
245
+ options = next;
246
+ controller.canEdit = next.canEdit !== false;
247
+ if (source === next.content) return;
248
+ source = next.content;
249
+ controller.cancelLoad();
250
+ if (!source) return;
251
+ const input = source;
252
+ controller.loadSource(async () => {
253
+ return input instanceof Blob ? new Uint8Array(await input.arrayBuffer()) : input;
254
+ }).catch(() => {});
255
+ },
256
+ destroy() {
257
+ if (destroyed) return;
258
+ destroyed = true;
259
+ disposeCanvas?.();
260
+ disposeCanvas = void 0;
261
+ host = null;
262
+ stopEvents();
263
+ controller.destroy();
264
+ }
265
+ };
266
+ }
267
+ //#endregion
268
+ //#region packages/solid/src/building-blocks.tsx
269
+ var _tmpl$ = ["<div", "></div>"];
270
+ var _tmpl$2 = [
271
+ "<div",
272
+ " role=\"toolbar\"><button type=\"button\"",
273
+ ">",
274
+ "</button><button type=\"button\"",
275
+ ">",
276
+ "</button><button type=\"button\">",
277
+ "</button></div>"
278
+ ];
279
+ var _tmpl$3 = [
280
+ "<select",
281
+ ">",
282
+ "</select>"
283
+ ];
284
+ var _tmpl$4 = [
285
+ "<option",
286
+ ">",
287
+ "</option>"
288
+ ];
289
+ function createViewerBuildingBlocks(options = () => ({})) {
290
+ const session = createBuildingBlockSession();
291
+ const [state, setState] = createSignal(session.controller.state);
292
+ const stop = session.controller.subscribe((value) => setState(() => value));
293
+ createEffect(() => session.update(options()));
294
+ onCleanup(() => {
295
+ stop();
296
+ session.destroy();
297
+ });
298
+ const toolbarProps = createMemo(() => {
299
+ state();
300
+ return session.toolbar(options().locale);
301
+ });
302
+ return {
303
+ controller: session.controller,
304
+ state,
305
+ canvasProps: { mount: session.mount },
306
+ toolbarProps,
307
+ inspectorProps: { controller: session.controller },
308
+ loading: () => state().loading,
309
+ error: () => state().error ?? state().edit.error,
310
+ mode: () => state().canEdit ? "edit" : "view"
311
+ };
312
+ }
313
+ function DiagramCanvas(props) {
314
+ let host;
315
+ createEffect(() => {
316
+ const mount = props.mount;
317
+ mount(host);
318
+ onCleanup(() => mount(null));
319
+ });
320
+ return ssr(_tmpl$, ssrHydrationKey() + ssrAttribute("class", escape(props.class, true), false));
321
+ }
322
+ function Toolbar(props) {
323
+ return ssr(_tmpl$2, ssrHydrationKey(), ssrAttribute("disabled", !props.canUndo, true), escape(props.labels.Undo), ssrAttribute("disabled", !props.canRedo, true), escape(props.labels.Redo), escape(props.labels["Fit to Window"]));
324
+ }
325
+ function PageNavigation(props) {
326
+ return ssr(_tmpl$3, ssrHydrationKey() + ssrAttribute("aria-label", escape(props.labels.Page, true), false) + ssrAttribute("value", escape(props.pageIndex, true), false), escape(createComponent(For, {
327
+ get each() {
328
+ return props.pages;
329
+ },
330
+ children: (page, index) => ssr(_tmpl$4, ssrHydrationKey() + ssrAttribute("value", escape(index(), true), false), escape(page.name))
331
+ })));
332
+ }
333
+ function Inspector(props) {
334
+ let host;
335
+ createEffect(() => {
336
+ const stop = buildingBlock("mountDiagramInspector")(host, props.controller);
337
+ onCleanup(stop);
338
+ });
339
+ return ssr(_tmpl$, ssrHydrationKey());
340
+ }
341
+ //#endregion
342
+ export { DiagramCanvas, Inspector, PageNavigation, Toolbar, VisioViewer, createViewerBuildingBlocks, createVisioViewerState };
@@ -0,0 +1,53 @@
1
+ import { ViewerController, type VsdxSource } from 'ooxml-ui/visio';
2
+ import * as ui from 'ooxml-ui/visio';
3
+ export interface BuildingBlockOptions {
4
+ content?: VsdxSource | undefined;
5
+ canEdit?: boolean | undefined;
6
+ locale?: string | undefined;
7
+ onDocumentChange?: ((document: NonNullable<ViewerController['state']['document']>) => void) | undefined;
8
+ onDirtyChange?: ((dirty: boolean) => void) | undefined;
9
+ onDocumentError?: ((error: Error) => void) | undefined;
10
+ }
11
+ type Api = Pick<typeof ui, 'mountDiagramCanvas' | 'fitDiagramCanvas' | 'mountDiagramInspector' | 'diagramToolbarProps'>;
12
+ export declare function buildingBlock<K extends keyof Api>(key: K): Api[K];
13
+ /** Framework-neutral ownership only; each adapter supplies its own reactive state. */
14
+ export declare function createBuildingBlockSession(): {
15
+ controller: ViewerController;
16
+ mount(node: HTMLElement | null): void;
17
+ toolbar(locale?: string | undefined): {
18
+ labels: {
19
+ [k: string]: string;
20
+ };
21
+ canEdit: boolean;
22
+ pageIndex: number;
23
+ pages: {
24
+ id: string;
25
+ name: string;
26
+ }[];
27
+ zoom: number;
28
+ selection: readonly import("ooxml-core/visio/ui").VisioShapeSelection[];
29
+ canUndo: boolean;
30
+ canRedo: boolean;
31
+ canDuplicate: boolean;
32
+ canGroup: boolean;
33
+ canUngroup: boolean;
34
+ onPageChange: (index: number) => void;
35
+ onZoomChange: (zoom: number) => void;
36
+ onFit: () => void;
37
+ onUndo: () => Promise<void>;
38
+ onRedo: () => Promise<void>;
39
+ onDuplicate: () => Promise<void>;
40
+ onGroup: () => Promise<void>;
41
+ onUngroup: () => Promise<void>;
42
+ onTextChange: (pageId: string, shapeId: string, text: string) => Promise<void>;
43
+ onSearchChange: (query: string) => void;
44
+ onNextSearchResult: () => void;
45
+ onPreviousSearchResult: () => void;
46
+ };
47
+ update(next: BuildingBlockOptions): void;
48
+ destroy(): void;
49
+ };
50
+ export type BuildingBlockSession = ReturnType<typeof createBuildingBlockSession>;
51
+ export type { DiagramToolbarProps, ViewerController } from 'ooxml-ui/visio';
52
+ /** Resolve the new optional-control theme API only when that control is mounted. */
53
+ export declare function prepareDiagramBlock(host: HTMLElement | null, kind: 'toolbar' | 'tabs'): void;
@@ -0,0 +1,55 @@
1
+ import { type Accessor } from 'solid-js';
2
+ import { type BuildingBlockOptions, type DiagramToolbarProps, type ViewerController } from '../../bindings/src/building-blocks.js';
3
+ export declare function createViewerBuildingBlocks(options?: Accessor<BuildingBlockOptions>): {
4
+ controller: ViewerController;
5
+ state: Accessor<import("ooxml-ui/visio").ViewerState>;
6
+ canvasProps: {
7
+ mount: (node: HTMLElement | null) => void;
8
+ };
9
+ toolbarProps: Accessor<{
10
+ labels: {
11
+ [k: string]: string;
12
+ };
13
+ canEdit: boolean;
14
+ pageIndex: number;
15
+ pages: {
16
+ id: string;
17
+ name: string;
18
+ }[];
19
+ zoom: number;
20
+ selection: readonly import("ooxml-ui/visio").VisioShapeSelection[];
21
+ canUndo: boolean;
22
+ canRedo: boolean;
23
+ canDuplicate: boolean;
24
+ canGroup: boolean;
25
+ canUngroup: boolean;
26
+ onPageChange: (index: number) => void;
27
+ onZoomChange: (zoom: number) => void;
28
+ onFit: () => void;
29
+ onUndo: () => Promise<void>;
30
+ onRedo: () => Promise<void>;
31
+ onDuplicate: () => Promise<void>;
32
+ onGroup: () => Promise<void>;
33
+ onUngroup: () => Promise<void>;
34
+ onTextChange: (pageId: string, shapeId: string, text: string) => Promise<void>;
35
+ onSearchChange: (query: string) => void;
36
+ onNextSearchResult: () => void;
37
+ onPreviousSearchResult: () => void;
38
+ }>;
39
+ inspectorProps: {
40
+ controller: ViewerController;
41
+ };
42
+ loading: () => boolean;
43
+ error: () => Error | null;
44
+ mode: () => "edit" | "view";
45
+ };
46
+ export declare function DiagramCanvas(props: {
47
+ mount(node: HTMLElement | null): void;
48
+ class?: string | undefined;
49
+ }): import("solid-js").JSX.Element;
50
+ export declare function Toolbar(props: DiagramToolbarProps): import("solid-js").JSX.Element;
51
+ export declare function PageNavigation(props: DiagramToolbarProps): import("solid-js").JSX.Element;
52
+ export declare function Inspector(props: {
53
+ controller: ViewerController;
54
+ }): import("solid-js").JSX.Element;
55
+ export type { BuildingBlockOptions, DiagramToolbarProps } from '../../bindings/src/building-blocks.js';
@@ -1,3 +1,4 @@
1
1
  export * from '../../../src/index.js';
2
2
  export * from 'visio-core';
3
3
  export * from '../../bindings/src/solid.js';
4
+ export * from './building-blocks.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "visio-solid-viewer",
3
- "version": "0.7.4",
3
+ "version": "0.8.0",
4
4
  "description": "Local-first Visio viewer for solid",
5
5
  "homepage": "https://christophervr.github.io/ooxml/visio/",
6
6
  "bugs": {
@@ -32,9 +32,9 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "emf-converter": "4.21.0",
35
- "ooxml-core": "^1.19.2",
36
- "ooxml-ui": "^1.18.3",
37
- "visio-core": "^0.4.1"
35
+ "ooxml-core": "^1.20.0",
36
+ "ooxml-ui": "^1.19.0",
37
+ "visio-core": "^0.5.0"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "solid-js": "^1.9.0"