orcasvn-react-diagrams 0.2.12 → 0.2.13

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +15 -11
  3. package/ai/api-contract.json +47 -0
  4. package/ai/invariants.json +6 -2
  5. package/ai/manifest.json +1 -1
  6. package/dist/cjs/examples.js +427 -48
  7. package/dist/cjs/index.js +226 -36
  8. package/dist/cjs/types/api/createDiagramEditor.d.ts +2 -1
  9. package/dist/cjs/types/api/types.d.ts +50 -0
  10. package/dist/cjs/types/displaybox/demos/deletionEventsDemo.d.ts +2 -0
  11. package/dist/cjs/types/displaybox/demos/focusElementDemo.d.ts +4 -0
  12. package/dist/cjs/types/engine/DiagramEngine.d.ts +9 -0
  13. package/dist/esm/examples.js +427 -48
  14. package/dist/esm/examples.js.map +1 -1
  15. package/dist/esm/index.js +226 -36
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/types/api/createDiagramEditor.d.ts +2 -1
  18. package/dist/esm/types/api/types.d.ts +50 -0
  19. package/dist/esm/types/displaybox/demos/deletionEventsDemo.d.ts +2 -0
  20. package/dist/esm/types/displaybox/demos/focusElementDemo.d.ts +4 -0
  21. package/dist/esm/types/engine/DiagramEngine.d.ts +9 -0
  22. package/dist/examples.d.ts +51 -0
  23. package/dist/index.d.ts +52 -1
  24. package/docs/API_CONTRACT.md +48 -1
  25. package/docs/ARCHITECTURE.md +9 -7
  26. package/docs/CAPABILITIES.md +3 -0
  27. package/docs/COMMANDS_EVENTS.md +15 -6
  28. package/docs/DOCUMENTATION_WORKFLOW.md +3 -1
  29. package/docs/INTEGRATION_PLAYBOOK.md +4 -0
  30. package/docs/PORTING_CHECKLIST.md +4 -1
  31. package/package.json +1 -1
  32. package/src/displaybox/demos/DeletionEventsDemoTab.tsx +167 -9
  33. package/src/displaybox/demos/deletionEventsDemo.ts +2 -2
  34. package/src/displaybox/demos/focusElementDemo.ts +91 -0
  35. package/src/displaybox/demos/index.tsx +2 -0
