visio-angular-viewer 0.7.4 → 1.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 CHANGED
@@ -1,5 +1,7 @@
1
- import { eventKeys, mountViewer, propertyKeys } from "ooxml-ui/visio";
2
- import { Component, ElementRef, EventEmitter, Input, Output, inject, signal } from "@angular/core";
1
+ import * as ui from "ooxml-ui/visio";
2
+ import { ViewerController, createWorkerParser, eventKeys, mountViewer, propertyKeys } from "ooxml-ui/visio";
3
+ import { Component, DestroyRef, ElementRef, EventEmitter, Injectable, Input, Output, computed, effect, inject, signal, untracked } from "@angular/core";
4
+ import * as officeTheme from "ooxml-ui/theme";
3
5
  export * from "ooxml-ui/visio";
4
6
  export * from "visio-core";
5
7
  //#region packages/bindings/src/common.ts
@@ -178,6 +180,7 @@ var VisioViewerComponent = class VisioViewerComponent {
178
180
  this.shapeSelect = new EventEmitter();
179
181
  this.selectionChange = new EventEmitter();
180
182
  this.ribbonAddIn = new EventEmitter();
183
+ this.collaborationChange = new EventEmitter();
181
184
  this.host = inject(ElementRef);
182
185
  this.handle = viewerHandle(() => this.binding);
183
186
  this.stopState = () => {};
@@ -191,7 +194,8 @@ var VisioViewerComponent = class VisioViewerComponent {
191
194
  "zoom-change": this.zoomChange,
192
195
  "shape-select": this.shapeSelect,
193
196
  "selection-change": this.selectionChange,
194
- "office-ribbon-add-in": this.ribbonAddIn
197
+ "office-ribbon-add-in": this.ribbonAddIn,
198
+ "collaboration-change": this.collaborationChange
195
199
  };
196
200
  }
