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