@@ -4386,7 +4386,7 @@ var createDeletionEventsState = function () { return ({
4386
4386
  var deletionEventsDemoConfig = {
4387
4387
  id: 'deletion-events',
4388
4388
  title: 'Deletion Events',
4389
- description: 'Inspect elementDeleted, portDeleted, linkDeleted, and textDeleted payloads for direct and cascade removals.',
4389
+ description: 'Inspect cancellable delete lifecycle events, post-delete payloads, and element double click in one integration-focused scenario.',
4390
4390
  createState: createDeletionEventsState,
4391
4391
  elementShapes: baseElementShapes,
4392
4392
  portShapes: basePortShapes,
@@ -8563,8 +8563,15 @@ var DiagramEngine = /** @class */ (function () {
8563
8563
  var _this = this;
8564
8564
  var _a;
8565
8565
  var existing = this.model.getElement(id);
8566
- var parentId = (_a = existing === null || existing === void 0 ? void 0 : existing.parentId) !== null && _a !== void 0 ? _a : null;
8566
+ if (!existing)
8567
+ return;
8568
+ var parentId = (_a = existing.parentId) !== null && _a !== void 0 ? _a : null;
8567
8569
  var before = this.model.toState();
8570
+ var root = { entity: 'element', id: id };
8571
+ var plannedRemoval = this.planRemoval(before, root);
8572
+ if (!plannedRemoval || this.emitDeletionLifecycleEvents(plannedRemoval.removed, root, 'direct')) {
8573
+ return;
8574
+ }
8568
8575
  this.commandQueue.run(createRemoveElementCommand(id), this.model);
8569
8576
  var removal = this.computeRemovalDiff(before);
8570
8577
  var allPatches = this.mutationPipeline.run({
@@ -8572,9 +8579,6 @@ var DiagramEngine = /** @class */ (function () {
8572
8579
  layoutSteps: [function () { return _this.applyLayoutCascade(parentId); }],
8573
8580
  });
8574
8581
  this.emitChange(allPatches);
8575
- if (existing) {
8576
- this.events.emit('elementDeleted', { elementId: id });
8577
- }
8578
8582
  this.emitEntityDeletionEvents(removal.removed);
8579
8583
  };
8580
8584
  DiagramEngine.prototype.addPortToElement = function (elementId, port) {
@@ -8655,7 +8659,14 @@ var DiagramEngine = /** @class */ (function () {
8655
8659
  }
8656
8660
  };
8657
8661
  DiagramEngine.prototype.removePort = function (id) {
8662
+ if (!this.model.getPort(id))
8663
+ return;
8658
8664
  var before = this.model.toState();
8665
+ var root = { entity: 'port', id: id };
8666
+ var plannedRemoval = this.planRemoval(before, root);
8667
+ if (!plannedRemoval || this.emitDeletionLifecycleEvents(plannedRemoval.removed, root, 'direct')) {
8668
+ return;
8669
+ }
8659
8670
  this.commandQueue.run(createRemovePortCommand(id), this.model);
8660
8671
  var removal = this.computeRemovalDiff(before);
8661
8672
  this.emitChange(removal.patches);
@@ -8749,7 +8760,14 @@ var DiagramEngine = /** @class */ (function () {
8749
8760
  }
8750
8761
  };
8751
8762
  DiagramEngine.prototype.removeLink = function (id) {
8763
+ if (!this.model.getLink(id))
8764
+ return;
8752
8765
  var before = this.model.toState();
8766
+ var root = { entity: 'link', id: id };
8767
+ var plannedRemoval = this.planRemoval(before, root);
8768
+ if (!plannedRemoval || this.emitDeletionLifecycleEvents(plannedRemoval.removed, root, 'direct')) {
8769
+ return;
8770
+ }
8753
8771
  this.commandQueue.run(createRemoveLinkCommand(id), this.model);
8754
8772
  var removal = this.computeRemovalDiff(before);
8755
8773
  this.emitChange(removal.patches);
@@ -8834,7 +8852,14 @@ var DiagramEngine = /** @class */ (function () {
8834
8852
  this.emitChange(patches);
8835
8853
  };
8836
8854
  DiagramEngine.prototype.removeText = function (id) {
8855
+ if (!this.model.getText(id))
8856
+ return;
8837
8857
  var before = this.model.toState();
8858
+ var root = { entity: 'text', id: id };
8859
+ var plannedRemoval = this.planRemoval(before, root);
8860
+ if (!plannedRemoval || this.emitDeletionLifecycleEvents(plannedRemoval.removed, root, 'direct')) {
8861
+ return;
8862
+ }
8838
8863
  this.commandQueue.run(createRemoveTextCommand(id), this.model);
8839
8864
  var removal = this.computeRemovalDiff(before);
8840
8865
  this.emitChange(removal.patches);
@@ -9029,40 +9054,22 @@ var DiagramEngine = /** @class */ (function () {
9029
9054
  if (selected.length === 0)
9030
9055
  return;
9031
9056
  var before = this.model.toState();
9032
- var elementIds = [];
9033
- selected.forEach(function (id) {
9034
- if (_this.model.getElement(id))
9035
- elementIds.push(id);
9036
- else if (_this.model.getPort(id))
9037
- ;
9038
- else if (_this.model.getLink(id))
9039
- ;
9040
- else if (_this.model.getText(id))
9041
- ;
9042
- });
9043
9057
  selected.forEach(function (id) {
9044
- if (_this.model.getElement(id)) {
9045
- _this.commandQueue.run(createRemoveElementCommand(id), _this.model);
9058
+ var root = _this.resolveDeletionRoot(id);
9059
+ if (!root)
9046
9060
  return;
9047
- }
9048
- if (_this.model.getPort(id)) {
9049
- _this.commandQueue.run(createRemovePortCommand(id), _this.model);
9050
- return;
9051
- }
9052
- if (_this.model.getLink(id)) {
9053
- _this.commandQueue.run(createRemoveLinkCommand(id), _this.model);
9061
+ var currentState = _this.model.toState();
9062
+ var plannedRemoval = _this.planRemoval(currentState, root);
9063
+ if (!plannedRemoval || _this.emitDeletionLifecycleEvents(plannedRemoval.removed, root, 'selection')) {
9054
9064
  return;
9055
9065
  }
9056
- if (_this.model.getText(id)) {
9057
- _this.commandQueue.run(createRemoveTextCommand(id), _this.model);
9058
- }
9066
+ _this.applyRemovalCommand(root);
9059
9067
  });
9060
9068
  var removal = this.computeRemovalDiff(before);
9069
+ if (removal.patches.length === 0)
9070
+ return;
9061
9071
  this.emitChange(removal.patches);
9062
9072
  this.emitEntityDeletionEvents(removal.removed);
9063
- elementIds.forEach(function (elementId) { return _this.events.emit('elementDeleted', { elementId: elementId }); });
9064
- this.selection.clear();
9065
- this.emitSelection();
9066
9073
  };
9067
9074
  DiagramEngine.prototype.setViewport = function (pan, zoom) {
9068
9075
  var hasPanChange = this.viewport.pan.x !== pan.x || this.viewport.pan.y !== pan.y;
@@ -9115,6 +9122,9 @@ var DiagramEngine = /** @class */ (function () {
9115
9122
  DiagramEngine.prototype.emitElementClick = function (elementId, pointer, isMulti) {
9116
9123
  this.events.emit('elementClick', { elementId: elementId, pointer: pointer, isMulti: isMulti });
9117
9124
  };
9125
+ DiagramEngine.prototype.emitElementDoubleClick = function (elementId, pointer, isMulti) {
9126
+ this.events.emit('elementDoubleClick', { elementId: elementId, pointer: pointer, isMulti: isMulti });
9127
+ };
9118
9128
  DiagramEngine.prototype.emitElementLinkStarted = function (event) {
9119
9129
  this.events.emit('elementLinkStarted', event);
9120
9130
  };
@@ -9758,8 +9768,8 @@ var DiagramEngine = /** @class */ (function () {
9758
9768
  });
9759
9769
  return { patches: normalizedPatches, movedPortIds: Array.from(movedPortIds) };
9760
9770
  };
9761
- DiagramEngine.prototype.computeRemovalDiff = function (before) {
9762
- var after = this.model.toState();
9771
+ DiagramEngine.prototype.computeRemovalDiff = function (before, after) {
9772
+ if (after === void 0) { after = this.model.toState(); }
9763
9773
  var removedPatches = [];
9764
9774
  var beforeElements = new Set(before.elements.map(function (element) { return element.id; }));
9765
9775
  var beforePorts = new Set(before.ports.map(function (port) { return port.id; }));
@@ -9769,9 +9779,13 @@ var DiagramEngine = /** @class */ (function () {
9769
9779
  var afterPorts = new Set(after.ports.map(function (port) { return port.id; }));
9770
9780
  var afterLinks = new Set(after.links.map(function (link) { return link.id; }));
9771
9781
  var afterTexts = new Set(after.texts.map(function (text) { return text.id; }));
9782
+ var removedElements = [];
9772
9783
  beforeElements.forEach(function (id) {
9773
9784
  if (!afterElements.has(id))
9774
9785
  removedPatches.push(patchRemove('element', id));
9786
+ var removedElement = before.elements.find(function (element) { return element.id === id; });
9787
+ if (!afterElements.has(id) && removedElement)
9788
+ removedElements.push(removedElement);
9775
9789
  });
9776
9790
  var removedPorts = [];
9777
9791
  beforePorts.forEach(function (id) {
@@ -9802,11 +9816,144 @@ var DiagramEngine = /** @class */ (function () {
9802
9816
  });
9803
9817
  return {
9804
9818
  patches: removedPatches,
9805
- removed: { ports: removedPorts, links: removedLinks, texts: removedTexts },
9819
+ removed: { elements: removedElements, ports: removedPorts, links: removedLinks, texts: removedTexts },
9806
9820
  };
9807
9821
  };
9822
+ DiagramEngine.prototype.resolveDeletionRoot = function (id) {
9823
+ if (this.model.getElement(id))
9824
+ return { entity: 'element', id: id };
9825
+ if (this.model.getPort(id))
9826
+ return { entity: 'port', id: id };
9827
+ if (this.model.getLink(id))
9828
+ return { entity: 'link', id: id };
9829
+ if (this.model.getText(id))
9830
+ return { entity: 'text', id: id };
9831
+ return null;
9832
+ };
9833
+ DiagramEngine.prototype.applyRemovalCommand = function (root) {
9834
+ if (root.entity === 'element') {
9835
+ this.commandQueue.run(createRemoveElementCommand(root.id), this.model);
9836
+ return;
9837
+ }
9838
+ if (root.entity === 'port') {
9839
+ this.commandQueue.run(createRemovePortCommand(root.id), this.model);
9840
+ return;
9841
+ }
9842
+ if (root.entity === 'link') {
9843
+ this.commandQueue.run(createRemoveLinkCommand(root.id), this.model);
9844
+ return;
9845
+ }
9846
+ this.commandQueue.run(createRemoveTextCommand(root.id), this.model);
9847
+ };
9848
+ DiagramEngine.prototype.planRemoval = function (before, root) {
9849
+ var tempModel = new DiagramModel();
9850
+ tempModel.load(before);
9851
+ if (root.entity === 'element') {
9852
+ tempModel.removeElement(root.id);
9853
+ }
9854
+ else if (root.entity === 'port') {
9855
+ tempModel.removePort(root.id);
9856
+ }
9857
+ else if (root.entity === 'link') {
9858
+ tempModel.removeLink(root.id);
9859
+ }
9860
+ else {
9861
+ tempModel.removeText(root.id);
9862
+ }
9863
+ return this.computeRemovalDiff(before, tempModel.toState());
9864
+ };
9865
+ DiagramEngine.prototype.emitDeletionLifecycleEvents = function (removed, root, rootTrigger) {
9866
+ for (var _i = 0, _a = removed.elements; _i < _a.length; _i++) {
9867
+ var element = _a[_i];
9868
+ var trigger = root.entity === 'element' && element.id === root.id ? rootTrigger : 'cascade';
9869
+ if (this.emitElementDeleting({
9870
+ elementId: element.id,
9871
+ trigger: trigger,
9872
+ root: root,
9873
+ })) {
9874
+ return true;
9875
+ }
9876
+ }
9877
+ for (var _b = 0, _c = removed.ports; _b < _c.length; _b++) {
9878
+ var port = _c[_b];
9879
+ var trigger = root.entity === 'port' && port.id === root.id ? rootTrigger : 'cascade';
9880
+ if (this.emitPortDeleting({
9881
+ portId: port.id,
9882
+ elementId: port.elementId,
9883
+ trigger: trigger,
9884
+ root: root,
9885
+ })) {
9886
+ return true;
9887
+ }
9888
+ }
9889
+ for (var _d = 0, _e = removed.links; _d < _e.length; _d++) {
9890
+ var link = _e[_d];
9891
+ var trigger = root.entity === 'link' && link.id === root.id ? rootTrigger : 'cascade';
9892
+ if (this.emitLinkDeleting({
9893
+ linkId: link.id,
9894
+ sourcePortId: link.sourcePortId,
9895
+ targetPortId: link.targetPortId,
9896
+ trigger: trigger,
9897
+ root: root,
9898
+ })) {
9899
+ return true;
9900
+ }
9901
+ }
9902
+ for (var _f = 0, _g = removed.texts; _f < _g.length; _f++) {
9903
+ var text = _g[_f];
9904
+ var trigger = root.entity === 'text' && text.id === root.id ? rootTrigger : 'cascade';
9905
+ if (this.emitTextDeleting({
9906
+ textId: text.id,
9907
+ ownerId: text.ownerId,
9908
+ trigger: trigger,
9909
+ root: root,
9910
+ })) {
9911
+ return true;
9912
+ }
9913
+ }
9914
+ return false;
9915
+ };
9916
+ DiagramEngine.prototype.emitElementDeleting = function (event) {
9917
+ var cancelled = false;
9918
+ var payload = __assign(__assign({}, event), { cancelled: false, cancel: function () {
9919
+ cancelled = true;
9920
+ payload.cancelled = true;
9921
+ } });
9922
+ this.events.emit('elementDeleting', payload);
9923
+ return cancelled || payload.cancelled;
9924
+ };
9925
+ DiagramEngine.prototype.emitPortDeleting = function (event) {
9926
+ var cancelled = false;
9927
+ var payload = __assign(__assign({}, event), { cancelled: false, cancel: function () {
9928
+ cancelled = true;
9929
+ payload.cancelled = true;
9930
+ } });
9931
+ this.events.emit('portDeleting', payload);
9932
+ return cancelled || payload.cancelled;
9933
+ };
9934
+ DiagramEngine.prototype.emitLinkDeleting = function (event) {
9935
+ var cancelled = false;
9936
+ var payload = __assign(__assign({}, event), { cancelled: false, cancel: function () {
9937
+ cancelled = true;
9938
+ payload.cancelled = true;
9939
+ } });
9940
+ this.events.emit('linkDeleting', payload);
9941
+ return cancelled || payload.cancelled;
9942
+ };
9943
+ DiagramEngine.prototype.emitTextDeleting = function (event) {
9944
+ var cancelled = false;
9945
+ var payload = __assign(__assign({}, event), { cancelled: false, cancel: function () {
9946
+ cancelled = true;
9947
+ payload.cancelled = true;
9948
+ } });
9949
+ this.events.emit('textDeleting', payload);
9950
+ return cancelled || payload.cancelled;
9951
+ };
9808
9952
  DiagramEngine.prototype.emitEntityDeletionEvents = function (removed) {
9809
9953
  var _this = this;
9954
+ removed.elements.forEach(function (element) {
9955
+ _this.events.emit('elementDeleted', { elementId: element.id });
9956
+ });
9810
9957
  removed.ports.forEach(function (port) {
9811
9958
  _this.events.emit('portDeleted', { portId: port.id, elementId: port.elementId });
9812
9959
  });
@@ -11645,15 +11792,29 @@ var KonvaInteraction = /** @class */ (function () {
11645
11792
  this.stage.on(upEvents, pointerUp);
11646
11793
  this.handlers.push([upEvents, pointerUp]);
11647
11794
  var doubleClick = function () {
11795
+ var args = [];
11796
+ for (var _i = 0; _i < arguments.length; _i++) {
11797
+ args[_i] = arguments[_i];
11798
+ }
11799
+ var event = args[0];
11800
+ var nativeEvent = event === null || event === void 0 ? void 0 : event.evt;
11648
11801
  var point = _this.getPointerPosition();
11649
11802
  if (!point)
11650
11803
  return;
11651
11804
  var hit = _this.resolveHit(point);
11652
- if (!hit || hit.type !== 'text')
11805
+ if (!hit)
11653
11806
  return;
11654
- if (!_this.isTextEditable(hit.id))
11807
+ if (hit.type === 'text') {
11808
+ if (!_this.isTextEditable(hit.id))
11809
+ return;
11810
+ _this.startTextEdit(hit.id);
11811
+ return;
11812
+ }
11813
+ if (hit.type !== 'element')
11655
11814
  return;
11656
- _this.startTextEdit(hit.id);
11815
+ var isMulti = Boolean(nativeEvent && (nativeEvent.ctrlKey || nativeEvent.metaKey || nativeEvent.shiftKey));
11816
+ var pointerInfo = _this.buildPointerInfo(point, nativeEvent);
11817
+ _this.engine.emitElementDoubleClick(hit.id, pointerInfo, isMulti);
11657
11818
  };
11658
11819
  this.stage.on('dblclick dbltap', doubleClick);
11659
11820
  this.handlers.push(['dblclick dbltap', doubleClick]);
@@ -14171,6 +14332,29 @@ var resolveViewportFitTransform = function (state, viewportSize, options) {
14171
14332
  };
14172
14333
  return { pan: pan, zoom: zoom };
14173
14334
  };
14335
+ var resolveFocusElementTransform = function (state, elementId, viewportSize, currentZoom, options) {
14336
+ var elementsById = new Map(state.elements.map(function (element) { return [element.id, element]; }));
14337
+ var element = elementsById.get(elementId);
14338
+ if (!element)
14339
+ return null;
14340
+ var position = resolveElementWorldPositionInState(elementId, elementsById);
14341
+ if (!position)
14342
+ return null;
14343
+ var safeWidth = Math.max(1, viewportSize.width);
14344
+ var safeHeight = Math.max(1, viewportSize.height);
14345
+ var requestedZoom = options === null || options === void 0 ? void 0 : options.zoom;
14346
+ var zoom = typeof requestedZoom === 'number' && Number.isFinite(requestedZoom)
14347
+ ? clamp(requestedZoom, DEFAULT_VIEWPORT_FIT_MIN_ZOOM, DEFAULT_VIEWPORT_FIT_MAX_ZOOM)
14348
+ : currentZoom;
14349
+ var safeZoom = zoom === 0 ? 1 : zoom;
14350
+ var centerX = position.x + element.size.width / 2;
14351
+ var centerY = position.y + element.size.height / 2;
14352
+ var pan = {
14353
+ x: safeWidth / 2 - centerX * safeZoom,
14354
+ y: safeHeight / 2 - centerY * safeZoom,
14355
+ };
14356
+ return { pan: pan, zoom: zoom };
14357
+ };
14174
14358
  var resolveFitToContentPadding = function (fitToContent) {
14175
14359
  if (typeof fitToContent === 'object' && typeof fitToContent.padding === 'number') {
14176
14360
  return Math.max(0, fitToContent.padding);
@@ -14405,6 +14589,12 @@ var createDiagramEditor = function (config) {
14405
14589
  return;
14406
14590
  engine.setViewport(transform.pan, transform.zoom);
14407
14591
  },
14592
+ focusElement: function (elementId, options) {
14593
+ var transform = resolveFocusElementTransform(engine.getState(), elementId, { width: stageWidth, height: stageHeight }, engine.getViewport().zoom, options);
14594
+ if (!transform)
14595
+ return;
14596
+ engine.setViewport(transform.pan, transform.zoom);
14597
+ },
14408
14598
  exportImage: function (options) {
14409
14599
  engine.render();
14410
14600
  if (typeof stage.toDataURL !== 'function') {
@@ -16434,10 +16624,21 @@ var ZoomToFitElementsDemo = function () { return (React.createElement(SimpleDemo
16434
16624
  React.createElement("li", null, "Use `Run Zoom-To-Fit (Padding 48)` to confirm optional padding keeps extra breathing room around content.")))) })); };
16435
16625
 
16436
16626
  var initialPayloads = {
16627
+ elementDeleting: null,
16437
16628
  elementDeleted: null,
16629
+ portDeleting: null,
16438
16630
  portDeleted: null,
16631
+ linkDeleting: null,
16439
16632
  linkDeleted: null,
16633
+ textDeleting: null,
16440
16634
  textDeleted: null,
16635
+ elementDoubleClick: null,
16636
+ };
16637
+ var initialCancelFlags = {
16638
+ element: false,
16639
+ port: false,
16640
+ link: false,
16641
+ text: false,
16441
16642
  };
16442
16643
  var eventBoxStyle = {
16443
16644
  width: '100%',
@@ -16470,21 +16671,77 @@ var DeletionEventsDemo = function () {
16470
16671
  actionHelpers: actionHelpers,
16471
16672
  });
16472
16673
  var _f = React.useState(initialPayloads), payloads = _f[0], setPayloads = _f[1];
16674
+ var _g = React.useState(initialCancelFlags), cancelFlags = _g[0], setCancelFlags = _g[1];
16675
+ var _h = React.useState([]), eventLog = _h[0], setEventLog = _h[1];
16676
+ var appendLog = React.useCallback(function (eventName, payload) {
16677
+ setEventLog(function (prev) { return __spreadArray([
16678
+ "".concat(eventName, ": ").concat(JSON.stringify(payload))
16679
+ ], prev, true).slice(0, 18); });
16680
+ }, []);
16473
16681
  React.useEffect(function () {
16474
16682
  var editor = editorRef.current;
16475
16683
  if (!editor)
16476
16684
  return undefined;
16477
16685
  setPayloads(initialPayloads);
16686
+ setEventLog([]);
16478
16687
  var unsubs = [
16479
- editor.on('elementDeleted', function (payload) { return setPayloads(function (prev) { return (__assign(__assign({}, prev), { elementDeleted: payload })); }); }),
16480
- editor.on('portDeleted', function (payload) { return setPayloads(function (prev) { return (__assign(__assign({}, prev), { portDeleted: payload })); }); }),
16481
- editor.on('linkDeleted', function (payload) { return setPayloads(function (prev) { return (__assign(__assign({}, prev), { linkDeleted: payload })); }); }),
16482
- editor.on('textDeleted', function (payload) { return setPayloads(function (prev) { return (__assign(__assign({}, prev), { textDeleted: payload })); }); }),
16688
+ editor.on('elementDeleting', function (payload) {
16689
+ if (cancelFlags.element) {
16690
+ payload.cancel();
16691
+ }
16692
+ setPayloads(function (prev) { return (__assign(__assign({}, prev), { elementDeleting: payload })); });
16693
+ appendLog('elementDeleting', payload);
16694
+ }),
16695
+ editor.on('elementDeleted', function (payload) {
16696
+ setPayloads(function (prev) { return (__assign(__assign({}, prev), { elementDeleted: payload })); });
16697
+ appendLog('elementDeleted', payload);
16698
+ }),
16699
+ editor.on('portDeleting', function (payload) {
16700
+ if (cancelFlags.port) {
16701
+ payload.cancel();
16702
+ }
16703
+ setPayloads(function (prev) { return (__assign(__assign({}, prev), { portDeleting: payload })); });
16704
+ appendLog('portDeleting', payload);
16705
+ }),
16706
+ editor.on('portDeleted', function (payload) {
16707
+ setPayloads(function (prev) { return (__assign(__assign({}, prev), { portDeleted: payload })); });
16708
+ appendLog('portDeleted', payload);
16709
+ }),
16710
+ editor.on('linkDeleting', function (payload) {
16711
+ if (cancelFlags.link) {
16712
+ payload.cancel();
16713
+ }
16714
+ setPayloads(function (prev) { return (__assign(__assign({}, prev), { linkDeleting: payload })); });
16715
+ appendLog('linkDeleting', payload);
16716
+ }),
16717
+ editor.on('linkDeleted', function (payload) {
16718
+ setPayloads(function (prev) { return (__assign(__assign({}, prev), { linkDeleted: payload })); });
16719
+ appendLog('linkDeleted', payload);
16720
+ }),
16721
+ editor.on('textDeleting', function (payload) {
16722
+ if (cancelFlags.text) {
16723
+ payload.cancel();
16724
+ }
16725
+ setPayloads(function (prev) { return (__assign(__assign({}, prev), { textDeleting: payload })); });
16726
+ appendLog('textDeleting', payload);
16727
+ }),
16728
+ editor.on('textDeleted', function (payload) {
16729
+ setPayloads(function (prev) { return (__assign(__assign({}, prev), { textDeleted: payload })); });
16730
+ appendLog('textDeleted', payload);
16731
+ }),
16732
+ editor.on('elementDoubleClick', function (payload) {
16733
+ setPayloads(function (prev) { return (__assign(__assign({}, prev), { elementDoubleClick: payload })); });
16734
+ appendLog('elementDoubleClick', payload);
16735
+ }),
16483
16736
  ];
16484
16737
  return function () {
16485
- unsubs.forEach(function (unsub) { return unsub(); });
16738
+ unsubs.forEach(function (unsub) {
16739
+ if (typeof unsub === 'function') {
16740
+ unsub();
16741
+ }
16742
+ });
16486
16743
  };
16487
- }, [editorRef]);
16744
+ }, [appendLog, cancelFlags, editorRef]);
16488
16745
  var hasPort = ((_a = diagramState === null || diagramState === void 0 ? void 0 : diagramState.ports) !== null && _a !== void 0 ? _a : []).some(function (port) { return port.id === 'delete-source-free-port'; });
16489
16746
  var hasLink = ((_b = diagramState === null || diagramState === void 0 ? void 0 : diagramState.links) !== null && _b !== void 0 ? _b : []).some(function (link) { return link.id === 'delete-link'; });
16490
16747
  var hasText = ((_c = diagramState === null || diagramState === void 0 ? void 0 : diagramState.texts) !== null && _c !== void 0 ? _c : []).some(function (text) { return text.id === 'delete-free-text'; });
@@ -16493,29 +16750,67 @@ var DeletionEventsDemo = function () {
16493
16750
  React.createElement("div", { style: { marginBottom: 12 } },
16494
16751
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
16495
16752
  React.createElement("p", { style: { marginTop: 0 } },
16496
- "Use direct delete buttons for port/link/text, then delete ",
16753
+ "Use the direct delete buttons for port/link/text, then delete ",
16497
16754
  React.createElement("code", null, "delete-source"),
16498
- " to trigger cascade removals. Latest payloads are shown below for integration debugging.")),
16755
+ " to trigger cascades. Turn cancellation toggles on to veto deletes before commit, double click an element body to log",
16756
+ React.createElement("code", null, " elementDoubleClick"),
16757
+ ", and use the batch-selection helper before pressing Delete to verify partial success.")),
16499
16758
  React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
16500
16759
  React.createElement("div", { style: { display: 'grid', gap: 12, marginBottom: 12 } },
16501
16760
  React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
16502
16761
  React.createElement("button", { type: "button", onClick: function () { var _a; return (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.removePort('delete-source-free-port'); }, disabled: !hasPort }, "Delete Port"),
16503
16762
  React.createElement("button", { type: "button", onClick: function () { var _a; return (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.removeLink('delete-link'); }, disabled: !hasLink }, "Delete Link"),
16504
16763
  React.createElement("button", { type: "button", onClick: function () { var _a; return (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.removeText('delete-free-text'); }, disabled: !hasText }, "Delete Text"),
16505
- React.createElement("button", { type: "button", onClick: function () { var _a; return (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.removeElement('delete-source'); }, disabled: !hasCascadeElement }, "Delete Element (Cascade)")),
16506
- React.createElement("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12 } },
16764
+ React.createElement("button", { type: "button", onClick: function () { var _a; return (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.removeElement('delete-source'); }, disabled: !hasCascadeElement }, "Delete Element (Cascade)"),
16765
+ React.createElement("button", { type: "button", onClick: function () { var _a; return (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.setSelection(['delete-free-text', 'delete-target']); }, disabled: !hasText }, "Prepare Batch Delete Selection")),
16766
+ React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 12 } },
16767
+ React.createElement("label", null,
16768
+ React.createElement("input", { type: "checkbox", checked: cancelFlags.element, onChange: function (event) { return setCancelFlags(function (prev) { return (__assign(__assign({}, prev), { element: event.target.checked })); }); } }),
16769
+ ' ',
16770
+ "Cancel elementDeleting"),
16771
+ React.createElement("label", null,
16772
+ React.createElement("input", { type: "checkbox", checked: cancelFlags.port, onChange: function (event) { return setCancelFlags(function (prev) { return (__assign(__assign({}, prev), { port: event.target.checked })); }); } }),
16773
+ ' ',
16774
+ "Cancel portDeleting"),
16775
+ React.createElement("label", null,
16776
+ React.createElement("input", { type: "checkbox", checked: cancelFlags.link, onChange: function (event) { return setCancelFlags(function (prev) { return (__assign(__assign({}, prev), { link: event.target.checked })); }); } }),
16777
+ ' ',
16778
+ "Cancel linkDeleting"),
16779
+ React.createElement("label", null,
16780
+ React.createElement("input", { type: "checkbox", checked: cancelFlags.text, onChange: function (event) { return setCancelFlags(function (prev) { return (__assign(__assign({}, prev), { text: event.target.checked })); }); } }),
16781
+ ' ',
16782
+ "Cancel textDeleting")),
16783
+ React.createElement("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 12 } },
16784
+ React.createElement("div", null,
16785
+ React.createElement("label", { style: { display: 'block', fontWeight: 600, marginBottom: 6 } }, "elementDeleting"),
16786
+ React.createElement("textarea", { readOnly: true, value: payloads.elementDeleting ? JSON.stringify(payloads.elementDeleting, null, 2) : '', style: eventBoxStyle })),
16507
16787
  React.createElement("div", null,
16508
16788
  React.createElement("label", { style: { display: 'block', fontWeight: 600, marginBottom: 6 } }, "elementDeleted"),
16509
16789
  React.createElement("textarea", { readOnly: true, value: payloads.elementDeleted ? JSON.stringify(payloads.elementDeleted, null, 2) : '', style: eventBoxStyle })),
16790
+ React.createElement("div", null,
16791
+ React.createElement("label", { style: { display: 'block', fontWeight: 600, marginBottom: 6 } }, "portDeleting"),
16792
+ React.createElement("textarea", { readOnly: true, value: payloads.portDeleting ? JSON.stringify(payloads.portDeleting, null, 2) : '', style: eventBoxStyle })),
16510
16793
  React.createElement("div", null,
16511
16794
  React.createElement("label", { style: { display: 'block', fontWeight: 600, marginBottom: 6 } }, "portDeleted"),
16512
16795
  React.createElement("textarea", { readOnly: true, value: payloads.portDeleted ? JSON.stringify(payloads.portDeleted, null, 2) : '', style: eventBoxStyle })),
16796
+ React.createElement("div", null,
16797
+ React.createElement("label", { style: { display: 'block', fontWeight: 600, marginBottom: 6 } }, "linkDeleting"),
16798
+ React.createElement("textarea", { readOnly: true, value: payloads.linkDeleting ? JSON.stringify(payloads.linkDeleting, null, 2) : '', style: eventBoxStyle })),
16513
16799
  React.createElement("div", null,
16514
16800
  React.createElement("label", { style: { display: 'block', fontWeight: 600, marginBottom: 6 } }, "linkDeleted"),
16515
16801
  React.createElement("textarea", { readOnly: true, value: payloads.linkDeleted ? JSON.stringify(payloads.linkDeleted, null, 2) : '', style: eventBoxStyle })),
16802
+ React.createElement("div", null,
16803
+ React.createElement("label", { style: { display: 'block', fontWeight: 600, marginBottom: 6 } }, "textDeleting"),
16804
+ React.createElement("textarea", { readOnly: true, value: payloads.textDeleting ? JSON.stringify(payloads.textDeleting, null, 2) : '', style: eventBoxStyle })),
16516
16805
  React.createElement("div", null,
16517
16806
  React.createElement("label", { style: { display: 'block', fontWeight: 600, marginBottom: 6 } }, "textDeleted"),
16518
- React.createElement("textarea", { readOnly: true, value: payloads.textDeleted ? JSON.stringify(payloads.textDeleted, null, 2) : '', style: eventBoxStyle })))),
16807
+ React.createElement("textarea", { readOnly: true, value: payloads.textDeleted ? JSON.stringify(payloads.textDeleted, null, 2) : '', style: eventBoxStyle })),
16808
+ React.createElement("div", null,
16809
+ React.createElement("label", { style: { display: 'block', fontWeight: 600, marginBottom: 6 } }, "elementDoubleClick"),
16810
+ React.createElement("textarea", { readOnly: true, value: payloads.elementDoubleClick ? JSON.stringify(payloads.elementDoubleClick, null, 2) : '', style: eventBoxStyle }))),
16811
+ React.createElement("div", null,
16812
+ React.createElement("label", { style: { display: 'block', fontWeight: 600, marginBottom: 6 } }, "Event Log"),
16813
+ React.createElement("textarea", { readOnly: true, value: eventLog.join('\n'), style: __assign(__assign({}, eventBoxStyle), { minHeight: 180 }) }))),
16519
16814
  React.createElement(DisplayBoxStage, { containerRef: containerRef })));
16520
16815
  };
16521
16816
 
@@ -17324,6 +17619,89 @@ var VertexControlLinkSessionDemo = function () {
17324
17619
  React.createElement(DisplayBoxStage, { containerRef: containerRef, stageStyle: gridStageStyle })));
17325
17620
  };
17326
17621
 
17622
+ var createFocusElementState = function () { return ({
17623
+ elements: [
17624
+ {
17625
+ id: 'focus-left',
17626
+ position: { x: 40, y: 80 },
17627
+ size: { width: 140, height: 90 },
17628
+ shapeId: 'default',
17629
+ style: { fill: '#e0f2fe', stroke: '#0369a1', strokeWidth: 2 },
17630
+ },
17631
+ {
17632
+ id: 'focus-right',
17633
+ position: { x: 820, y: 120 },
17634
+ size: { width: 180, height: 110 },
17635
+ shapeId: 'panel',
17636
+ style: { fill: '#fef3c7', stroke: '#b45309', strokeWidth: 2 },
17637
+ },
17638
+ {
17639
+ id: 'focus-parent',
17640
+ position: { x: 520, y: 420 },
17641
+ size: { width: 220, height: 160 },
17642
+ shapeId: 'default',
17643
+ style: { fill: '#ecfccb', stroke: '#4d7c0f', strokeWidth: 2 },
17644
+ },
17645
+ {
17646
+ id: 'focus-child',
17647
+ parentId: 'focus-parent',
17648
+ position: { x: 110, y: 60 },
17649
+ size: { width: 70, height: 50 },
17650
+ shapeId: 'default',
17651
+ anchorCenter: true,
17652
+ style: { fill: '#dcfce7', stroke: '#15803d', strokeWidth: 2 },
17653
+ },
17654
+ ],
17655
+ ports: [],
17656
+ links: [],
17657
+ texts: [
17658
+ {
17659
+ id: 'focus-copy',
17660
+ content: 'Compare explicit element focus against zoom-to-fit.',
17661
+ position: { x: 40, y: 26 },
17662
+ },
17663
+ ],
17664
+ }); };
17665
+ var focusLeftAction = {
17666
+ id: 'focus-left-default',
17667
+ label: 'Focus Left (Keep Zoom)',
17668
+ run: function (editor) {
17669
+ editor.focusElement('focus-left');
17670
+ },
17671
+ };
17672
+ var focusRightZoomAction = {
17673
+ id: 'focus-right-zoom',
17674
+ label: 'Focus Right (Zoom 2.2)',
17675
+ run: function (editor) {
17676
+ editor.focusElement('focus-right', { zoom: 2.2 });
17677
+ },
17678
+ };
17679
+ var focusChildZoomAction = {
17680
+ id: 'focus-child-zoom',
17681
+ label: 'Focus Nested Child (Zoom 1.8)',
17682
+ run: function (editor) {
17683
+ editor.focusElement('focus-child', { zoom: 1.8 });
17684
+ },
17685
+ };
17686
+ var fitAllAction = {
17687
+ id: 'focus-fit-all',
17688
+ label: 'Zoom-To-Fit For Comparison',
17689
+ run: function (editor) {
17690
+ editor.zoomToFitElements({ padding: 40 });
17691
+ },
17692
+ };
17693
+ var focusElementDemoConfig = {
17694
+ id: 'focus-element',
17695
+ title: 'Focus Element',
17696
+ description: 'Centers one explicit element, optionally with a chosen zoom, unlike zoom-to-fit which frames the whole scene.',
17697
+ createState: createFocusElementState,
17698
+ elementShapes: baseElementShapes,
17699
+ portShapes: basePortShapes,
17700
+ defaultElementShapeId: 'default',
17701
+ defaultPortShapeId: 'port-circle',
17702
+ actions: [focusLeftAction, focusRightZoomAction, focusChildZoomAction, fitAllAction],
17703
+ };
17704
+
17327
17705
  var wrapSimpleDemo = function (demo) { return function () { return React.createElement(SimpleDemo, { demo: demo }); }; };
17328
17706
  var menuGroupOrder = {
17329
17707
  basics: 1,
@@ -17362,6 +17740,7 @@ var basics = [
17362
17740
  registerDemo(selectionDemoConfig, wrapSimpleDemo(selectionDemoConfig), 'basics', 4),
17363
17741
  registerDemo(elementVisibilitySelectionDemoConfig, wrapSimpleDemo(elementVisibilitySelectionDemoConfig), 'basics', 5),
17364
17742
  registerDemo(zoomToFitElementsDemoConfig, ZoomToFitElementsDemo, 'basics', 6),
17743
+ registerDemo(focusElementDemoConfig, wrapSimpleDemo(focusElementDemoConfig), 'basics', 7),
17365
17744
  ];
17366
17745
  var layoutAndText = [
17367
17746
  registerDemo(textDemoConfig, TextLayoutDemo, 'layoutText', 1),