197
201
  options() {
@@ -287,7 +291,9 @@ __decorate([Input()], VisioViewerComponent.prototype, "document", void 0);
287
291
  __decorate([Input()], VisioViewerComponent.prototype, "pageIndex", void 0);
288
292
  __decorate([Input()], VisioViewerComponent.prototype, "zoom", void 0);
289
293
  __decorate([Input()], VisioViewerComponent.prototype, "showToolbar", void 0);
294
+ __decorate([Input()], VisioViewerComponent.prototype, "canEdit", void 0);
290
295
  __decorate([Input()], VisioViewerComponent.prototype, "ribbonAddIns", void 0);
296
+ __decorate([Input()], VisioViewerComponent.prototype, "collaboration", void 0);
291
297
  __decorate([Input()], VisioViewerComponent.prototype, "events", void 0);
292
298
  __decorate([Output()], VisioViewerComponent.prototype, "documentLoad", void 0);
293
299
  __decorate([Output()], VisioViewerComponent.prototype, "documentChange", void 0);
@@ -297,10 +303,435 @@ __decorate([Output()], VisioViewerComponent.prototype, "zoomChange", void 0);
297
303
  __decorate([Output()], VisioViewerComponent.prototype, "shapeSelect", void 0);
298
304
  __decorate([Output()], VisioViewerComponent.prototype, "selectionChange", void 0);
299
305
  __decorate([Output()], VisioViewerComponent.prototype, "ribbonAddIn", void 0);
306
+ __decorate([Output()], VisioViewerComponent.prototype, "collaborationChange", void 0);
300
307
  VisioViewerComponent = __decorate([Component({
301
308
  selector: "visio-viewer-host",
302
309
  standalone: true,
303
310
  template: ""
304
311
  })], VisioViewerComponent);
305
312
  //#endregion
306
- export { VisioViewerComponent };
313
+ //#region packages/bindings/src/building-blocks.ts
314
+ function buildingBlock(key) {
315
+ const value = Reflect.get(ui, key);
316
+ if (typeof value !== "function") throw new Error("Update ooxml-ui to use Visio building blocks.");
317
+ return value;
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
+ }
323
+ /** Framework-neutral ownership only; each adapter supplies its own reactive state. */
324
+ function createBuildingBlockSession(initial = {}) {
325
+ const controller = new ViewerController(createWorkerParser());
326
+ controller.canEdit = initial.canEdit === true;
327
+ let options = initial;
328
+ let source;
329
+ let host = null;
330
+ let disposeCanvas;
331
+ let destroyed = false;
332
+ const stopEvents = controller.onEvent((name, detail) => {
333
+ if (name === "document-change") {
334
+ const change = detail;
335
+ options.onDocumentChange?.(change.document);
336
+ options.onDirtyChange?.(change.dirty);
337
+ } else if (name === "document-error") options.onDocumentError?.(detail);
338
+ });
339
+ const fit = () => {
340
+ if (host) buildingBlock("fitDiagramCanvas")(host, controller);
341
+ };
342
+ return {
343
+ controller,
344
+ mount(node) {
345
+ if (destroyed || node === host) return;
346
+ disposeCanvas?.();
347
+ disposeCanvas = void 0;
348
+ host = node;
349
+ if (node) disposeCanvas = buildingBlock("mountDiagramCanvas")(node, controller);
350
+ },
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();
363
+ },
364
+ update(next) {
365
+ if (destroyed) return;
366
+ options = next;
367
+ controller.canEdit = next.canEdit === true;
368
+ if (source === next.content) return;
369
+ source = next.content;
370
+ controller.cancelLoad();
371
+ if (!source) return;
372
+ const input = source;
373
+ controller.loadSource(async () => {
374
+ return input instanceof Blob ? new Uint8Array(await input.arrayBuffer()) : input;
375
+ }).catch(() => {});
376
+ },
377
+ destroy() {
378
+ if (destroyed) return;
379
+ destroyed = true;
380
+ disposeCanvas?.();
381
+ disposeCanvas = void 0;
382
+ host = null;
383
+ stopEvents();
384
+ controller.destroy();
385
+ }
386
+ };
387
+ }
388
+ /** Resolve the new optional-control theme API only when that control is mounted. */
389
+ function prepareDiagramBlock(host, kind) {
390
+ if (!host) return;
391
+ const prepare = Reflect.get(officeTheme, "prepareOfficeUiBlock");
392
+ if (typeof prepare !== "function") throw new Error("Update ooxml-ui to theme Visio building blocks.");
393
+ prepare(host, kind, "visio");
394
+ }
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
476
+ //#region packages/angular/src/building-blocks.ts
477
+ var ViewerBuildingBlocks = class ViewerBuildingBlocks {
478
+ session = createBuildingBlockSession();
479
+ controller = this.session.controller;
480
+ current = signal(this.controller.state);
481
+ state = this.current.asReadonly();
482
+ latest = signal({});
483
+ toolbarProps = computed(() => {
484
+ this.state();
485
+ return this.session.toolbar(this.latest());
486
+ });
487
+ loading = computed(() => this.state().loading);
488
+ error = computed(() => this.state().error ?? this.state().edit.error);
489
+ /** Effective permission: host `canEdit` and a drawing that can be saved back. */
490
+ mode = computed(() => effectiveMode(this.state()));
491
+ documentChange = new EventEmitter();
492
+ dirtyChange = new EventEmitter();
493
+ documentError = new EventEmitter();
494
+ canvasProps = { mount: (node) => this.session.mount(node) };
495
+ inspectorProps = { controller: this.controller };
496
+ shapesProps = computed(() => ({
497
+ controller: this.controller,
498
+ locale: this.latest().locale
499
+ }));
500
+ constructor() {
501
+ this.update({});
502
+ const stop = this.controller.subscribe((state) => this.current.set(state));
503
+ inject(DestroyRef).onDestroy(() => {
504
+ stop();
505
+ this.session.destroy();
506
+ });
507
+ }
508
+ /** Acknowledge a host save: clears dirty and emits `dirtyChange(false)`. */
509
+ markClean() {
510
+ this.session.markClean();
511
+ }
512
+ update(options) {
513
+ this.latest.set(options);
514
+ this.session.update({
515
+ ...options,
516
+ onDocumentChange: (document) => {
517
+ this.documentChange.emit(document);
518
+ options.onDocumentChange?.(document);
519
+ },
520
+ onDirtyChange: (dirty) => {
521
+ this.dirtyChange.emit(dirty);
522
+ options.onDirtyChange?.(dirty);
523
+ },
524
+ onDocumentError: (error) => {
525
+ this.documentError.emit(error);
526
+ options.onDocumentError?.(error);
527
+ }
528
+ });
529
+ }
530
+ };
531
+ ViewerBuildingBlocks = __decorate([Injectable()], ViewerBuildingBlocks);
532
+ /** Connect host signals to the scoped owner within an Angular injection context. */
533
+ function createViewerBuildingBlocks(options = () => ({})) {
534
+ const blocks = inject(ViewerBuildingBlocks);
535
+ effect(() => {
536
+ const next = options();
537
+ untracked(() => blocks.update(next));
538
+ });
539
+ return blocks;
540
+ }
541
+ var ViewerBuildingBlocksComponent = class ViewerBuildingBlocksComponent {
542
+ blocks = inject(ViewerBuildingBlocks);
543
+ content;
544
+ /** Host edit permission. Read-only until `true`. */
545
+ canEdit = false;
546
+ locale;
547
+ onOpenFile;
548
+ onOpenPageSetup;
549
+ onOpenSettings;
550
+ onOpenShareDialog;
551
+ documentChange = this.blocks.documentChange;
552
+ dirtyChange = this.blocks.dirtyChange;
553
+ documentError = this.blocks.documentError;
554
+ ngOnInit() {
555
+ this.ngOnChanges();
556
+ }
557
+ ngOnChanges() {
558
+ this.blocks.update({
559
+ content: this.content,
560
+ canEdit: this.canEdit,
561
+ locale: this.locale,
562
+ onOpenFile: this.onOpenFile,
563
+ onOpenPageSetup: this.onOpenPageSetup,
564
+ onOpenSettings: this.onOpenSettings,
565
+ onOpenShareDialog: this.onOpenShareDialog
566
+ });
567
+ }
568
+ };
569
+ __decorate([Input()], ViewerBuildingBlocksComponent.prototype, "content", void 0);
570
+ __decorate([Input()], ViewerBuildingBlocksComponent.prototype, "canEdit", void 0);
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);
576
+ __decorate([Output()], ViewerBuildingBlocksComponent.prototype, "documentChange", void 0);
577
+ __decorate([Output()], ViewerBuildingBlocksComponent.prototype, "dirtyChange", void 0);
578
+ __decorate([Output()], ViewerBuildingBlocksComponent.prototype, "documentError", void 0);
579
+ ViewerBuildingBlocksComponent = __decorate([Component({
580
+ selector: "visio-building-blocks",
581
+ standalone: true,
582
+ template: "<ng-content />",
583
+ providers: [ViewerBuildingBlocks]
584
+ })], ViewerBuildingBlocksComponent);
585
+ var DiagramCanvasComponent = class DiagramCanvasComponent {
586
+ blocks = inject(ViewerBuildingBlocks);
587
+ host = inject(ElementRef);
588
+ ngAfterViewInit() {
589
+ this.blocks.canvasProps.mount(this.host.nativeElement);
590
+ }
591
+ ngOnDestroy() {
592
+ this.blocks.canvasProps.mount(null);
593
+ }
594
+ };
595
+ DiagramCanvasComponent = __decorate([Component({
596
+ selector: "visio-diagram-canvas",
597
+ standalone: true,
598
+ template: ""
599
+ })], DiagramCanvasComponent);
600
+ var ToolbarComponent = class ToolbarComponent {
601
+ host = inject(ElementRef);
602
+ /** Groups to show; omit for all. Hosts may build their own controls from toolbarProps. */
603
+ include;
604
+ ngAfterViewInit() {
605
+ prepareDiagramBlock(this.host.nativeElement.querySelector("div"), "toolbar");
606
+ }
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
+ }
623
+ };
624
+ __decorate([Input()], ToolbarComponent.prototype, "include", void 0);
625
+ ToolbarComponent = __decorate([Component({
626
+ selector: "visio-diagram-toolbar",
627
+ standalone: true,
628
+ template: `<div role="toolbar">
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
+ }
679
+ </div>`
680
+ })], ToolbarComponent);
681
+ var PageNavigationComponent = class PageNavigationComponent {
682
+ host = inject(ElementRef);
683
+ ngAfterViewInit() {
684
+ prepareDiagramBlock(this.host.nativeElement.querySelector("select"), "tabs");
685
+ }
686
+ blocks = inject(ViewerBuildingBlocks);
687
+ change(event) {
688
+ this.blocks.toolbarProps().onPageChange(Number(event.target.value));
689
+ }
690
+ };
691
+ PageNavigationComponent = __decorate([Component({
692
+ selector: "visio-page-navigation",
693
+ standalone: true,
694
+ template: `<select
695
+ [attr.aria-label]="blocks.toolbarProps().labels['Page']"
696
+ [value]="blocks.toolbarProps().pageIndex"
697
+ (change)="change($event)"
698
+ >
699
+ @for (page of blocks.toolbarProps().pages; track page.id; let index = $index) {
700
+ <option [value]="index">{{ page.name }}</option>
701
+ }
702
+ </select>`
703
+ })], PageNavigationComponent);
704
+ var InspectorComponent = class InspectorComponent {
705
+ blocks = inject(ViewerBuildingBlocks);
706
+ host = inject(ElementRef);
707
+ stop;
708
+ ngAfterViewInit() {
709
+ this.stop = buildingBlock("mountDiagramInspector")(this.host.nativeElement, this.blocks.controller);
710
+ }
711
+ ngOnDestroy() {
712
+ this.stop?.();
713
+ }
714
+ };
715
+ InspectorComponent = __decorate([Component({
716
+ selector: "visio-diagram-inspector",
717
+ standalone: true,
718
+ template: ""
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);
736
+ //#endregion
737
+ export { DiagramCanvasComponent, InspectorComponent, PageNavigationComponent, ShapesPaneComponent, ToolbarComponent, ViewerBuildingBlocks, ViewerBuildingBlocksComponent, VisioViewerComponent, createViewerBuildingBlocks };
@@ -0,0 +1,148 @@
1
+ import { EventEmitter, type AfterViewInit, type OnChanges, type OnDestroy, type OnInit } from '@angular/core';
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';
4
+ export declare class ViewerBuildingBlocks {
5
+ private readonly session;
6
+ readonly controller: import("ooxml-ui/visio").ViewerController;
7
+ private readonly current;
8
+ readonly state: import("@angular/core").Signal<import("ooxml-ui/visio").ViewerState>;
9
+ private readonly latest;
10
+ readonly toolbarProps: import("@angular/core").Signal<{
11
+ labels: Readonly<Record<"Arc" | "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" | "Undo" | "Ungroup" | "Zoom In" | "Zoom Out", string>>;
12
+ mode: import("ooxml-ui/visio").DiagramMode;
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
+ 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
+ };
41
+ onPageChange: (index: number) => void;
42
+ onZoomChange: (zoom: number) => void;
43
+ onZoomIn: () => void;
44
+ onZoomOut: () => void;
45
+ onFit: () => void;
46
+ onUndo: () => Promise<void>;
47
+ onRedo: () => Promise<void>;
48
+ onDuplicate: () => Promise<void>;
49
+ onGroup: () => Promise<void>;
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;
56
+ onTextChange: (pageId: string, shapeId: string, text: string) => Promise<void>;
57
+ onSearchChange: (query: string) => void;
58
+ onNextSearchResult: () => void;
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;
68
+ }>;
69
+ readonly loading: import("@angular/core").Signal<boolean>;
70
+ readonly error: import("@angular/core").Signal<Error | null>;
71
+ /** Effective permission: host `canEdit` and a drawing that can be saved back. */
72
+ readonly mode: import("@angular/core").Signal<"edit" | "view">;
73
+ readonly documentChange: EventEmitter<import("visio-core").VisioDocument>;
74
+ readonly dirtyChange: EventEmitter<boolean>;
75
+ readonly documentError: EventEmitter<Error>;
76
+ readonly canvasProps: {
77
+ mount: (node: HTMLElement | null) => void;
78
+ };
79
+ readonly inspectorProps: {
80
+ controller: import("ooxml-ui/visio").ViewerController;
81
+ };
82
+ readonly shapesProps: import("@angular/core").Signal<{
83
+ controller: import("ooxml-ui/visio").ViewerController;
84
+ locale: string | undefined;
85
+ }>;
86
+ constructor();
87
+ /** Acknowledge a host save: clears dirty and emits `dirtyChange(false)`. */
88
+ markClean(): void;
89
+ update(options: BuildingBlockOptions): void;
90
+ }
91
+ /** Connect host signals to the scoped owner within an Angular injection context. */
92
+ export declare function createViewerBuildingBlocks(options?: () => BuildingBlockOptions): ViewerBuildingBlocks;
93
+ export declare class ViewerBuildingBlocksComponent implements OnChanges, OnInit {
94
+ readonly blocks: ViewerBuildingBlocks;
95
+ content?: BuildingBlockOptions['content'];
96
+ /** Host edit permission. Read-only until `true`. */
97
+ canEdit: boolean;
98
+ locale?: string;
99
+ onOpenFile?: () => void;
100
+ onOpenPageSetup?: () => void;
101
+ onOpenSettings?: () => void;
102
+ onOpenShareDialog?: () => void;
103
+ readonly documentChange: EventEmitter<import("visio-core").VisioDocument>;
104
+ readonly dirtyChange: EventEmitter<boolean>;
105
+ readonly documentError: EventEmitter<Error>;
106
+ ngOnInit(): void;
107
+ ngOnChanges(): void;
108
+ }
109
+ export declare class DiagramCanvasComponent implements AfterViewInit, OnDestroy {
110
+ readonly blocks: ViewerBuildingBlocks;
111
+ private readonly host;
112
+ ngAfterViewInit(): void;
113
+ ngOnDestroy(): void;
114
+ }
115
+ export declare class ToolbarComponent implements AfterViewInit {
116
+ private readonly host;
117
+ /** Groups to show; omit for all. Hosts may build their own controls from toolbarProps. */
118
+ include?: readonly ToolbarGroupId[];
119
+ ngAfterViewInit(): void;
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;
126
+ }
127
+ export declare class PageNavigationComponent implements AfterViewInit {
128
+ private readonly host;
129
+ ngAfterViewInit(): void;
130
+ readonly blocks: ViewerBuildingBlocks;
131
+ change(event: Event): void;
132
+ }
133
+ export declare class InspectorComponent implements AfterViewInit, OnDestroy {
134
+ readonly blocks: ViewerBuildingBlocks;
135
+ private readonly host;
136
+ private stop;
137
+ ngAfterViewInit(): void;
138
+ ngOnDestroy(): void;
139
+ }
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';
@@ -1,3 +1,4 @@
1
1
  export * from '../../../src/index.js';
2
2
  export * from 'visio-core';
3
3
  export * from '../../bindings/src/angular.js';
4
+ export * from './building-blocks.js';
@@ -6,13 +6,15 @@ export declare class VisioViewerComponent implements AfterViewInit, OnChanges, O
6
6
  pageIndex?: number;
7
7
  zoom?: number;
8
8
  showToolbar?: boolean;
9
+ canEdit?: boolean;
9
10
  ribbonAddIns?: ViewerProperties['ribbonAddIns'];
11
+ collaboration?: ViewerProperties['collaboration'];
10
12
  events?: ViewerCallbacks;
11
13
  documentLoad: EventEmitter<import("visio-core").VisioDocument>;
12
14
  documentChange: EventEmitter<{
13
15
  document: import("visio-core").VisioDocument;
14
16
  dirty: boolean;
15
- kind: "edit" | "undo" | "redo" | "remote";
17
+ kind: "edit" | "undo" | "redo" | "remote" | "save";
16
18
  }>;
17
19
  documentError: EventEmitter<Error>;
18
20
  pageChange: EventEmitter<number>;
@@ -20,6 +22,7 @@ export declare class VisioViewerComponent implements AfterViewInit, OnChanges, O
20
22
  shapeSelect: EventEmitter<VisioShapeSelection | null>;
21
23
  selectionChange: EventEmitter<readonly VisioShapeSelection[]>;
22
24
  ribbonAddIn: EventEmitter<import("ooxml-core/ribbon").RibbonAddInCommandDetail>;
25
+ collaborationChange: EventEmitter<import("ooxml-core/collab").CollaborationState>;
23
26
  private readonly host;
24
27
  private binding;
25
28
  private readonly handle;
@@ -0,0 +1,95 @@
1
+ import { ViewerController, type VsdxSource } from 'ooxml-ui/visio';
2
+ import * as ui from 'ooxml-ui/visio';
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 {
11
+ content?: VsdxSource | undefined;
12
+ /** Host edit permission. Read-only until it is `true`, so nothing is editable before the host says so. */
13
+ canEdit?: boolean | undefined;
14
+ locale?: string | undefined;
15
+ onDocumentChange?: ((document: NonNullable<ViewerController['state']['document']>) => void) | undefined;
16
+ onDirtyChange?: ((dirty: boolean) => void) | undefined;
17
+ onDocumentError?: ((error: Error) => void) | undefined;
18
+ }
19
+ type Api = Pick<typeof ui, 'mountDiagramCanvas' | 'fitDiagramCanvas' | 'mountDiagramInspector' | 'mountDiagramShapes' | 'diagramToolbarProps' | 'diagramMode'>;
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';
23
+ /** Framework-neutral ownership only; each adapter supplies its own reactive state. */
24
+ export declare function createBuildingBlockSession(initial?: BuildingBlockOptions): {
25
+ controller: ViewerController;
26
+ mount(node: HTMLElement | null): void;
27
+ /** Flat control props; pass the framework's latest options so callbacks stay current. */
28
+ toolbar(latest?: BuildingBlockOptions): {
29
+ labels: Readonly<Record<"Arc" | "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" | "Undo" | "Ungroup" | "Zoom In" | "Zoom Out", string>>;
30
+ mode: ui.DiagramMode;
31
+ canEdit: boolean;
32
+ pageIndex: number;
33
+ pages: {
34
+ id: string;
35
+ name: string;
36
+ }[];
37
+ zoom: number;
38
+ selection: readonly import("ooxml-core/visio/ui").VisioShapeSelection[];
39
+ canUndo: boolean;
40
+ canRedo: boolean;
41
+ canDuplicate: boolean;
42
+ canGroup: boolean;
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
+ };
59
+ onPageChange: (index: number) => void;
60
+ onZoomChange: (zoom: number) => void;
61
+ onZoomIn: () => void;
62
+ onZoomOut: () => void;
63
+ onFit: () => void;
64
+ onUndo: () => Promise<void>;
65
+ onRedo: () => Promise<void>;
66
+ onDuplicate: () => Promise<void>;
67
+ onGroup: () => Promise<void>;
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;
74
+ onTextChange: (pageId: string, shapeId: string, text: string) => Promise<void>;
75
+ onSearchChange: (query: string) => void;
76
+ onNextSearchResult: () => void;
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;
86
+ };
87
+ /** Acknowledge a host save: clears dirty and emits the dirty change. */
88
+ markClean(): void;
89
+ update(next: BuildingBlockOptions): void;
90
+ destroy(): void;
91
+ };
92
+ export type BuildingBlockSession = ReturnType<typeof createBuildingBlockSession>;
93
+ export type { DiagramToolbarProps, DiagramToolbarLabels, DiagramToolbarHostCallbacks, DiagramMode, DiagramTool, ViewerController, } from 'ooxml-ui/visio';
94
+ /** Resolve the new optional-control theme API only when that control is mounted. */
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.7.4",
3
+ "version": "1.0.0",
4
4
  "description": "Local-first Visio viewer for angular",
5
5
  "homepage": "https://christophervr.github.io/ooxml/visio/",
6
6
  "bugs": {
@@ -31,11 +31,16 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "emf-converter": "4.21.0",
34
- "ooxml-core": "^1.19.2",
35
- "ooxml-ui": "^1.18.3",
36
- "visio-core": "^0.4.1"
34
+ "ooxml-core": "^1.22.0",
35
+ "ooxml-ui": "^1.21.0",
36
+ "visio-core": "^1.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "@angular/compiler": "^22.2.3",
40
+ "@angular/core": "^22.2.3",
41
+ "@angular/platform-browser": "^22.2.3"
37
42
  },
38
43
  "peerDependencies": {
39
- "@angular/core": "^21.0.0"
44
+ "@angular/core": "^21.0.0 || ^22.0.0"
40
45
  }
41
46
  }