visio-angular-viewer 0.8.0 → 1.0.1
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 +17 -5
- package/dist/index.js +218 -28
- package/dist/types/packages/angular/src/building-blocks.d.ts +62 -5
- package/dist/types/packages/bindings/src/angular.d.ts +3 -1
- package/dist/types/packages/bindings/src/building-blocks.d.ts +50 -8
- package/dist/types/packages/bindings/src/toolbar-controls.d.ts +41 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -52,22 +52,34 @@ export class DiagramComponent {
|
|
|
52
52
|
|
|
53
53
|
The shared properties are `document` (a parsed `VisioDocument`, or `null` to clear),
|
|
54
54
|
`pageIndex` (zero-based), `zoom` (`1` means 100%), `showToolbar`, `ribbonAddIns` (tabs the host adds
|
|
55
|
-
after Help)
|
|
56
|
-
|
|
57
|
-
`
|
|
55
|
+
after Help), `canEdit` (editing permission; the viewer is editable unless you pass `false`),
|
|
56
|
+
`collaboration` (shares the open drawing in a room while set; `null` leaves it) and `events`.
|
|
57
|
+
The callback map uses `document-load`, `document-change` (`{ document, dirty, kind }` with kind
|
|
58
|
+
`edit`, `undo`, `redo`, `remote` or `save`), `document-error`, `page-change`, `zoom-change`,
|
|
59
|
+
`shape-select`, `selection-change`, `office-ribbon-add-in` and `collaboration-change`. File loading uses the imperative
|
|
58
60
|
`load(File | Blob | Uint8Array | ArrayBuffer)` handle; `document` is not a file URL.
|
|
59
61
|
Provide a height for the viewer host. Mount and load files in the browser.
|
|
60
62
|
|
|
61
63
|
The handle supports `fit()`, `replacePlainText(pageId, shapeId, text)`,
|
|
62
64
|
`applyEdits(edits)`, `undo()`, `redo()` and `exportVsdx()`.
|
|
63
65
|
`exportVsdx()` returns `{ bytes, dirty, diagnostics }`; saving or downloading those
|
|
64
|
-
bytes is the application's responsibility
|
|
66
|
+
bytes is the application's responsibility, and `handle.controller.markClean()` declares them saved.
|
|
67
|
+
Sharing state is on the handle's element: `collaborationState`, `reconnectCollaboration()` and
|
|
68
|
+
`stopCollaboration()`. Load original bytes through `load()`
|
|
65
69
|
to enable source-backed editing and export. Handles are usable after mount.
|
|
66
70
|
|
|
67
71
|
Every viewer package includes the same browser UI and re-exports the
|
|
68
72
|
`visio-core` document API. Parsing and editing belong to `ooxml-core/visio`;
|
|
69
73
|
framework bindings only manage lifecycle, properties and events.
|
|
70
74
|
|
|
75
|
+
## Compose your own UI
|
|
76
|
+
|
|
77
|
+
To build a custom diagram UI instead of mounting the whole viewer, use the building blocks: `createViewerBuildingBlocks` and the `ViewerBuildingBlocks` class, with the standalone `DiagramCanvasComponent`, `ToolbarComponent`, `PageNavigationComponent`, `InspectorComponent` and `ShapesPaneComponent`. They expose signals
|
|
78
|
+
for the flat `toolbarProps` (page, zoom, selection, search, enabled flags and callbacks), `mode`,
|
|
79
|
+
`loading`/`error` and `markClean()`. Hosts pass `canEdit` (read-only until `true`) and optional
|
|
80
|
+
`onOpenSettings`/`onOpenShareDialog`/`onOpenPageSetup`/`onOpenFile` callbacks for dialogs they own.
|
|
81
|
+
See [composable building blocks](https://github.com/ChristopherVR/ooxml/blob/main/viewers/visio/docs/building-blocks.md).
|
|
82
|
+
|
|
71
83
|
## Limitations
|
|
72
84
|
|
|
73
85
|
Rendering and editing are beta features. Supported text and geometry edits are
|
|
@@ -77,7 +89,7 @@ Files stay in the browser unless your application sends them elsewhere.
|
|
|
77
89
|
|
|
78
90
|
## Documentation
|
|
79
91
|
|
|
80
|
-
[Viewer guide](https://christophervr.github.io/ooxml/visio/
|
|
92
|
+
[Viewer guide](https://christophervr.github.io/ooxml/visio/) |
|
|
81
93
|
[Demo](https://christophervr.github.io/ooxml/visio/demo/) |
|
|
82
94
|
[Source](https://github.com/ChristopherVR/ooxml/tree/main/viewers/visio)
|
|
83
95
|
|
package/dist/index.js
CHANGED
|
@@ -180,6 +180,7 @@ var VisioViewerComponent = class VisioViewerComponent {
|
|
|
180
180
|
this.shapeSelect = new EventEmitter();
|
|
181
181
|
this.selectionChange = new EventEmitter();
|
|
182
182
|
this.ribbonAddIn = new EventEmitter();
|
|
183
|
+
this.collaborationChange = new EventEmitter();
|
|
183
184
|
this.host = inject(ElementRef);
|
|
184
185
|
this.handle = viewerHandle(() => this.binding);
|
|
185
186
|
this.stopState = () => {};
|
|
@@ -193,7 +194,8 @@ var VisioViewerComponent = class VisioViewerComponent {
|
|
|
193
194
|
"zoom-change": this.zoomChange,
|
|
194
195
|
"shape-select": this.shapeSelect,
|
|
195
196
|
"selection-change": this.selectionChange,
|
|
196
|
-
"office-ribbon-add-in": this.ribbonAddIn
|
|
197
|
+
"office-ribbon-add-in": this.ribbonAddIn,
|
|
198
|
+
"collaboration-change": this.collaborationChange
|
|
197
199
|
};
|
|
198
200
|
}
|
|
199
201
|
options() {
|
|
@@ -291,6 +293,7 @@ __decorate([Input()], VisioViewerComponent.prototype, "zoom", void 0);
|
|
|
291
293
|
__decorate([Input()], VisioViewerComponent.prototype, "showToolbar", void 0);
|
|
292
294
|
__decorate([Input()], VisioViewerComponent.prototype, "canEdit", void 0);
|
|
293
295
|
__decorate([Input()], VisioViewerComponent.prototype, "ribbonAddIns", void 0);
|
|
296
|
+
__decorate([Input()], VisioViewerComponent.prototype, "collaboration", void 0);
|
|
294
297
|
__decorate([Input()], VisioViewerComponent.prototype, "events", void 0);
|
|
295
298
|
__decorate([Output()], VisioViewerComponent.prototype, "documentLoad", void 0);
|
|
296
299
|
__decorate([Output()], VisioViewerComponent.prototype, "documentChange", void 0);
|
|
@@ -300,6 +303,7 @@ __decorate([Output()], VisioViewerComponent.prototype, "zoomChange", void 0);
|
|
|
300
303
|
__decorate([Output()], VisioViewerComponent.prototype, "shapeSelect", void 0);
|
|
301
304
|
__decorate([Output()], VisioViewerComponent.prototype, "selectionChange", void 0);
|
|
302
305
|
__decorate([Output()], VisioViewerComponent.prototype, "ribbonAddIn", void 0);
|
|
306
|
+
__decorate([Output()], VisioViewerComponent.prototype, "collaborationChange", void 0);
|
|
303
307
|
VisioViewerComponent = __decorate([Component({
|
|
304
308
|
selector: "visio-viewer-host",
|
|
305
309
|
standalone: true,
|
|
@@ -312,10 +316,15 @@ function buildingBlock(key) {
|
|
|
312
316
|
if (typeof value !== "function") throw new Error("Update ooxml-ui to use Visio building blocks.");
|
|
313
317
|
return value;
|
|
314
318
|
}
|
|
319
|
+
/** The mode a host sees: 'edit' only when permission is granted and the file can be saved back. */
|
|
320
|
+
function effectiveMode(state) {
|
|
321
|
+
return buildingBlock("diagramMode")(state);
|
|
322
|
+
}
|
|
315
323
|
/** Framework-neutral ownership only; each adapter supplies its own reactive state. */
|
|
316
|
-
function createBuildingBlockSession() {
|
|
324
|
+
function createBuildingBlockSession(initial = {}) {
|
|
317
325
|
const controller = new ViewerController(createWorkerParser());
|
|
318
|
-
|
|
326
|
+
controller.canEdit = initial.canEdit === true;
|
|
327
|
+
let options = initial;
|
|
319
328
|
let source;
|
|
320
329
|
let host = null;
|
|
321
330
|
let disposeCanvas;
|
|
@@ -339,13 +348,23 @@ function createBuildingBlockSession() {
|
|
|
339
348
|
host = node;
|
|
340
349
|
if (node) disposeCanvas = buildingBlock("mountDiagramCanvas")(node, controller);
|
|
341
350
|
},
|
|
342
|
-
|
|
343
|
-
|
|
351
|
+
/** Flat control props; pass the framework's latest options so callbacks stay current. */
|
|
352
|
+
toolbar(latest = options) {
|
|
353
|
+
return buildingBlock("diagramToolbarProps")(controller, fit, latest.locale, {
|
|
354
|
+
onOpenFile: latest.onOpenFile,
|
|
355
|
+
onOpenPageSetup: latest.onOpenPageSetup,
|
|
356
|
+
onOpenSettings: latest.onOpenSettings,
|
|
357
|
+
onOpenShareDialog: latest.onOpenShareDialog
|
|
358
|
+
});
|
|
359
|
+
},
|
|
360
|
+
/** Acknowledge a host save: clears dirty and emits the dirty change. */
|
|
361
|
+
markClean() {
|
|
362
|
+
controller.markClean();
|
|
344
363
|
},
|
|
345
364
|
update(next) {
|
|
346
365
|
if (destroyed) return;
|
|
347
366
|
options = next;
|
|
348
|
-
controller.canEdit = next.canEdit
|
|
367
|
+
controller.canEdit = next.canEdit === true;
|
|
349
368
|
if (source === next.content) return;
|
|
350
369
|
source = next.content;
|
|
351
370
|
controller.cancelLoad();
|
|
@@ -374,25 +393,110 @@ function prepareDiagramBlock(host, kind) {
|
|
|
374
393
|
prepare(host, kind, "visio");
|
|
375
394
|
}
|
|
376
395
|
//#endregion
|
|
396
|
+
//#region packages/bindings/src/toolbar-controls.ts
|
|
397
|
+
var TOOLBAR_GROUPS = [
|
|
398
|
+
"file",
|
|
399
|
+
"history",
|
|
400
|
+
"zoom",
|
|
401
|
+
"selection",
|
|
402
|
+
"clipboard",
|
|
403
|
+
"tools",
|
|
404
|
+
"search"
|
|
405
|
+
];
|
|
406
|
+
var fire = (action) => () => {
|
|
407
|
+
Promise.resolve().then(action).catch(() => {});
|
|
408
|
+
};
|
|
409
|
+
var button = (id, label, enabled, action, pressed) => ({
|
|
410
|
+
kind: "button",
|
|
411
|
+
id,
|
|
412
|
+
label,
|
|
413
|
+
disabled: !enabled,
|
|
414
|
+
...pressed === void 0 ? {} : { pressed },
|
|
415
|
+
run: fire(action)
|
|
416
|
+
});
|
|
417
|
+
/**
|
|
418
|
+
* What the library Toolbar shows, as plain data every framework maps onto its own elements.
|
|
419
|
+
* Every control reflects `toolbarProps`: edit actions are disabled while the diagram is read-only,
|
|
420
|
+
* viewing actions (zoom, fit, search) never are, and a host dialog button appears only when the
|
|
421
|
+
* host supplied its callback.
|
|
422
|
+
*/
|
|
423
|
+
function toolbarGroups(props, include = TOOLBAR_GROUPS) {
|
|
424
|
+
const { labels } = props;
|
|
425
|
+
const has = props.pages.length > 0;
|
|
426
|
+
const groups = [];
|
|
427
|
+
const add = (id, controls) => {
|
|
428
|
+
if (include.includes(id) && controls.length) groups.push({
|
|
429
|
+
id,
|
|
430
|
+
controls
|
|
431
|
+
});
|
|
432
|
+
};
|
|
433
|
+
add("file", [
|
|
434
|
+
...props.onOpenFile ? [button("open", labels.Open, true, props.onOpenFile)] : [],
|
|
435
|
+
...props.onOpenPageSetup ? [button("page-setup", labels["Page Setup"], has, props.onOpenPageSetup)] : [],
|
|
436
|
+
...props.onOpenShareDialog ? [button("share", labels.Share, has, props.onOpenShareDialog)] : [],
|
|
437
|
+
...props.onOpenSettings ? [button("settings", labels.Options, true, props.onOpenSettings)] : []
|
|
438
|
+
]);
|
|
439
|
+
add("history", [button("undo", labels.Undo, props.canUndo, props.onUndo), button("redo", labels.Redo, props.canRedo, props.onRedo)]);
|
|
440
|
+
add("zoom", [
|
|
441
|
+
button("zoom-out", labels["Zoom Out"], props.canZoomOut, props.onZoomOut),
|
|
442
|
+
{
|
|
443
|
+
kind: "text",
|
|
444
|
+
id: "zoom",
|
|
445
|
+
text: `${Math.round(props.zoom * 100)}%`
|
|
446
|
+
},
|
|
447
|
+
button("zoom-in", labels["Zoom In"], props.canZoomIn, props.onZoomIn),
|
|
448
|
+
button("fit", labels["Fit to Window"], has, props.onFit)
|
|
449
|
+
]);
|
|
450
|
+
add("selection", [
|
|
451
|
+
button("duplicate", labels.Duplicate, props.canDuplicate, props.onDuplicate),
|
|
452
|
+
button("group", labels.Group, props.canGroup, props.onGroup),
|
|
453
|
+
button("ungroup", labels.Ungroup, props.canUngroup, props.onUngroup),
|
|
454
|
+
button("delete", labels.Delete, props.canDelete, props.onDelete)
|
|
455
|
+
]);
|
|
456
|
+
add("clipboard", [
|
|
457
|
+
button("cut", labels.Cut, props.canCut, props.onCut),
|
|
458
|
+
button("copy", labels.Copy, props.canCopy, props.onCopy),
|
|
459
|
+
button("paste", labels.Paste, props.canPaste, props.onPaste)
|
|
460
|
+
]);
|
|
461
|
+
add("tools", props.tools.map((tool) => button(`tool-${tool.id}`, tool.label, tool.id === "pointer" || props.canDraw, () => props.onToolChange(tool.id), tool.active)));
|
|
462
|
+
const { search } = props;
|
|
463
|
+
add("search", [{
|
|
464
|
+
kind: "search",
|
|
465
|
+
id: "search",
|
|
466
|
+
label: labels.Find,
|
|
467
|
+
query: search.query,
|
|
468
|
+
summary: search.query ? search.resultCount ? `${search.activeIndex >= 0 ? search.activeIndex + 1 : "-"} / ${search.resultCount}` : "0" : "",
|
|
469
|
+
previous: button("search-previous", labels["Previous Result"], search.resultCount > 0, props.onPreviousSearchResult),
|
|
470
|
+
next: button("search-next", labels["Next Result"], search.resultCount > 0, props.onNextSearchResult),
|
|
471
|
+
change: (query) => props.onSearchChange(query)
|
|
472
|
+
}]);
|
|
473
|
+
return groups;
|
|
474
|
+
}
|
|
475
|
+
//#endregion
|
|
377
476
|
//#region packages/angular/src/building-blocks.ts
|
|
378
477
|
var ViewerBuildingBlocks = class ViewerBuildingBlocks {
|
|
379
478
|
session = createBuildingBlockSession();
|
|
380
479
|
controller = this.session.controller;
|
|
381
480
|
current = signal(this.controller.state);
|
|
382
481
|
state = this.current.asReadonly();
|
|
383
|
-
|
|
482
|
+
latest = signal({});
|
|
384
483
|
toolbarProps = computed(() => {
|
|
385
484
|
this.state();
|
|
386
|
-
return this.session.toolbar(this.
|
|
485
|
+
return this.session.toolbar(this.latest());
|
|
387
486
|
});
|
|
388
487
|
loading = computed(() => this.state().loading);
|
|
389
488
|
error = computed(() => this.state().error ?? this.state().edit.error);
|
|
390
|
-
|
|
489
|
+
/** Effective permission: host `canEdit` and a drawing that can be saved back. */
|
|
490
|
+
mode = computed(() => effectiveMode(this.state()));
|
|
391
491
|
documentChange = new EventEmitter();
|
|
392
492
|
dirtyChange = new EventEmitter();
|
|
393
493
|
documentError = new EventEmitter();
|
|
394
494
|
canvasProps = { mount: (node) => this.session.mount(node) };
|
|
395
495
|
inspectorProps = { controller: this.controller };
|
|
496
|
+
shapesProps = computed(() => ({
|
|
497
|
+
controller: this.controller,
|
|
498
|
+
locale: this.latest().locale
|
|
499
|
+
}));
|
|
396
500
|
constructor() {
|
|
397
501
|
this.update({});
|
|
398
502
|
const stop = this.controller.subscribe((state) => this.current.set(state));
|
|
@@ -401,8 +505,12 @@ var ViewerBuildingBlocks = class ViewerBuildingBlocks {
|
|
|
401
505
|
this.session.destroy();
|
|
402
506
|
});
|
|
403
507
|
}
|
|
508
|
+
/** Acknowledge a host save: clears dirty and emits `dirtyChange(false)`. */
|
|
509
|
+
markClean() {
|
|
510
|
+
this.session.markClean();
|
|
511
|
+
}
|
|
404
512
|
update(options) {
|
|
405
|
-
this.
|
|
513
|
+
this.latest.set(options);
|
|
406
514
|
this.session.update({
|
|
407
515
|
...options,
|
|
408
516
|
onDocumentChange: (document) => {
|
|
@@ -433,8 +541,13 @@ function createViewerBuildingBlocks(options = () => ({})) {
|
|
|
433
541
|
var ViewerBuildingBlocksComponent = class ViewerBuildingBlocksComponent {
|
|
434
542
|
blocks = inject(ViewerBuildingBlocks);
|
|
435
543
|
content;
|
|
436
|
-
|
|
544
|
+
/** Host edit permission. Read-only until `true`. */
|
|
545
|
+
canEdit = false;
|
|
437
546
|
locale;
|
|
547
|
+
onOpenFile;
|
|
548
|
+
onOpenPageSetup;
|
|
549
|
+
onOpenSettings;
|
|
550
|
+
onOpenShareDialog;
|
|
438
551
|
documentChange = this.blocks.documentChange;
|
|
439
552
|
dirtyChange = this.blocks.dirtyChange;
|
|
440
553
|
documentError = this.blocks.documentError;
|
|
@@ -445,13 +558,21 @@ var ViewerBuildingBlocksComponent = class ViewerBuildingBlocksComponent {
|
|
|
445
558
|
this.blocks.update({
|
|
446
559
|
content: this.content,
|
|
447
560
|
canEdit: this.canEdit,
|
|
448
|
-
locale: this.locale
|
|
561
|
+
locale: this.locale,
|
|
562
|
+
onOpenFile: this.onOpenFile,
|
|
563
|
+
onOpenPageSetup: this.onOpenPageSetup,
|
|
564
|
+
onOpenSettings: this.onOpenSettings,
|
|
565
|
+
onOpenShareDialog: this.onOpenShareDialog
|
|
449
566
|
});
|
|
450
567
|
}
|
|
451
568
|
};
|
|
452
569
|
__decorate([Input()], ViewerBuildingBlocksComponent.prototype, "content", void 0);
|
|
453
570
|
__decorate([Input()], ViewerBuildingBlocksComponent.prototype, "canEdit", void 0);
|
|
454
571
|
__decorate([Input()], ViewerBuildingBlocksComponent.prototype, "locale", void 0);
|
|
572
|
+
__decorate([Input()], ViewerBuildingBlocksComponent.prototype, "onOpenFile", void 0);
|
|
573
|
+
__decorate([Input()], ViewerBuildingBlocksComponent.prototype, "onOpenPageSetup", void 0);
|
|
574
|
+
__decorate([Input()], ViewerBuildingBlocksComponent.prototype, "onOpenSettings", void 0);
|
|
575
|
+
__decorate([Input()], ViewerBuildingBlocksComponent.prototype, "onOpenShareDialog", void 0);
|
|
455
576
|
__decorate([Output()], ViewerBuildingBlocksComponent.prototype, "documentChange", void 0);
|
|
456
577
|
__decorate([Output()], ViewerBuildingBlocksComponent.prototype, "dirtyChange", void 0);
|
|
457
578
|
__decorate([Output()], ViewerBuildingBlocksComponent.prototype, "documentError", void 0);
|
|
@@ -478,30 +599,83 @@ DiagramCanvasComponent = __decorate([Component({
|
|
|
478
599
|
})], DiagramCanvasComponent);
|
|
479
600
|
var ToolbarComponent = class ToolbarComponent {
|
|
480
601
|
host = inject(ElementRef);
|
|
602
|
+
/** Groups to show; omit for all. Hosts may build their own controls from toolbarProps. */
|
|
603
|
+
include;
|
|
481
604
|
ngAfterViewInit() {
|
|
482
605
|
prepareDiagramBlock(this.host.nativeElement.querySelector("div"), "toolbar");
|
|
483
606
|
}
|
|
484
607
|
blocks = inject(ViewerBuildingBlocks);
|
|
608
|
+
groups() {
|
|
609
|
+
return toolbarGroups(this.blocks.toolbarProps(), this.include);
|
|
610
|
+
}
|
|
611
|
+
asText(control) {
|
|
612
|
+
return control;
|
|
613
|
+
}
|
|
614
|
+
asButton(control) {
|
|
615
|
+
return control;
|
|
616
|
+
}
|
|
617
|
+
asSearch(control) {
|
|
618
|
+
return control;
|
|
619
|
+
}
|
|
620
|
+
value(event) {
|
|
621
|
+
return event.target.value;
|
|
622
|
+
}
|
|
485
623
|
};
|
|
624
|
+
__decorate([Input()], ToolbarComponent.prototype, "include", void 0);
|
|
486
625
|
ToolbarComponent = __decorate([Component({
|
|
487
626
|
selector: "visio-diagram-toolbar",
|
|
488
627
|
standalone: true,
|
|
489
628
|
template: `<div role="toolbar">
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
629
|
+
@for (group of groups(); track group.id) {
|
|
630
|
+
<span [attr.data-group]="group.id">
|
|
631
|
+
@for (control of group.controls; track control.id) {
|
|
632
|
+
@switch (control.kind) {
|
|
633
|
+
@case ('text') {
|
|
634
|
+
<span [attr.data-control]="control.id">{{ asText(control).text }}</span>
|
|
635
|
+
}
|
|
636
|
+
@case ('button') {
|
|
637
|
+
<button
|
|
638
|
+
type="button"
|
|
639
|
+
[attr.data-control]="control.id"
|
|
640
|
+
[disabled]="asButton(control).disabled"
|
|
641
|
+
[attr.aria-pressed]="asButton(control).pressed ?? null"
|
|
642
|
+
(click)="asButton(control).run()"
|
|
643
|
+
>
|
|
644
|
+
{{ asButton(control).label }}
|
|
645
|
+
</button>
|
|
646
|
+
}
|
|
647
|
+
@default {
|
|
648
|
+
<span data-control="search">
|
|
649
|
+
<input
|
|
650
|
+
type="search"
|
|
651
|
+
[attr.aria-label]="asSearch(control).label"
|
|
652
|
+
[attr.placeholder]="asSearch(control).label"
|
|
653
|
+
[value]="asSearch(control).query"
|
|
654
|
+
(input)="asSearch(control).change(value($event))"
|
|
655
|
+
/>
|
|
656
|
+
<span aria-live="polite">{{ asSearch(control).summary }}</span>
|
|
657
|
+
<button
|
|
658
|
+
type="button"
|
|
659
|
+
[attr.data-control]="asSearch(control).previous.id"
|
|
660
|
+
[disabled]="asSearch(control).previous.disabled"
|
|
661
|
+
(click)="asSearch(control).previous.run()"
|
|
662
|
+
>
|
|
663
|
+
{{ asSearch(control).previous.label }}
|
|
664
|
+
</button>
|
|
665
|
+
<button
|
|
666
|
+
type="button"
|
|
667
|
+
[attr.data-control]="asSearch(control).next.id"
|
|
668
|
+
[disabled]="asSearch(control).next.disabled"
|
|
669
|
+
(click)="asSearch(control).next.run()"
|
|
670
|
+
>
|
|
671
|
+
{{ asSearch(control).next.label }}
|
|
672
|
+
</button>
|
|
673
|
+
</span>
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
</span>
|
|
678
|
+
}
|
|
505
679
|
</div>`
|
|
506
680
|
})], ToolbarComponent);
|
|
507
681
|
var PageNavigationComponent = class PageNavigationComponent {
|
|
@@ -543,5 +717,21 @@ InspectorComponent = __decorate([Component({
|
|
|
543
717
|
standalone: true,
|
|
544
718
|
template: ""
|
|
545
719
|
})], InspectorComponent);
|
|
720
|
+
var ShapesPaneComponent = class ShapesPaneComponent {
|
|
721
|
+
blocks = inject(ViewerBuildingBlocks);
|
|
722
|
+
host = inject(ElementRef);
|
|
723
|
+
stop;
|
|
724
|
+
ngAfterViewInit() {
|
|
725
|
+
this.stop = buildingBlock("mountDiagramShapes")(this.host.nativeElement, this.blocks.controller, { locale: this.blocks.shapesProps().locale });
|
|
726
|
+
}
|
|
727
|
+
ngOnDestroy() {
|
|
728
|
+
this.stop?.();
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
ShapesPaneComponent = __decorate([Component({
|
|
732
|
+
selector: "visio-shapes-pane",
|
|
733
|
+
standalone: true,
|
|
734
|
+
template: ""
|
|
735
|
+
})], ShapesPaneComponent);
|
|
546
736
|
//#endregion
|
|
547
|
-
export { DiagramCanvasComponent, InspectorComponent, PageNavigationComponent, ToolbarComponent, ViewerBuildingBlocks, ViewerBuildingBlocksComponent, VisioViewerComponent, createViewerBuildingBlocks };
|
|
737
|
+
export { DiagramCanvasComponent, InspectorComponent, PageNavigationComponent, ShapesPaneComponent, ToolbarComponent, ViewerBuildingBlocks, ViewerBuildingBlocksComponent, VisioViewerComponent, createViewerBuildingBlocks };
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { EventEmitter, type AfterViewInit, type OnChanges, type OnDestroy, type OnInit } from '@angular/core';
|
|
2
2
|
import { type BuildingBlockOptions } from '../../bindings/src/building-blocks.js';
|
|
3
|
+
import { type ToolbarButton, type ToolbarControl, type ToolbarGroupId, type ToolbarSearch, type ToolbarText } from '../../bindings/src/toolbar-controls.js';
|
|
3
4
|
export declare class ViewerBuildingBlocks {
|
|
4
5
|
private readonly session;
|
|
5
6
|
readonly controller: import("ooxml-ui/visio").ViewerController;
|
|
6
7
|
private readonly current;
|
|
7
8
|
readonly state: import("@angular/core").Signal<import("ooxml-ui/visio").ViewerState>;
|
|
8
|
-
private readonly
|
|
9
|
+
private readonly latest;
|
|
9
10
|
readonly toolbarProps: import("@angular/core").Signal<{
|
|
10
|
-
labels:
|
|
11
|
-
|
|
12
|
-
};
|
|
11
|
+
labels: Readonly<Record<"Arc" | "Connector" | "Copy" | "Cut" | "Delete" | "Duplicate" | "Ellipse" | "Find" | "Fit to Window" | "Freeform" | "Group" | "Line" | "Next Result" | "Open" | "Options" | "Page" | "Page Setup" | "Paste" | "Pencil" | "Pointer Tool" | "Previous Result" | "Rectangle" | "Redo" | "Shapes" | "Share" | "Text" | "Undo" | "Ungroup" | "Zoom In" | "Zoom Out", string>>;
|
|
12
|
+
mode: import("ooxml-ui/visio").DiagramMode;
|
|
13
13
|
canEdit: boolean;
|
|
14
14
|
pageIndex: number;
|
|
15
15
|
pages: {
|
|
@@ -23,21 +23,52 @@ export declare class ViewerBuildingBlocks {
|
|
|
23
23
|
canDuplicate: boolean;
|
|
24
24
|
canGroup: boolean;
|
|
25
25
|
canUngroup: boolean;
|
|
26
|
+
canDelete: boolean;
|
|
27
|
+
canCut: boolean;
|
|
28
|
+
canCopy: boolean;
|
|
29
|
+
canPaste: boolean;
|
|
30
|
+
canDraw: boolean;
|
|
31
|
+
canZoomIn: boolean;
|
|
32
|
+
canZoomOut: boolean;
|
|
33
|
+
tool: import("ooxml-ui/visio").DiagramTool;
|
|
34
|
+
tools: import("ooxml-ui/visio").DiagramToolSummary[];
|
|
35
|
+
search: {
|
|
36
|
+
query: string;
|
|
37
|
+
resultCount: number;
|
|
38
|
+
activeIndex: number;
|
|
39
|
+
truncated: boolean;
|
|
40
|
+
};
|
|
26
41
|
onPageChange: (index: number) => void;
|
|
27
42
|
onZoomChange: (zoom: number) => void;
|
|
43
|
+
onZoomIn: () => void;
|
|
44
|
+
onZoomOut: () => void;
|
|
28
45
|
onFit: () => void;
|
|
29
46
|
onUndo: () => Promise<void>;
|
|
30
47
|
onRedo: () => Promise<void>;
|
|
31
48
|
onDuplicate: () => Promise<void>;
|
|
32
49
|
onGroup: () => Promise<void>;
|
|
33
50
|
onUngroup: () => Promise<void>;
|
|
51
|
+
onDelete: () => Promise<void>;
|
|
52
|
+
onCut: () => Promise<void>;
|
|
53
|
+
onCopy: () => Promise<void>;
|
|
54
|
+
onPaste: () => Promise<void>;
|
|
55
|
+
onToolChange: (tool: import("ooxml-ui/visio").DiagramTool) => void;
|
|
34
56
|
onTextChange: (pageId: string, shapeId: string, text: string) => Promise<void>;
|
|
35
57
|
onSearchChange: (query: string) => void;
|
|
36
58
|
onNextSearchResult: () => void;
|
|
37
59
|
onPreviousSearchResult: () => void;
|
|
60
|
+
canOpenFile: boolean;
|
|
61
|
+
canOpenPageSetup: boolean;
|
|
62
|
+
canOpenSettings: boolean;
|
|
63
|
+
canOpenShareDialog: boolean;
|
|
64
|
+
onOpenFile?: () => void;
|
|
65
|
+
onOpenPageSetup?: () => void;
|
|
66
|
+
onOpenSettings?: () => void;
|
|
67
|
+
onOpenShareDialog?: () => void;
|
|
38
68
|
}>;
|
|
39
69
|
readonly loading: import("@angular/core").Signal<boolean>;
|
|
40
70
|
readonly error: import("@angular/core").Signal<Error | null>;
|
|
71
|
+
/** Effective permission: host `canEdit` and a drawing that can be saved back. */
|
|
41
72
|
readonly mode: import("@angular/core").Signal<"edit" | "view">;
|
|
42
73
|
readonly documentChange: EventEmitter<import("visio-core").VisioDocument>;
|
|
43
74
|
readonly dirtyChange: EventEmitter<boolean>;
|
|
@@ -48,7 +79,13 @@ export declare class ViewerBuildingBlocks {
|
|
|
48
79
|
readonly inspectorProps: {
|
|
49
80
|
controller: import("ooxml-ui/visio").ViewerController;
|
|
50
81
|
};
|
|
82
|
+
readonly shapesProps: import("@angular/core").Signal<{
|
|
83
|
+
controller: import("ooxml-ui/visio").ViewerController;
|
|
84
|
+
locale: string | undefined;
|
|
85
|
+
}>;
|
|
51
86
|
constructor();
|
|
87
|
+
/** Acknowledge a host save: clears dirty and emits `dirtyChange(false)`. */
|
|
88
|
+
markClean(): void;
|
|
52
89
|
update(options: BuildingBlockOptions): void;
|
|
53
90
|
}
|
|
54
91
|
/** Connect host signals to the scoped owner within an Angular injection context. */
|
|
@@ -56,8 +93,13 @@ export declare function createViewerBuildingBlocks(options?: () => BuildingBlock
|
|
|
56
93
|
export declare class ViewerBuildingBlocksComponent implements OnChanges, OnInit {
|
|
57
94
|
readonly blocks: ViewerBuildingBlocks;
|
|
58
95
|
content?: BuildingBlockOptions['content'];
|
|
96
|
+
/** Host edit permission. Read-only until `true`. */
|
|
59
97
|
canEdit: boolean;
|
|
60
98
|
locale?: string;
|
|
99
|
+
onOpenFile?: () => void;
|
|
100
|
+
onOpenPageSetup?: () => void;
|
|
101
|
+
onOpenSettings?: () => void;
|
|
102
|
+
onOpenShareDialog?: () => void;
|
|
61
103
|
readonly documentChange: EventEmitter<import("visio-core").VisioDocument>;
|
|
62
104
|
readonly dirtyChange: EventEmitter<boolean>;
|
|
63
105
|
readonly documentError: EventEmitter<Error>;
|
|
@@ -72,8 +114,15 @@ export declare class DiagramCanvasComponent implements AfterViewInit, OnDestroy
|
|
|
72
114
|
}
|
|
73
115
|
export declare class ToolbarComponent implements AfterViewInit {
|
|
74
116
|
private readonly host;
|
|
117
|
+
/** Groups to show; omit for all. Hosts may build their own controls from toolbarProps. */
|
|
118
|
+
include?: readonly ToolbarGroupId[];
|
|
75
119
|
ngAfterViewInit(): void;
|
|
76
120
|
readonly blocks: ViewerBuildingBlocks;
|
|
121
|
+
groups(): import("../../bindings/src/toolbar-controls.js").ToolbarGroup[];
|
|
122
|
+
asText(control: ToolbarControl): ToolbarText;
|
|
123
|
+
asButton(control: ToolbarControl): ToolbarButton;
|
|
124
|
+
asSearch(control: ToolbarControl): ToolbarSearch;
|
|
125
|
+
value(event: Event): string;
|
|
77
126
|
}
|
|
78
127
|
export declare class PageNavigationComponent implements AfterViewInit {
|
|
79
128
|
private readonly host;
|
|
@@ -88,4 +137,12 @@ export declare class InspectorComponent implements AfterViewInit, OnDestroy {
|
|
|
88
137
|
ngAfterViewInit(): void;
|
|
89
138
|
ngOnDestroy(): void;
|
|
90
139
|
}
|
|
91
|
-
export
|
|
140
|
+
export declare class ShapesPaneComponent implements AfterViewInit, OnDestroy {
|
|
141
|
+
readonly blocks: ViewerBuildingBlocks;
|
|
142
|
+
private readonly host;
|
|
143
|
+
private stop;
|
|
144
|
+
ngAfterViewInit(): void;
|
|
145
|
+
ngOnDestroy(): void;
|
|
146
|
+
}
|
|
147
|
+
export type { BuildingBlockOptions, BuildingBlockHostCallbacks, DiagramToolbarProps, } from '../../bindings/src/building-blocks.js';
|
|
148
|
+
export type { ToolbarGroupId } from '../../bindings/src/toolbar-controls.js';
|
|
@@ -8,12 +8,13 @@ export declare class VisioViewerComponent implements AfterViewInit, OnChanges, O
|
|
|
8
8
|
showToolbar?: boolean;
|
|
9
9
|
canEdit?: boolean;
|
|
10
10
|
ribbonAddIns?: ViewerProperties['ribbonAddIns'];
|
|
11
|
+
collaboration?: ViewerProperties['collaboration'];
|
|
11
12
|
events?: ViewerCallbacks;
|
|
12
13
|
documentLoad: EventEmitter<import("visio-core").VisioDocument>;
|
|
13
14
|
documentChange: EventEmitter<{
|
|
14
15
|
document: import("visio-core").VisioDocument;
|
|
15
16
|
dirty: boolean;
|
|
16
|
-
kind: "edit" | "undo" | "redo" | "remote";
|
|
17
|
+
kind: "edit" | "undo" | "redo" | "remote" | "save";
|
|
17
18
|
}>;
|
|
18
19
|
documentError: EventEmitter<Error>;
|
|
19
20
|
pageChange: EventEmitter<number>;
|
|
@@ -21,6 +22,7 @@ export declare class VisioViewerComponent implements AfterViewInit, OnChanges, O
|
|
|
21
22
|
shapeSelect: EventEmitter<VisioShapeSelection | null>;
|
|
22
23
|
selectionChange: EventEmitter<readonly VisioShapeSelection[]>;
|
|
23
24
|
ribbonAddIn: EventEmitter<import("ooxml-core/ribbon").RibbonAddInCommandDetail>;
|
|
25
|
+
collaborationChange: EventEmitter<import("ooxml-core/collab").CollaborationState>;
|
|
24
26
|
private readonly host;
|
|
25
27
|
private binding;
|
|
26
28
|
private readonly handle;
|
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
import { ViewerController, type VsdxSource } from 'ooxml-ui/visio';
|
|
2
2
|
import * as ui from 'ooxml-ui/visio';
|
|
3
|
-
|
|
3
|
+
/** Host-owned dialogs, as in the PowerPoint building blocks; omitted ones report not-enabled. */
|
|
4
|
+
export interface BuildingBlockHostCallbacks {
|
|
5
|
+
onOpenFile?: (() => void) | undefined;
|
|
6
|
+
onOpenPageSetup?: (() => void) | undefined;
|
|
7
|
+
onOpenSettings?: (() => void) | undefined;
|
|
8
|
+
onOpenShareDialog?: (() => void) | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface BuildingBlockOptions extends BuildingBlockHostCallbacks {
|
|
4
11
|
content?: VsdxSource | undefined;
|
|
12
|
+
/** Host edit permission. Read-only until it is `true`, so nothing is editable before the host says so. */
|
|
5
13
|
canEdit?: boolean | undefined;
|
|
6
14
|
locale?: string | undefined;
|
|
7
15
|
onDocumentChange?: ((document: NonNullable<ViewerController['state']['document']>) => void) | undefined;
|
|
8
16
|
onDirtyChange?: ((dirty: boolean) => void) | undefined;
|
|
9
17
|
onDocumentError?: ((error: Error) => void) | undefined;
|
|
10
18
|
}
|
|
11
|
-
type Api = Pick<typeof ui, 'mountDiagramCanvas' | 'fitDiagramCanvas' | 'mountDiagramInspector' | 'diagramToolbarProps'>;
|
|
19
|
+
type Api = Pick<typeof ui, 'mountDiagramCanvas' | 'fitDiagramCanvas' | 'mountDiagramInspector' | 'mountDiagramShapes' | 'diagramToolbarProps' | 'diagramMode'>;
|
|
12
20
|
export declare function buildingBlock<K extends keyof Api>(key: K): Api[K];
|
|
21
|
+
/** The mode a host sees: 'edit' only when permission is granted and the file can be saved back. */
|
|
22
|
+
export declare function effectiveMode(state: ViewerController['state']): 'edit' | 'view';
|
|
13
23
|
/** Framework-neutral ownership only; each adapter supplies its own reactive state. */
|
|
14
|
-
export declare function createBuildingBlockSession(): {
|
|
24
|
+
export declare function createBuildingBlockSession(initial?: BuildingBlockOptions): {
|
|
15
25
|
controller: ViewerController;
|
|
16
26
|
mount(node: HTMLElement | null): void;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
/** Flat control props; pass the framework's latest options so callbacks stay current. */
|
|
28
|
+
toolbar(latest?: BuildingBlockOptions): {
|
|
29
|
+
labels: Readonly<Record<"Arc" | "Connector" | "Copy" | "Cut" | "Delete" | "Duplicate" | "Ellipse" | "Find" | "Fit to Window" | "Freeform" | "Group" | "Line" | "Next Result" | "Open" | "Options" | "Page" | "Page Setup" | "Paste" | "Pencil" | "Pointer Tool" | "Previous Result" | "Rectangle" | "Redo" | "Shapes" | "Share" | "Text" | "Undo" | "Ungroup" | "Zoom In" | "Zoom Out", string>>;
|
|
30
|
+
mode: ui.DiagramMode;
|
|
21
31
|
canEdit: boolean;
|
|
22
32
|
pageIndex: number;
|
|
23
33
|
pages: {
|
|
@@ -31,23 +41,55 @@ export declare function createBuildingBlockSession(): {
|
|
|
31
41
|
canDuplicate: boolean;
|
|
32
42
|
canGroup: boolean;
|
|
33
43
|
canUngroup: boolean;
|
|
44
|
+
canDelete: boolean;
|
|
45
|
+
canCut: boolean;
|
|
46
|
+
canCopy: boolean;
|
|
47
|
+
canPaste: boolean;
|
|
48
|
+
canDraw: boolean;
|
|
49
|
+
canZoomIn: boolean;
|
|
50
|
+
canZoomOut: boolean;
|
|
51
|
+
tool: ui.DiagramTool;
|
|
52
|
+
tools: ui.DiagramToolSummary[];
|
|
53
|
+
search: {
|
|
54
|
+
query: string;
|
|
55
|
+
resultCount: number;
|
|
56
|
+
activeIndex: number;
|
|
57
|
+
truncated: boolean;
|
|
58
|
+
};
|
|
34
59
|
onPageChange: (index: number) => void;
|
|
35
60
|
onZoomChange: (zoom: number) => void;
|
|
61
|
+
onZoomIn: () => void;
|
|
62
|
+
onZoomOut: () => void;
|
|
36
63
|
onFit: () => void;
|
|
37
64
|
onUndo: () => Promise<void>;
|
|
38
65
|
onRedo: () => Promise<void>;
|
|
39
66
|
onDuplicate: () => Promise<void>;
|
|
40
67
|
onGroup: () => Promise<void>;
|
|
41
68
|
onUngroup: () => Promise<void>;
|
|
69
|
+
onDelete: () => Promise<void>;
|
|
70
|
+
onCut: () => Promise<void>;
|
|
71
|
+
onCopy: () => Promise<void>;
|
|
72
|
+
onPaste: () => Promise<void>;
|
|
73
|
+
onToolChange: (tool: ui.DiagramTool) => void;
|
|
42
74
|
onTextChange: (pageId: string, shapeId: string, text: string) => Promise<void>;
|
|
43
75
|
onSearchChange: (query: string) => void;
|
|
44
76
|
onNextSearchResult: () => void;
|
|
45
77
|
onPreviousSearchResult: () => void;
|
|
78
|
+
canOpenFile: boolean;
|
|
79
|
+
canOpenPageSetup: boolean;
|
|
80
|
+
canOpenSettings: boolean;
|
|
81
|
+
canOpenShareDialog: boolean;
|
|
82
|
+
onOpenFile?: () => void;
|
|
83
|
+
onOpenPageSetup?: () => void;
|
|
84
|
+
onOpenSettings?: () => void;
|
|
85
|
+
onOpenShareDialog?: () => void;
|
|
46
86
|
};
|
|
87
|
+
/** Acknowledge a host save: clears dirty and emits the dirty change. */
|
|
88
|
+
markClean(): void;
|
|
47
89
|
update(next: BuildingBlockOptions): void;
|
|
48
90
|
destroy(): void;
|
|
49
91
|
};
|
|
50
92
|
export type BuildingBlockSession = ReturnType<typeof createBuildingBlockSession>;
|
|
51
|
-
export type { DiagramToolbarProps, ViewerController } from 'ooxml-ui/visio';
|
|
93
|
+
export type { DiagramToolbarProps, DiagramToolbarLabels, DiagramToolbarHostCallbacks, DiagramMode, DiagramTool, ViewerController, } from 'ooxml-ui/visio';
|
|
52
94
|
/** Resolve the new optional-control theme API only when that control is mounted. */
|
|
53
95
|
export declare function prepareDiagramBlock(host: HTMLElement | null, kind: 'toolbar' | 'tabs'): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { DiagramToolbarProps } from 'ooxml-ui/visio';
|
|
2
|
+
/** The groups of the library toolbar; a host picks the ones it wants with `include`. */
|
|
3
|
+
export type ToolbarGroupId = 'file' | 'history' | 'zoom' | 'selection' | 'clipboard' | 'tools' | 'search';
|
|
4
|
+
export declare const TOOLBAR_GROUPS: readonly ToolbarGroupId[];
|
|
5
|
+
export interface ToolbarButton {
|
|
6
|
+
readonly kind: 'button';
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly label: string;
|
|
9
|
+
readonly disabled: boolean;
|
|
10
|
+
/** Set for toggles such as the drawing tools. */
|
|
11
|
+
readonly pressed?: boolean;
|
|
12
|
+
run(): void;
|
|
13
|
+
}
|
|
14
|
+
export interface ToolbarText {
|
|
15
|
+
readonly kind: 'text';
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly text: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ToolbarSearch {
|
|
20
|
+
readonly kind: 'search';
|
|
21
|
+
readonly id: 'search';
|
|
22
|
+
readonly label: string;
|
|
23
|
+
readonly query: string;
|
|
24
|
+
/** Result summary for assistive technology, e.g. "2 of 5". */
|
|
25
|
+
readonly summary: string;
|
|
26
|
+
readonly previous: ToolbarButton;
|
|
27
|
+
readonly next: ToolbarButton;
|
|
28
|
+
change(query: string): void;
|
|
29
|
+
}
|
|
30
|
+
export type ToolbarControl = ToolbarButton | ToolbarText | ToolbarSearch;
|
|
31
|
+
export interface ToolbarGroup {
|
|
32
|
+
readonly id: ToolbarGroupId;
|
|
33
|
+
readonly controls: readonly ToolbarControl[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* What the library Toolbar shows, as plain data every framework maps onto its own elements.
|
|
37
|
+
* Every control reflects `toolbarProps`: edit actions are disabled while the diagram is read-only,
|
|
38
|
+
* viewing actions (zoom, fit, search) never are, and a host dialog button appears only when the
|
|
39
|
+
* host supplied its callback.
|
|
40
|
+
*/
|
|
41
|
+
export declare function toolbarGroups(props: DiagramToolbarProps, include?: readonly ToolbarGroupId[]): ToolbarGroup[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "visio-angular-viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Local-first Visio viewer for angular",
|
|
5
5
|
"homepage": "https://christophervr.github.io/ooxml/visio/",
|
|
6
6
|
"bugs": {
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"emf-converter": "4.21.0",
|
|
34
|
-
"ooxml-core": "^1.
|
|
35
|
-
"ooxml-ui": "^1.
|
|
36
|
-
"visio-core": "^0.
|
|
34
|
+
"ooxml-core": "^1.23.0",
|
|
35
|
+
"ooxml-ui": "^1.22.0",
|
|
36
|
+
"visio-core": "^1.0.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@angular/compiler": "^22.2.3",
|