@metadev/daga 1.4.0 → 1.4.2

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 (30) hide show
  1. package/Changelog.md +26 -2
  2. package/assets/styles/_property-editor.scss +225 -157
  3. package/assets/styles/daga.scss +149 -156
  4. package/fesm2022/metadev-daga.mjs +1536 -584
  5. package/fesm2022/metadev-daga.mjs.map +1 -1
  6. package/index.d.ts +5 -3
  7. package/lib/daga.module.d.ts +4 -5
  8. package/lib/diagram-editor/diagram/collab/collab-action.d.ts +90 -0
  9. package/lib/diagram-editor/diagram/collab/collab-client.d.ts +57 -0
  10. package/lib/diagram-editor/diagram/collab/collab-engine.d.ts +46 -0
  11. package/lib/diagram-editor/diagram/collab/message-types.d.ts +97 -0
  12. package/lib/diagram-editor/diagram/collab/primitives.d.ts +15 -0
  13. package/lib/diagram-editor/diagram/converters/daga-model.d.ts +3 -0
  14. package/lib/diagram-editor/diagram/diagram-action.d.ts +27 -19
  15. package/lib/diagram-editor/diagram/diagram-canvas.d.ts +4 -1
  16. package/lib/diagram-editor/diagram/diagram-connection.d.ts +35 -3
  17. package/lib/diagram-editor/diagram/diagram-element.d.ts +7 -2
  18. package/lib/diagram-editor/diagram/diagram-field.d.ts +13 -2
  19. package/lib/diagram-editor/diagram/diagram-model.d.ts +6 -0
  20. package/lib/diagram-editor/diagram/diagram-node.d.ts +67 -3
  21. package/lib/diagram-editor/diagram/diagram-port.d.ts +28 -2
  22. package/lib/diagram-editor/diagram/diagram-property.d.ts +143 -19
  23. package/lib/diagram-editor/diagram/diagram-section.d.ts +40 -3
  24. package/lib/interfaces/canvas.d.ts +13 -0
  25. package/lib/property-editor/autocomplete/autocomplete.component.d.ts +39 -0
  26. package/lib/{object-editor → property-editor/object-editor}/object-editor.component.d.ts +5 -6
  27. package/lib/property-editor/option-list-editor/option-list-editor.component.d.ts +29 -0
  28. package/lib/{text-list-editor → property-editor/text-list-editor}/text-list-editor.component.d.ts +2 -2
  29. package/lib/{text-map-editor → property-editor/text-map-editor}/text-map-editor.component.d.ts +2 -2
  30. package/package.json +3 -3
@@ -1,13 +1,12 @@
1
- import * as i2 from '@angular/common';
1
+ import * as i1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
4
  import { ElementRef, Component, Input, Injectable, ViewChild, EventEmitter, Output, NgModule } from '@angular/core';
5
5
  import * as d3 from 'd3';
6
- import * as i4 from '@angular/forms';
6
+ import * as i2 from '@angular/forms';
7
7
  import { FormsModule } from '@angular/forms';
8
- import * as i3 from '@metadev/lux';
9
- import { LuxModule } from '@metadev/lux';
10
8
  import { Subject, merge, delay, map } from 'rxjs';
9
+ import { v4 } from 'uuid';
11
10
 
12
11
  /**
13
12
  * An enumeration of the possible sides of a rectangle and directions parallel to the axes of coordinates.
@@ -243,6 +242,100 @@ const addIfNotExists = (arr, obj) => {
243
242
  return arr;
244
243
  };
245
244
 
245
+ /**
246
+ * Returns whether the incoming timestamp wins over the existing timestamp.
247
+ *
248
+ * In the DiagramModel, timestamps that have never been set are left undefined;
249
+ * `timestampWins` treats that as an "initial timestamp" that loses to all LogicalTimestamps.
250
+ */
251
+ function timestampWins(incoming, existing) {
252
+ if (existing === undefined)
253
+ return true;
254
+ if (incoming[0] > existing[0])
255
+ return true;
256
+ if (incoming[0] === existing[0]) {
257
+ // In case of equality, declare the incoming timestamp the "winner".
258
+ // This occurs if a client reuses a timestamp for multiple actions in a row,
259
+ // in which case the last created should win.
260
+ return incoming[1] >= existing[1];
261
+ }
262
+ return false;
263
+ }
264
+
265
+ /**
266
+ * Collaborative action which consists of adding a node.
267
+ * @see AddNodeAction
268
+ * @private
269
+ */
270
+ class AddNodeCollabAction {
271
+ constructor(canvas, id, nodeTypeId, coords, label, values) {
272
+ this.canvas = canvas;
273
+ this.id = id;
274
+ this.nodeTypeId = nodeTypeId;
275
+ this.coords = coords;
276
+ this.label = label;
277
+ this.values = values;
278
+ }
279
+ do() {
280
+ const node = this.canvas.model.nodes.new(this.nodeTypeId, this.coords, this.id);
281
+ if (this.values !== undefined) {
282
+ node.valueSet.setValues(structuredClone({
283
+ ...node.valueSet.getValues(),
284
+ ...this.values
285
+ }));
286
+ }
287
+ if (node.label) {
288
+ node.label.text = this.label || '';
289
+ }
290
+ }
291
+ serialize() {
292
+ return {
293
+ type: 'add',
294
+ id: this.id,
295
+ nodeTypeId: this.nodeTypeId,
296
+ coords: this.coords,
297
+ label: this.label,
298
+ values: this.values
299
+ };
300
+ }
301
+ static deserialize(canvas, serialized) {
302
+ return new AddNodeCollabAction(canvas, serialized.id, serialized.nodeTypeId, serialized.coords, serialized.label, serialized.values);
303
+ }
304
+ }
305
+ /**
306
+ * Collaborative action which consists of moving a node.
307
+ * @see MoveNodeAction
308
+ * @private
309
+ */
310
+ class MoveNodeCollabAction {
311
+ constructor(canvas, nodeId, to, timestamp) {
312
+ this.canvas = canvas;
313
+ this.nodeId = nodeId;
314
+ this.to = to;
315
+ this.timestamp = timestamp;
316
+ }
317
+ do() {
318
+ const node = this.canvas.model.nodes.get(this.nodeId);
319
+ if (!node)
320
+ return;
321
+ if (timestampWins(this.timestamp, node.coordsTimestamp)) {
322
+ node.move(this.to);
323
+ node.coordsTimestamp = this.timestamp;
324
+ }
325
+ }
326
+ serialize() {
327
+ return {
328
+ type: 'move',
329
+ nodeId: this.nodeId,
330
+ to: this.to,
331
+ timestamp: this.timestamp
332
+ };
333
+ }
334
+ static deserialize(canvas, serialized) {
335
+ return new MoveNodeCollabAction(canvas, serialized.nodeId, serialized.to, serialized.timestamp);
336
+ }
337
+ }
338
+
246
339
  /**
247
340
  * A queue of recent actions taken by the user that can be undone and redone.
248
341
  * @private
@@ -362,38 +455,71 @@ var DiagramActions;
362
455
  * @private
363
456
  */
364
457
  class AddNodeAction {
365
- constructor(canvas, type, coords, label, values, id) {
458
+ constructor(canvas, type, coords, label, values) {
366
459
  this.canvas = canvas;
367
460
  this.type = type;
368
461
  this.coords = coords;
369
462
  this.label = label;
370
463
  this.values = values;
371
- this.id = id;
464
+ this.id = this.canvas.collabEngine.freshId();
465
+ }
466
+ do() {
467
+ const collabAction = new AddNodeCollabAction(this.canvas, this.id, this.type.id, this.coords, this.label, this.values);
468
+ this.canvas.collabEngine.doCollaboratively(collabAction);
372
469
  }
373
470
  undo() {
374
- if (this.id) {
375
- this.canvas.model.nodes.remove(this.id);
471
+ const node = this.canvas.model.nodes.get(this.id);
472
+ if (node) {
473
+ node.removed = true;
474
+ for (const section of node.sections) {
475
+ section.removed = true;
476
+ for (const port of section.ports) {
477
+ port.removed = true;
478
+ port.updateInView();
479
+ }
480
+ if (section.label) {
481
+ section.label.removed = true;
482
+ section.label.updateInView();
483
+ }
484
+ section.updateInView();
485
+ }
486
+ for (const port of node.ports) {
487
+ port.removed = true;
488
+ port.updateInView();
489
+ }
490
+ if (node.label) {
491
+ node.label.removed = true;
492
+ node.label.updateInView();
493
+ }
494
+ node.updateInView();
376
495
  }
377
496
  }
378
497
  redo() {
379
- const node = this.canvas.model.nodes.new(this.type, this.coords, this.id, this.sectionIds, this.sectionPortIds, this.sectionPortLabelIds, this.portIds, this.portLabelIds, this.labelId);
380
- if (this.values !== undefined) {
381
- node.valueSet.setValues(structuredClone({
382
- ...node.valueSet.getValues(),
383
- ...this.values
384
- }));
385
- }
386
- if (node.label) {
387
- node.label.text = this.label || '';
498
+ const node = this.canvas.model.nodes.get(this.id);
499
+ if (node) {
500
+ node.removed = false;
501
+ for (const section of node.sections) {
502
+ section.removed = false;
503
+ for (const port of section.ports) {
504
+ port.removed = false;
505
+ port.updateInView();
506
+ }
507
+ if (section.label) {
508
+ section.label.removed = false;
509
+ section.label.updateInView();
510
+ }
511
+ section.updateInView();
512
+ }
513
+ for (const port of node.ports) {
514
+ port.removed = false;
515
+ port.updateInView();
516
+ }
517
+ if (node.label) {
518
+ node.label.removed = false;
519
+ node.label.updateInView();
520
+ }
521
+ node.updateInView();
388
522
  }
389
- // reset our ids in case they were automatically generated
390
- this.id = node.id;
391
- this.sectionIds = node.sections.map((s) => s.id);
392
- this.sectionPortIds = node.sections.map((s) => s.ports.map((p) => p.id));
393
- this.sectionPortLabelIds = node.sections.map((s) => s.ports.map((p) => p.label?.id));
394
- this.portIds = node.ports.map((p) => p.id);
395
- this.portLabelIds = node.ports.map((p) => p.label?.id);
396
- this.labelId = node.label?.id;
397
523
  }
398
524
  }
399
525
  /**
@@ -408,11 +534,16 @@ class MoveNodeAction {
408
534
  this.from = from;
409
535
  this.to = to;
410
536
  }
537
+ do() {
538
+ const collabAction = new MoveNodeCollabAction(this.canvas, this.nodeId, this.to, this.canvas.collabEngine.freshTimestamp());
539
+ this.canvas.collabEngine.doCollaboratively(collabAction);
540
+ }
411
541
  undo() {
412
- this.canvas.model.nodes.get(this.nodeId)?.move(this.from);
542
+ const collabAction = new MoveNodeCollabAction(this.canvas, this.nodeId, this.from, this.canvas.collabEngine.freshTimestamp());
543
+ this.canvas.collabEngine.doCollaboratively(collabAction);
413
544
  }
414
545
  redo() {
415
- this.canvas.model.nodes.get(this.nodeId)?.move(this.to);
546
+ this.do();
416
547
  }
417
548
  }
418
549
  /**
@@ -428,15 +559,18 @@ class StretchNodeAction {
428
559
  this.from = from;
429
560
  this.to = to;
430
561
  }
562
+ do() {
563
+ this.canvas.model.nodes
564
+ .get(this.nodeId)
565
+ ?.stretch(this.direction, this.to - this.from);
566
+ }
431
567
  undo() {
432
568
  this.canvas.model.nodes
433
569
  .get(this.nodeId)
434
570
  ?.stretch(this.direction, this.from - this.to);
435
571
  }
436
572
  redo() {
437
- this.canvas.model.nodes
438
- .get(this.nodeId)
439
- ?.stretch(this.direction, this.to - this.from);
573
+ this.do();
440
574
  }
441
575
  }
442
576
  /**
@@ -452,20 +586,23 @@ class StretchSectionAction {
452
586
  this.from = from;
453
587
  this.to = to;
454
588
  }
455
- undo() {
589
+ do() {
456
590
  const section = this.canvas.model.sections.get(this.sectionId);
457
591
  const node = section?.node;
458
592
  if (node) {
459
- node.stretchSections(this.direction, this.from - this.to, section.indexXInNode, section.indexYInNode);
593
+ node.stretchSections(this.direction, this.to - this.from, section.indexXInNode, section.indexYInNode);
460
594
  }
461
595
  }
462
- redo() {
596
+ undo() {
463
597
  const section = this.canvas.model.sections.get(this.sectionId);
464
598
  const node = section?.node;
465
599
  if (node) {
466
- node.stretchSections(this.direction, this.to - this.from, section.indexXInNode, section.indexYInNode);
600
+ node.stretchSections(this.direction, this.from - this.to, section.indexXInNode, section.indexYInNode);
467
601
  }
468
602
  }
603
+ redo() {
604
+ this.do();
605
+ }
469
606
  }
470
607
  /**
471
608
  * Action which consists of adding a connection.
@@ -480,12 +617,7 @@ class AddConnectionAction {
480
617
  this.endId = endId;
481
618
  this.id = id;
482
619
  }
483
- undo() {
484
- if (this.id) {
485
- this.canvas.model.connections.remove(this.id);
486
- }
487
- }
488
- redo() {
620
+ do() {
489
621
  const start = this.canvas.model.ports.get(this.startId);
490
622
  const end = this.canvas.model.ports.get(this.endId);
491
623
  if (start && end) {
@@ -494,6 +626,24 @@ class AddConnectionAction {
494
626
  this.id = connection.id;
495
627
  }
496
628
  }
629
+ undo() {
630
+ if (this.id) {
631
+ const connection = this.canvas.model.connections.get(this.id);
632
+ if (connection) {
633
+ connection.removed = true;
634
+ connection.updateInView();
635
+ }
636
+ }
637
+ }
638
+ redo() {
639
+ if (this.id) {
640
+ const connection = this.canvas.model.connections.get(this.id);
641
+ if (connection) {
642
+ connection.removed = false;
643
+ connection.updateInView();
644
+ }
645
+ }
646
+ }
497
647
  }
498
648
  /**
499
649
  * Action which consists of editing the text of a field.
@@ -507,18 +657,21 @@ class EditFieldAction {
507
657
  this.from = from;
508
658
  this.to = to;
509
659
  }
510
- undo() {
660
+ do() {
511
661
  const field = this.canvas.model.fields.get(this.fieldId);
512
662
  if (field) {
513
- field.text = this.from;
663
+ field.text = this.to;
514
664
  }
515
665
  }
516
- redo() {
666
+ undo() {
517
667
  const field = this.canvas.model.fields.get(this.fieldId);
518
668
  if (field) {
519
- field.text = this.to;
669
+ field.text = this.from;
520
670
  }
521
671
  }
672
+ redo() {
673
+ this.do();
674
+ }
522
675
  }
523
676
  /**
524
677
  * Action which consists of editing the values of a value set.
@@ -540,11 +693,14 @@ class UpdateValuesAction {
540
693
  return (this.model.nodes.get(this.id) || this.model.connections.get(this.id))?.valueSet;
541
694
  }
542
695
  }
696
+ do() {
697
+ this.getValueSet()?.overwriteValues(this.to);
698
+ }
543
699
  undo() {
544
- this.getValueSet()?.setValues(this.from);
700
+ this.getValueSet()?.overwriteValues(this.from);
545
701
  }
546
702
  redo() {
547
- this.getValueSet()?.setValues(this.to);
703
+ this.do();
548
704
  }
549
705
  }
550
706
  /**
@@ -552,274 +708,153 @@ class UpdateValuesAction {
552
708
  * @private
553
709
  */
554
710
  class RemoveAction {
555
- constructor(model, nodes, sections, ports, connections, fields) {
556
- this.nodes = [];
557
- this.nodeIds = [];
558
- this.sections = [];
559
- this.sectionIds = [];
560
- this.ports = [];
561
- this.portIds = [];
562
- this.connections = [];
563
- this.connectionIds = [];
564
- this.fields = [];
565
- this.fieldIds = [];
711
+ constructor(model, nodeIds, sectionIds, portIds, connectionIds, fieldIds) {
566
712
  this.model = model;
567
- this.nodes = nodes;
568
- this.sections = sections;
569
- this.ports = ports;
570
- this.connections = connections;
571
- this.fields = fields;
572
- // if a node is deleted, its label, ports and sections must also be deleted
573
- for (const node of this.nodes) {
574
- if (node.label) {
575
- addIfNotExists(this.fields, node.label);
576
- }
577
- for (const port of node.ports) {
578
- addIfNotExists(this.ports, port);
579
- }
580
- for (const section of node.sections) {
581
- addIfNotExists(this.sections, section);
582
- }
583
- }
584
- // if a section is deleted, its label and ports must also be deleted
585
- for (const section of this.sections) {
586
- if (section.label) {
587
- addIfNotExists(this.fields, section.label);
588
- }
589
- for (const port of section.ports) {
590
- addIfNotExists(this.ports, port);
591
- }
592
- }
593
- // if a port is deleted, its label and connections must also be deleted
594
- for (const port of this.ports) {
595
- if (port.label) {
596
- addIfNotExists(this.fields, port.label);
597
- }
598
- for (const connection of port.outgoingConnections) {
599
- addIfNotExists(this.connections, connection);
600
- }
601
- for (const connection of port.incomingConnections) {
602
- addIfNotExists(this.connections, connection);
603
- }
604
- }
605
- this.nodeIds = this.nodes.map((n) => n.id);
606
- this.sectionIds = this.sections.map((s) => s.id);
607
- this.portIds = this.ports.map((p) => p.id);
608
- this.connectionIds = this.connections.map((c) => c.id);
609
- this.fieldIds = this.fields.map((f) => f.id);
713
+ this.nodeIds = nodeIds;
714
+ this.sectionIds = sectionIds;
715
+ this.portIds = portIds;
716
+ this.connectionIds = connectionIds;
717
+ this.fieldIds = fieldIds;
610
718
  }
611
- undo() {
612
- for (const node of this.nodes) {
613
- this.model.nodes.add(node);
614
- }
615
- for (const section of this.sections) {
616
- this.model.sections.add(section);
617
- }
618
- for (const port of this.ports) {
619
- this.model.ports.add(port);
620
- }
621
- for (const connection of this.connections) {
622
- this.model.connections.add(connection);
623
- }
624
- for (const field of this.fields) {
625
- this.model.fields.add(field);
626
- }
627
- for (const node of this.nodes) {
628
- if (node.label) {
629
- const label = this.model.fields.get(node.label.id);
630
- if (label) {
631
- node.label = label;
719
+ do() {
720
+ for (const nodeId of this.nodeIds) {
721
+ const node = this.model.nodes.get(nodeId);
722
+ if (node) {
723
+ node.removed = true;
724
+ for (const section of node.sections) {
725
+ addIfNotExists(this.sectionIds, section.id);
632
726
  }
633
- }
634
- const ports = [];
635
- for (const nodePort of node.ports) {
636
- const port = this.model.ports.get(nodePort.id);
637
- if (port) {
638
- ports.push(port);
727
+ for (const port of node.ports) {
728
+ addIfNotExists(this.portIds, port.id);
639
729
  }
640
- }
641
- node.ports = ports;
642
- const sections = [];
643
- for (const nodeSection of node.sections) {
644
- const section = this.model.sections.get(nodeSection.id);
645
- if (section) {
646
- sections.push(section);
730
+ if (node.label) {
731
+ addIfNotExists(this.fieldIds, node.label.id);
647
732
  }
648
733
  }
649
- node.sections = sections;
650
734
  }
651
- for (const section of this.sections) {
652
- if (section.node) {
653
- const node = this.model.nodes.get(section.node.id);
654
- if (node) {
655
- section.node = node;
656
- }
657
- }
658
- if (section.label) {
659
- const label = this.model.fields.get(section.label.id);
660
- if (label) {
661
- section.label = label;
735
+ for (const sectionId of this.sectionIds) {
736
+ const section = this.model.sections.get(sectionId);
737
+ if (section) {
738
+ section.removed = true;
739
+ for (const port of section.ports) {
740
+ addIfNotExists(this.portIds, port.id);
662
741
  }
663
- }
664
- const ports = [];
665
- for (const sectionPort of section.ports) {
666
- const port = this.model.ports.get(sectionPort.id);
667
- if (port) {
668
- ports.push(port);
742
+ if (section.label) {
743
+ addIfNotExists(this.fieldIds, section.label.id);
669
744
  }
670
745
  }
671
- section.ports = ports;
672
746
  }
673
- for (const port of this.ports) {
674
- if (port.rootElement) {
675
- const rootElement = this.model.nodes.get(port.rootElement.id) ||
676
- this.model.sections.get(port.rootElement.id);
677
- if (rootElement) {
678
- port.rootElement = rootElement;
679
- }
680
- }
681
- if (port.label) {
682
- const label = this.model.fields.get(port.label.id);
683
- if (label) {
684
- port.label = label;
747
+ for (const portId of this.portIds) {
748
+ const port = this.model.ports.get(portId);
749
+ if (port) {
750
+ port.removed = true;
751
+ for (const connection of port.outgoingConnections) {
752
+ addIfNotExists(this.connectionIds, connection.id);
685
753
  }
686
- }
687
- const outgoingConnections = [];
688
- for (const portOutgoingConnection of port.outgoingConnections) {
689
- const connection = this.model.connections.get(portOutgoingConnection.id);
690
- if (connection) {
691
- outgoingConnections.push(connection);
754
+ for (const connection of port.incomingConnections) {
755
+ addIfNotExists(this.connectionIds, connection.id);
692
756
  }
693
- }
694
- port.outgoingConnections = outgoingConnections;
695
- const incomingConnections = [];
696
- for (const portIncomingConnection of port.incomingConnections) {
697
- const connection = this.model.connections.get(portIncomingConnection.id);
698
- if (connection) {
699
- incomingConnections.push(connection);
757
+ if (port.label) {
758
+ addIfNotExists(this.fieldIds, port.label.id);
700
759
  }
701
760
  }
702
- port.incomingConnections = incomingConnections;
703
761
  }
704
- for (const connection of this.connections) {
705
- if (connection.start) {
706
- const start = this.model.ports.get(connection.start.id);
707
- if (start) {
708
- connection.setStart(start);
709
- }
710
- }
711
- if (connection.end) {
712
- const end = this.model.ports.get(connection.end.id);
713
- if (end) {
714
- connection.setEnd(end);
715
- }
762
+ for (const connectionId of this.connectionIds) {
763
+ const connection = this.model.connections.get(connectionId);
764
+ if (connection) {
765
+ connection.removed = true;
716
766
  }
717
767
  }
718
- for (const field of this.fields) {
719
- if (field.rootElement) {
720
- const rootElement = this.model.nodes.get(field.rootElement.id) ||
721
- this.model.sections.get(field.rootElement.id) ||
722
- this.model.ports.get(field.rootElement.id);
723
- if (rootElement) {
724
- field.rootElement = rootElement;
725
- }
768
+ for (const fieldId of this.fieldIds) {
769
+ const field = this.model.fields.get(fieldId);
770
+ if (field) {
771
+ field.removed = true;
726
772
  }
727
773
  }
728
774
  // update view
729
- this.model.canvas?.updateModelInView();
775
+ this.model.canvas?.updateNodesInView(...this.nodeIds);
776
+ this.model.canvas?.updateSectionsInView(...this.sectionIds);
777
+ this.model.canvas?.updatePortsInView(...this.portIds);
778
+ this.model.canvas?.updateConnectionsInView(...this.connectionIds);
779
+ this.model.canvas?.updateFieldsInView(...this.fieldIds);
730
780
  }
731
- redo() {
732
- this.nodes = [];
733
- this.sections = [];
734
- this.ports = [];
735
- this.connections = [];
736
- this.fields = [];
737
- // make shallow copies of the elements with their relationships
781
+ undo() {
738
782
  for (const nodeId of this.nodeIds) {
739
783
  const node = this.model.nodes.get(nodeId);
740
784
  if (node) {
741
- const nodeCopy = clone(node);
742
- if (nodeCopy.label) {
743
- nodeCopy.label = clone(nodeCopy.label);
744
- }
745
- nodeCopy.ports = nodeCopy.ports.map((p) => clone(p));
746
- nodeCopy.sections = nodeCopy.sections.map((s) => clone(s));
747
- this.nodes.push(nodeCopy);
785
+ node.removed = false;
748
786
  }
749
787
  }
750
788
  for (const sectionId of this.sectionIds) {
751
789
  const section = this.model.sections.get(sectionId);
752
790
  if (section) {
753
- const sectionCopy = clone(section);
754
- if (sectionCopy.label) {
755
- sectionCopy.label = clone(sectionCopy.label);
756
- }
757
- sectionCopy.ports = sectionCopy.ports.map((p) => clone(p));
758
- this.sections.push(sectionCopy);
791
+ section.removed = false;
759
792
  }
760
793
  }
761
794
  for (const portId of this.portIds) {
762
795
  const port = this.model.ports.get(portId);
763
796
  if (port) {
764
- const portCopy = clone(port);
765
- if (portCopy.rootElement) {
766
- portCopy.rootElement = clone(portCopy.rootElement);
767
- }
768
- if (portCopy.label) {
769
- portCopy.label = clone(portCopy.label);
770
- }
771
- portCopy.outgoingConnections = portCopy.outgoingConnections.map((c) => clone(c));
772
- portCopy.incomingConnections = portCopy.incomingConnections.map((c) => clone(c));
773
- this.ports.push(portCopy);
797
+ port.removed = false;
774
798
  }
775
799
  }
776
800
  for (const connectionId of this.connectionIds) {
777
801
  const connection = this.model.connections.get(connectionId);
778
802
  if (connection) {
779
- const connectionCopy = clone(connection);
780
- if (connectionCopy.start) {
781
- connectionCopy.start = clone(connectionCopy.start);
782
- }
783
- if (connectionCopy.end) {
784
- connectionCopy.end = clone(connectionCopy.end);
785
- }
786
- this.connections.push(connectionCopy);
803
+ connection.removed = false;
787
804
  }
788
805
  }
789
806
  for (const fieldId of this.fieldIds) {
790
807
  const field = this.model.fields.get(fieldId);
791
808
  if (field) {
792
- const fieldCopy = clone(field);
793
- if (fieldCopy.rootElement) {
794
- fieldCopy.rootElement = clone(fieldCopy.rootElement);
795
- }
796
- this.fields.push(fieldCopy);
809
+ field.removed = false;
797
810
  }
798
811
  }
799
- // delete the elements
812
+ // update view
813
+ this.model.canvas?.updateNodesInView(...this.nodeIds);
814
+ this.model.canvas?.updateSectionsInView(...this.sectionIds);
815
+ this.model.canvas?.updatePortsInView(...this.portIds);
816
+ this.model.canvas?.updateConnectionsInView(...this.connectionIds);
817
+ this.model.canvas?.updateFieldsInView(...this.fieldIds);
818
+ }
819
+ redo() {
800
820
  for (const nodeId of this.nodeIds) {
801
- this.model.nodes.remove(nodeId);
821
+ const node = this.model.nodes.get(nodeId);
822
+ if (node) {
823
+ node.removed = true;
824
+ }
802
825
  }
803
826
  for (const sectionId of this.sectionIds) {
804
- this.model.sections.remove(sectionId);
827
+ const section = this.model.sections.get(sectionId);
828
+ if (section) {
829
+ section.removed = true;
830
+ }
805
831
  }
806
832
  for (const portId of this.portIds) {
807
- this.model.ports.remove(portId);
833
+ const port = this.model.ports.get(portId);
834
+ if (port) {
835
+ port.removed = true;
836
+ }
808
837
  }
809
838
  for (const connectionId of this.connectionIds) {
810
- this.model.connections.remove(connectionId);
839
+ const connection = this.model.connections.get(connectionId);
840
+ if (connection) {
841
+ connection.removed = true;
842
+ }
811
843
  }
812
844
  for (const fieldId of this.fieldIds) {
813
- this.model.fields.remove(fieldId);
845
+ const field = this.model.fields.get(fieldId);
846
+ if (field) {
847
+ field.removed = true;
848
+ }
814
849
  }
850
+ // update view
851
+ this.model.canvas?.updateNodesInView(...this.nodeIds);
852
+ this.model.canvas?.updateSectionsInView(...this.sectionIds);
853
+ this.model.canvas?.updatePortsInView(...this.portIds);
854
+ this.model.canvas?.updateConnectionsInView(...this.connectionIds);
855
+ this.model.canvas?.updateFieldsInView(...this.fieldIds);
815
856
  }
816
857
  }
817
- const clone = (obj) => {
818
- if (obj === undefined || obj === null) {
819
- return obj;
820
- }
821
- return Object.create(obj);
822
- };
823
858
 
824
859
  /**
825
860
  * A two dimensional array of elements of the given class that can extend infinitely in any direction, both positive and negative.
@@ -1516,7 +1551,7 @@ class PriorityLayout {
1516
1551
  const gapSize = (model.canvas?.gridSize || 0) * 2;
1517
1552
  const nodesToBeArranged = [...model.nodes];
1518
1553
  const nodeArrangement = [];
1519
- const nodesWithMaximumPriorityToBeArranged = model.nodes.filter(undefined, maximumPriority);
1554
+ const nodesWithMaximumPriorityToBeArranged = model.nodes.filter((n) => !n.removed && n.getPriority() >= maximumPriority);
1520
1555
  const nodesWithMaximumPriority = [];
1521
1556
  if (nodesWithMaximumPriorityToBeArranged.length > 1) {
1522
1557
  // use bfs to sort nodes by distance to the first node
@@ -2296,6 +2331,21 @@ const stickyNotePath = (x, y, width, height) => {
2296
2331
  return `M ${x} ${y} L ${x + (3 * width) / 4} ${y} L ${x + (3 * width) / 4} ${y + height / 4} L ${x + (3 * width) / 4} ${y} L ${x + width} ${y + height / 4} L ${x + (3 * width) / 4} ${y + height / 4} L ${x + width} ${y + height / 4} L ${x + width} ${y + height} L ${x} ${y + height} Z`;
2297
2332
  };
2298
2333
 
2334
+ var CursorStyle;
2335
+ (function (CursorStyle) {
2336
+ CursorStyle["AllScroll"] = "all-scroll";
2337
+ CursorStyle["Auto"] = "auto";
2338
+ CursorStyle["EWResize"] = "ew-resize";
2339
+ CursorStyle["Grab"] = "grab";
2340
+ CursorStyle["Grabbing"] = "grabbing";
2341
+ CursorStyle["Move"] = "move";
2342
+ CursorStyle["NoDrop"] = "no-drop";
2343
+ CursorStyle["NSResize"] = "ns-resize";
2344
+ CursorStyle["NotAllowed"] = "not-allowed";
2345
+ CursorStyle["ZoomIn"] = "zoom-in";
2346
+ CursorStyle["ZoomOut"] = "zoom-out";
2347
+ })(CursorStyle || (CursorStyle = {}));
2348
+
2299
2349
  const numberOfColumns = (s) => {
2300
2350
  return Math.max(...s.split('\n').map((a) => a.length));
2301
2351
  };
@@ -2329,6 +2379,11 @@ class DiagramElement {
2329
2379
  * @private
2330
2380
  */
2331
2381
  this.selected = false;
2382
+ /**
2383
+ * Whether this diagram element has been removed.
2384
+ * @private
2385
+ */
2386
+ this.removed = false;
2332
2387
  this.model = model;
2333
2388
  this._id = id;
2334
2389
  }
@@ -2410,16 +2465,16 @@ class DiagramEntitySet {
2410
2465
  * @public
2411
2466
  * @returns An array containing the entities of this set that meet the given criteria.
2412
2467
  */
2413
- filter() {
2414
- return this.all();
2468
+ filter(predicate) {
2469
+ return this.entities.filter(predicate);
2415
2470
  }
2416
2471
  /**
2417
2472
  * Gets an entity of this set matching specific criteria.
2418
2473
  * @public
2419
2474
  * @returns An entity of this set.
2420
2475
  */
2421
- find() {
2422
- return this.entities.length > 0 ? this.entities[0] : undefined;
2476
+ find(predicate) {
2477
+ return this.entities.find(predicate);
2423
2478
  }
2424
2479
  /**
2425
2480
  * Gets an entity from this set.
@@ -2474,7 +2529,7 @@ class Property {
2474
2529
  }
2475
2530
  }
2476
2531
  /**
2477
- * The type that a property can hvae..
2532
+ * The type that a property can have.
2478
2533
  * @see Property
2479
2534
  * @private
2480
2535
  */
@@ -2492,6 +2547,10 @@ var Type;
2492
2547
  * A type whose value must be a date.
2493
2548
  */
2494
2549
  Type["Date"] = "date";
2550
+ /**
2551
+ * A type whose value must be a date with a time.
2552
+ */
2553
+ Type["Datetime"] = "datetime";
2495
2554
  /**
2496
2555
  * A type whose value must be a number.
2497
2556
  */
@@ -2506,6 +2565,7 @@ var Type;
2506
2565
  Type["Option"] = "option";
2507
2566
  /**
2508
2567
  * A type whose value must be a list of values picked from a set of options.
2568
+ * @see Type.Option
2509
2569
  */
2510
2570
  Type["OptionList"] = "option-list";
2511
2571
  /**
@@ -2569,6 +2629,13 @@ class ValueSet {
2569
2629
  this.rootElement = rootElement;
2570
2630
  this.resetValues();
2571
2631
  }
2632
+ /**
2633
+ * Get the value of the root element attribute under the given keys.
2634
+ * If given an array of keys, the value is found under repeated lookups to enable obtaining nested values.
2635
+ * @private
2636
+ * @param rootAttribute A key or array of keys to look up in the root element.
2637
+ * @returns The value if it could be found, `undefined` if nothing could be found.
2638
+ */
2572
2639
  getRootElementValue(rootAttribute) {
2573
2640
  if (typeof rootAttribute === 'string') {
2574
2641
  return this.rootElement[rootAttribute];
@@ -2586,6 +2653,15 @@ class ValueSet {
2586
2653
  }
2587
2654
  return undefined;
2588
2655
  }
2656
+ /**
2657
+ * Set the value of the root element's attribute under the given keys.
2658
+ * If given an array of keys, the value is found under repeated lookups to enable setting nested values.
2659
+ * If the root element has a function `updateInView()`, it is called upon successful setting of the value.
2660
+ * If the root element's attribute doesn't exist, it does nothing.
2661
+ * @private
2662
+ * @param rootAttribute A key or array of keys to look up in the root element.
2663
+ * @param value The value to set the root element's attribute to.
2664
+ */
2589
2665
  setRootElementValue(rootAttribute, value) {
2590
2666
  if (typeof rootAttribute === 'string') {
2591
2667
  this.rootElement[rootAttribute] = value;
@@ -2600,35 +2676,69 @@ class ValueSet {
2600
2676
  }
2601
2677
  rootSubelement = rootSubelement[rootSubattribute];
2602
2678
  }
2679
+ if (rootSubelement === null || rootSubelement === undefined) {
2680
+ return;
2681
+ }
2603
2682
  rootSubelement[rootAttribute[rootAttribute.length - 1]] = value;
2604
2683
  }
2684
+ if (typeof this.rootElement['updateInView'] === 'function') {
2685
+ this.rootElement['updateInView']();
2686
+ }
2605
2687
  }
2688
+ /**
2689
+ * Obtain the value under the given key.
2690
+ * @private
2691
+ * @param key A key.
2692
+ * @returns The value under the given key.
2693
+ */
2606
2694
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2607
2695
  getValue(key) {
2608
2696
  const rootAttribute = this.propertySet.getProperty(key)?.rootAttribute;
2609
2697
  if (rootAttribute !== undefined && rootAttribute !== null) {
2610
2698
  this.values[key] = this.getRootElementValue(rootAttribute);
2611
2699
  }
2612
- return this.values[key];
2700
+ const property = this.propertySet.getProperty(key);
2701
+ if (property.type === Type.Object) {
2702
+ return this.valueSets[key].getValues();
2703
+ }
2704
+ else {
2705
+ return this.values[key];
2706
+ }
2613
2707
  }
2708
+ /**
2709
+ * Obtain all the values in the set.
2710
+ * @private
2711
+ * @returns An object containing all the values in the set.
2712
+ */
2614
2713
  getValues() {
2615
2714
  const result = {};
2616
2715
  for (const key in this.propertySet.propertyMap) {
2617
2716
  const property = this.propertySet.getProperty(key);
2618
- if (property.type === Type.Object &&
2619
- this.valueSets[key].hasAnySetValue()) {
2717
+ if (property.type === Type.Object) {
2620
2718
  result[key] = this.valueSets[key].getValues();
2621
2719
  }
2622
- else if (this.hasSetValue(key)) {
2720
+ else {
2623
2721
  result[key] = this.getValue(key);
2624
2722
  }
2625
2723
  }
2626
2724
  return result;
2627
2725
  }
2726
+ /**
2727
+ * Check if the value under the key is not empty.
2728
+ * @private
2729
+ * @param key A key.
2730
+ * @returns `true` if the value under the key is empty, `false` otherwise.
2731
+ */
2628
2732
  hasValue(key) {
2629
2733
  const value = this.getValue(key);
2630
2734
  return !empty(value);
2631
2735
  }
2736
+ /**
2737
+ * Check if the value under the key is not empty or the default value.
2738
+ * @private
2739
+ * @param key A key.
2740
+ * @returns `true` if the value under the key is empty or the default value, `false` otherwise.
2741
+ */
2632
2742
  hasSetValue(key) {
2633
2743
  const value = this.getValue(key);
2634
2744
  const property = this.propertySet.getProperty(key);
@@ -2637,6 +2747,11 @@ class ValueSet {
2637
2747
  }
2638
2748
  return !empty(value) && !equals(value, property?.defaultValue);
2639
2749
  }
2750
+ /**
2751
+ * Check if any of the values in the set are not empty or the default value.
2752
+ * @private
2753
+ * @returns `true` if any of the values in the set are not empty or the default value, `false` otherwise.
2754
+ */
2640
2755
  hasAnySetValue() {
2641
2756
  for (const property of this.propertySet.propertyList) {
2642
2757
  if (this.hasSetValue(property.name)) {
@@ -2645,58 +2760,84 @@ class ValueSet {
2645
2760
  }
2646
2761
  return false;
2647
2762
  }
2648
- hasAnySetValueExcept(keys) {
2649
- for (const property of this.propertySet.propertyList) {
2650
- if (!keys.includes(property.name) && this.hasSetValue(property.name)) {
2651
- return true;
2652
- }
2653
- }
2654
- return false;
2655
- }
2763
+ /**
2764
+ * Set the value under the given key.
2765
+ * @private
2766
+ * @param key A key.
2767
+ * @param value A value.
2768
+ */
2656
2769
  setValue(key, value) {
2657
- this.values[key] = value;
2658
2770
  const property = this.propertySet.getProperty(key);
2659
2771
  if (property) {
2772
+ if (property.type === Type.Object) {
2773
+ this.valueSets[key].setValues(value);
2774
+ }
2775
+ else {
2776
+ this.values[key] = value;
2777
+ }
2660
2778
  const rootAttribute = property.rootAttribute;
2661
2779
  if (rootAttribute !== undefined && rootAttribute !== null) {
2662
2780
  this.setRootElementValue(rootAttribute, value);
2663
- if (typeof this.rootElement['updateInView'] === 'function') {
2664
- this.rootElement['updateInView']();
2665
- }
2666
2781
  }
2667
2782
  this.displayProperty(property);
2668
2783
  }
2669
2784
  }
2785
+ /**
2786
+ * Reset all values and then set them to the given values.
2787
+ * @private
2788
+ * @param values An object containing all values to set the values to.
2789
+ */
2670
2790
  setValues(values) {
2671
2791
  this.resetValues();
2672
- for (const key in this.propertySet.propertyMap) {
2792
+ for (const key in values) {
2673
2793
  const property = this.propertySet.getProperty(key);
2674
- if (!empty(values[key])) {
2675
- if (property.type === Type.Object) {
2676
- this.valueSets[key].setValues(values[key]);
2677
- }
2678
- else if (!equals(values[key], property?.defaultValue)) {
2679
- this.setValue(key, values[key]);
2680
- }
2794
+ if (property.type === Type.Object) {
2795
+ this.valueSets[key].setValues(values[key]);
2796
+ }
2797
+ else {
2798
+ this.setValue(key, values[key]);
2681
2799
  }
2682
2800
  }
2683
2801
  }
2684
- hasValues() {
2685
- return Object.keys(this.values).length > 0;
2802
+ /**
2803
+ * Writes the given values over the value set's existing values without resetting the existing values.
2804
+ * @private
2805
+ * @param values An object containing all values to set the values to.
2806
+ */
2807
+ overwriteValues(values) {
2808
+ for (const key in values) {
2809
+ const property = this.propertySet.getProperty(key);
2810
+ if (property.type === Type.Object) {
2811
+ this.valueSets[key].overwriteValues(values[key]);
2812
+ }
2813
+ else {
2814
+ this.setValue(key, values[key]);
2815
+ }
2816
+ }
2686
2817
  }
2818
+ /**
2819
+ * Set all the values of this set to the defaults.
2820
+ * If this set has a root element and any of its properties have root attributes, the root element's attributes are also set to the property's default value if one is provided.
2821
+ * @private
2822
+ */
2687
2823
  resetValues() {
2688
2824
  this.displayedProperties = [];
2689
2825
  this.hiddenProperties = [];
2690
2826
  for (const key in this.propertySet.propertyMap) {
2691
2827
  const property = this.propertySet.getProperty(key);
2692
2828
  const rootAttribute = property.rootAttribute;
2693
- if (rootAttribute !== undefined && rootAttribute !== null) {
2694
- this.values[key] = this.getRootElementValue(rootAttribute);
2829
+ if (property.type === Type.Object) {
2830
+ this.valueSets[key] = this.constructSubValueSet(key);
2695
2831
  }
2696
2832
  else {
2697
2833
  this.values[key] = structuredClone(property.defaultValue);
2698
- if (property.type === Type.Object) {
2699
- this.valueSets[key] = this.constructSubValueSet(key);
2834
+ }
2835
+ if (rootAttribute !== undefined && rootAttribute !== null) {
2836
+ if (property.defaultValue !== undefined) {
2837
+ this.setRootElementValue(rootAttribute, this.values[key]);
2838
+ }
2839
+ else {
2840
+ this.values[key] = this.getRootElementValue(rootAttribute);
2700
2841
  }
2701
2842
  }
2702
2843
  if (property.basic) {
@@ -2707,37 +2848,56 @@ class ValueSet {
2707
2848
  }
2708
2849
  }
2709
2850
  }
2710
- displayProperty(property) {
2711
- if (!this.displayedProperties.includes(property)) {
2712
- this.displayedProperties.push(property);
2713
- removeIfExists(this.hiddenProperties, property);
2714
- }
2715
- }
2716
- hideProperty(property) {
2717
- if (!this.hiddenProperties.includes(property)) {
2718
- this.hiddenProperties.push(property);
2719
- removeIfExists(this.displayedProperties, property);
2720
- }
2721
- }
2722
2851
  /**
2723
2852
  * Constructs a ValueSet with its corresponding PropertySet representing the values of the object.
2853
+ * @private
2854
+ * @param key Key that the ValueSet is under.
2855
+ * @returns The constructed ValueSet.
2724
2856
  */
2725
2857
  constructSubValueSet(key) {
2726
2858
  const property = this.propertySet.getProperty(key);
2727
2859
  const propertySet = new PropertySet(property.properties);
2728
2860
  const valueSet = new ValueSet(propertySet, this.rootElement);
2729
- // initialize the default values of each property
2730
- this.values[key] = {
2731
- ...valueSet.values,
2732
- ...this.values[key]
2733
- };
2734
- valueSet.values = this.values[key];
2861
+ valueSet.overwriteValues(structuredClone(property.defaultValue));
2735
2862
  return valueSet;
2736
2863
  }
2864
+ /**
2865
+ * Get the ValueSet under the given key when there are nested ValueSets.
2866
+ * @private
2867
+ * @param key A key.
2868
+ * @returns A ValueSet.
2869
+ */
2737
2870
  getSubValueSet(key) {
2738
2871
  return this.valueSets[key];
2739
2872
  }
2873
+ /**
2874
+ * Move the given property to the list of displayed properties.
2875
+ * @private
2876
+ * @param property A property.
2877
+ */
2878
+ displayProperty(property) {
2879
+ if (!this.displayedProperties.includes(property)) {
2880
+ this.displayedProperties.push(property);
2881
+ removeIfExists(this.hiddenProperties, property);
2882
+ }
2883
+ }
2884
+ /**
2885
+ * Move the given property to the list of hidden properties.
2886
+ * @private
2887
+ * @param property A property.
2888
+ */
2889
+ hideProperty(property) {
2890
+ if (!this.hiddenProperties.includes(property)) {
2891
+ this.hiddenProperties.push(property);
2892
+ removeIfExists(this.displayedProperties, property);
2893
+ }
2894
+ }
2740
2895
  }
2896
+ /**
2897
+ * Check if the given value is not empty.
2898
+ * @param a A value.
2899
+ * @returns `true` if the given value is not `undefined`, `null`, `''`, `[]` or `{}`; `false` otherwise.
2900
+ */
2741
2901
  const empty = (a) => {
2742
2902
  return (a === undefined ||
2743
2903
  a === null ||
@@ -2745,9 +2905,47 @@ const empty = (a) => {
2745
2905
  (a instanceof Array && a.length === 0) ||
2746
2906
  (a instanceof Object && Object.keys(a).length === 0));
2747
2907
  };
2908
+ /**
2909
+ * Check whether the given values are equal.
2910
+ * @param a A value.
2911
+ * @param b A value.
2912
+ * @returns `true` if the given values are equal, `false` otherwise.
2913
+ */
2748
2914
  const equals = (a, b) => {
2749
2915
  return a === b || JSON.stringify(a) === JSON.stringify(b);
2750
2916
  };
2917
+ /**
2918
+ * Calculate the differences between the given values and return two objects, each containing the keys for which the values are different in each argument, containing the different values.
2919
+ * @param a A dictionary.
2920
+ * @param b A dictionary.
2921
+ * @returns A tuple of two values with each containing the keys that have a different value in the corresponding argument compared to the other argument.
2922
+ */
2923
+ const diff = (a, b) => {
2924
+ const aDiff = {};
2925
+ const bDiff = {};
2926
+ const allKeys = [];
2927
+ for (const key in a) {
2928
+ allKeys.push(key);
2929
+ }
2930
+ for (const key in b) {
2931
+ if (!(key in a)) {
2932
+ allKeys.push(key);
2933
+ }
2934
+ }
2935
+ for (const key of allKeys) {
2936
+ if (isObject(a[key]) && isObject(b[key])) {
2937
+ [aDiff[key], bDiff[key]] = diff(a[key], b[key]);
2938
+ }
2939
+ else {
2940
+ if (!equals(a[key], b[key])) {
2941
+ aDiff[key] = a[key];
2942
+ bDiff[key] = b[key];
2943
+ }
2944
+ }
2945
+ }
2946
+ return [aDiff, bDiff];
2947
+ };
2948
+ const isObject = (x) => typeof x === 'object' && !!x;
2751
2949
 
2752
2950
  let idTicker$4 = 0;
2753
2951
  /**
@@ -2869,6 +3067,12 @@ class DiagramConnection extends DiagramElement {
2869
3067
  updateInView() {
2870
3068
  this.model.canvas?.updateConnectionsInView(this.id);
2871
3069
  }
3070
+ /**
3071
+ * Set the start of this connection to the given port or reset this connection's starting port if `undefined`.
3072
+ * Add or remove this connection from the list of outgoing connections of ports correspondingly.
3073
+ * @public
3074
+ * @param start A port.
3075
+ */
2872
3076
  setStart(start) {
2873
3077
  if (this.start !== start) {
2874
3078
  if (this.start !== undefined) {
@@ -2887,6 +3091,12 @@ class DiagramConnection extends DiagramElement {
2887
3091
  }
2888
3092
  this.updateInView();
2889
3093
  }
3094
+ /**
3095
+ * Set the end of this connection to the given port or reset this connection's ending port if `undefined`.
3096
+ * Add or remove this connection from the list of incoming connections of ports correspondingly.
3097
+ * @public
3098
+ * @param end A port.
3099
+ */
2890
3100
  setEnd(end) {
2891
3101
  if (this.end !== end) {
2892
3102
  if (this.end !== undefined) {
@@ -2905,6 +3115,11 @@ class DiagramConnection extends DiagramElement {
2905
3115
  }
2906
3116
  this.updateInView();
2907
3117
  }
3118
+ /**
3119
+ * Reassign the start and end ports of this connection to ports with less distance between them that have the same root element.
3120
+ * If no ports with less distance between them are found, do nothing.
3121
+ * @public
3122
+ */
2908
3123
  tighten() {
2909
3124
  if (this.start?.rootElement && this.end?.rootElement) {
2910
3125
  const tighterStartPort =
@@ -2942,15 +3157,44 @@ class DiagramConnection extends DiagramElement {
2942
3157
  }
2943
3158
  }
2944
3159
  class DiagramConnectionSet extends DiagramEntitySet {
3160
+ /**
3161
+ * Instance a set of connections for the given model. This method is used internally.
3162
+ * @private
3163
+ */
2945
3164
  constructor(model) {
2946
3165
  super();
3166
+ /**
3167
+ * Set of the possible types of connection that the connections of this set can have.
3168
+ * @public
3169
+ */
2947
3170
  this.types = new DiagramEntitySet();
2948
3171
  this.model = model;
2949
3172
  }
3173
+ /**
3174
+ * Instance a new connection and add it to this set.
3175
+ * @public
3176
+ * @param type The type of the connection given as either the type itself or the id of the type.
3177
+ * @param start The start port of the connection.
3178
+ * @param end The end port of the connection.
3179
+ * @param id The id of the connection. Should be left undefined unless a specific id is required.
3180
+ * @returns The instanced connection.
3181
+ */
2950
3182
  new(type, start, end, id) {
2951
- const connection = new DiagramConnection(this.model, type, start, end, id);
3183
+ let connectionType;
3184
+ if (type instanceof DiagramConnectionType) {
3185
+ connectionType = type;
3186
+ }
3187
+ else {
3188
+ const foundConnectionType = this.types.get(type);
3189
+ if (foundConnectionType === undefined) {
3190
+ throw new TypeError(`Connection type with id '${type}' could not be found.`);
3191
+ }
3192
+ connectionType = foundConnectionType;
3193
+ }
3194
+ const connection = new DiagramConnection(this.model, connectionType, start, end, id);
2952
3195
  super.add(connection);
2953
3196
  connection.updateInView();
3197
+ connection.valueSet.resetValues();
2954
3198
  return connection;
2955
3199
  }
2956
3200
  remove(id) {
@@ -2965,30 +3209,6 @@ class DiagramConnectionSet extends DiagramEntitySet {
2965
3209
  connection.updateInView();
2966
3210
  }
2967
3211
  }
2968
- filter(type, threshold) {
2969
- return this.entities.filter((e) => {
2970
- let matches = true;
2971
- if (type !== undefined && matches) {
2972
- matches = matches && e.type.id === type;
2973
- }
2974
- if (threshold !== undefined && matches) {
2975
- matches = matches && e.getPriority() >= threshold;
2976
- }
2977
- return matches;
2978
- });
2979
- }
2980
- find(type, threshold) {
2981
- return this.entities.find((e) => {
2982
- let matches = true;
2983
- if (type !== undefined && matches) {
2984
- matches = matches && e.type.id === type;
2985
- }
2986
- if (threshold !== undefined && matches) {
2987
- matches = matches && e.getPriority() >= threshold;
2988
- }
2989
- return matches;
2990
- });
2991
- }
2992
3212
  }
2993
3213
 
2994
3214
  let idTicker$3 = 0;
@@ -3100,6 +3320,11 @@ class DiagramSection extends DiagramElement {
3100
3320
  getPriority() {
3101
3321
  return (this.node?.type?.sectionGrid?.sections?.[this.indexYInNode]?.[this.indexXInNode]?.priority || DIAGRAM_SECTION_DEFAULTS.priority);
3102
3322
  }
3323
+ /**
3324
+ * Get the port of this section which is closest to the given coordinates.
3325
+ * @param coords A point in the diagram.
3326
+ * @returns The port of this section closest to the given coordinates, `undefined` if this section has no ports.
3327
+ */
3103
3328
  getClosestPortToPoint(coords) {
3104
3329
  if (this.ports.length === 0) {
3105
3330
  return undefined;
@@ -3114,6 +3339,11 @@ class DiagramSection extends DiagramElement {
3114
3339
  return closestPort;
3115
3340
  }
3116
3341
  }
3342
+ /**
3343
+ * Get all incoming connections of all ports of this section.
3344
+ * @public
3345
+ * @returns A list of connections.
3346
+ */
3117
3347
  getIncomingConnections() {
3118
3348
  const result = [];
3119
3349
  for (const port of this.ports) {
@@ -3123,6 +3353,11 @@ class DiagramSection extends DiagramElement {
3123
3353
  }
3124
3354
  return result;
3125
3355
  }
3356
+ /**
3357
+ * Get all outgoing connections of all ports of this section.
3358
+ * @public
3359
+ * @returns A list of connections.
3360
+ */
3126
3361
  getOutgoingConnections() {
3127
3362
  const result = [];
3128
3363
  for (const port of this.ports) {
@@ -3132,6 +3367,11 @@ class DiagramSection extends DiagramElement {
3132
3367
  }
3133
3368
  return result;
3134
3369
  }
3370
+ /**
3371
+ * Get all connections of all ports of this section.
3372
+ * @public
3373
+ * @returns A list of connections.
3374
+ */
3135
3375
  getConnections() {
3136
3376
  const result = [];
3137
3377
  for (const port of this.ports) {
@@ -3144,6 +3384,11 @@ class DiagramSection extends DiagramElement {
3144
3384
  }
3145
3385
  return result;
3146
3386
  }
3387
+ /**
3388
+ * Change the coordinates of this section to the given coordinates and move its ports and labels correspondingly.
3389
+ * @public
3390
+ * @param coords A point in the diagram.
3391
+ */
3147
3392
  move(coords) {
3148
3393
  const coordDifferences = [
3149
3394
  coords[0] - this.coords[0],
@@ -3165,6 +3410,12 @@ class DiagramSection extends DiagramElement {
3165
3410
  this.getConnections().forEach((c) => c.tighten());
3166
3411
  this.updateInView();
3167
3412
  }
3413
+ /**
3414
+ * Change the dimensions of this section in the given direction by the given amount.
3415
+ * @public
3416
+ * @param direction A direction.
3417
+ * @param distance A distance.
3418
+ */
3168
3419
  stretch(direction, distance) {
3169
3420
  const oldCoordsX = [this.coords[0], this.coords[0] + this.width];
3170
3421
  const oldCoordsY = [this.coords[1], this.coords[1] + this.height];
@@ -3221,11 +3472,19 @@ class DiagramSection extends DiagramElement {
3221
3472
  }
3222
3473
  }
3223
3474
  class DiagramSectionSet extends DiagramEntitySet {
3475
+ /**
3476
+ * Instance a set of sections for the given model. This method is used internally.
3477
+ * @private
3478
+ */
3224
3479
  constructor(model) {
3225
3480
  super();
3226
3481
  this.model = model;
3227
3482
  }
3228
- new(node, indexXInNode, indexYInNode, coords, width, height, id, portIds, portLabelIds) {
3483
+ /**
3484
+ * Instance a new section and add it to this set. This method is normally called when instancing an element with a section and it is rarely called by itself.
3485
+ * @private
3486
+ */
3487
+ new(node, indexXInNode, indexYInNode, coords, width, height, id) {
3229
3488
  const section = new DiagramSection(this.model, node, indexXInNode, indexYInNode, coords, width, height, id);
3230
3489
  super.add(section);
3231
3490
  section.updateInView();
@@ -3240,7 +3499,7 @@ class DiagramSectionSet extends DiagramEntitySet {
3240
3499
  const port = this.model.ports.new(section, [
3241
3500
  section.coords[0] + (portConfig?.coords?.[0] || 0),
3242
3501
  section.coords[1] + (portConfig?.coords?.[1] || 0)
3243
- ], portConfig?.direction, portIds && portIds.length > i ? portIds[i] : undefined);
3502
+ ], portConfig?.direction);
3244
3503
  if (portConfig.label) {
3245
3504
  const labelConfiguration = {
3246
3505
  ...DIAGRAM_FIELD_DEFAULTS,
@@ -3270,9 +3529,7 @@ class DiagramSectionSet extends DiagramEntitySet {
3270
3529
  default:
3271
3530
  labelCoords = port.coords;
3272
3531
  }
3273
- this.model.fields.new(port, labelCoords, labelConfiguration.fontSize, labelConfiguration.fontFamily, labelConfiguration.color, labelConfiguration.selectedColor, labelConfiguration.fontSize, labelConfiguration.fontSize, labelConfiguration.horizontalAlign, labelConfiguration.verticalAlign, '', labelConfiguration.editable, labelConfiguration.fit, portLabelIds && portLabelIds.length > i
3274
- ? portLabelIds[i]
3275
- : undefined);
3532
+ this.model.fields.new(port, labelCoords, labelConfiguration.fontSize, labelConfiguration.fontFamily, labelConfiguration.color, labelConfiguration.selectedColor, labelConfiguration.fontSize, labelConfiguration.fontSize, labelConfiguration.horizontalAlign, labelConfiguration.verticalAlign, '', labelConfiguration.editable, labelConfiguration.fit);
3276
3533
  }
3277
3534
  }
3278
3535
  }
@@ -3286,7 +3543,7 @@ class DiagramSectionSet extends DiagramEntitySet {
3286
3543
  this.model.fields.new(section, [
3287
3544
  section.coords[0] + labelConfiguration.margin,
3288
3545
  section.coords[1] + labelConfiguration.margin
3289
- ], labelConfiguration.fontSize, labelConfiguration.fontFamily, labelConfiguration.color, labelConfiguration.selectedColor, section.width - labelConfiguration.margin * 2, section.height - labelConfiguration.margin * 2, labelConfiguration.horizontalAlign, labelConfiguration.verticalAlign, '', labelConfiguration.editable, labelConfiguration.fit, id);
3546
+ ], labelConfiguration.fontSize, labelConfiguration.fontFamily, labelConfiguration.color, labelConfiguration.selectedColor, section.width - labelConfiguration.margin * 2, section.height - labelConfiguration.margin * 2, labelConfiguration.horizontalAlign, labelConfiguration.verticalAlign, '', labelConfiguration.editable, labelConfiguration.fit);
3290
3547
  }
3291
3548
  return section;
3292
3549
  }
@@ -3311,22 +3568,6 @@ class DiagramSectionSet extends DiagramEntitySet {
3311
3568
  section.updateInView();
3312
3569
  }
3313
3570
  }
3314
- filter(threshold) {
3315
- return this.entities.filter((e) => {
3316
- if (threshold !== undefined) {
3317
- return e.getPriority() >= threshold;
3318
- }
3319
- return true;
3320
- });
3321
- }
3322
- find(threshold) {
3323
- return this.entities.find((e) => {
3324
- if (threshold !== undefined) {
3325
- return e.getPriority() >= threshold;
3326
- }
3327
- return true;
3328
- });
3329
- }
3330
3571
  }
3331
3572
 
3332
3573
  let idTicker$2 = 0;
@@ -3446,6 +3687,11 @@ class DiagramNode extends DiagramElement {
3446
3687
  getPriority() {
3447
3688
  return this.type.priority;
3448
3689
  }
3690
+ /**
3691
+ * Get the port of this node which is closest to the given coordinates.
3692
+ * @param coords A point in the diagram.
3693
+ * @returns The port of this node closest to the given coordinates, `undefined` if this node has no ports.
3694
+ */
3449
3695
  getClosestPortToPoint(coords) {
3450
3696
  if (this.ports.length === 0) {
3451
3697
  return undefined;
@@ -3460,6 +3706,11 @@ class DiagramNode extends DiagramElement {
3460
3706
  return closestPort;
3461
3707
  }
3462
3708
  }
3709
+ /**
3710
+ * Get all incoming connections of all ports of this node, not including incoming connections of sections of this node.
3711
+ * @public
3712
+ * @returns A list of connections.
3713
+ */
3463
3714
  getIncomingConnections() {
3464
3715
  const result = [];
3465
3716
  for (const port of this.ports) {
@@ -3469,6 +3720,11 @@ class DiagramNode extends DiagramElement {
3469
3720
  }
3470
3721
  return result;
3471
3722
  }
3723
+ /**
3724
+ * Get all outgoing connections of all ports of this node, not including outgoing connections of sections of this node.
3725
+ * @public
3726
+ * @returns A list of connections.
3727
+ */
3472
3728
  getOutgoingConnections() {
3473
3729
  const result = [];
3474
3730
  for (const port of this.ports) {
@@ -3478,6 +3734,11 @@ class DiagramNode extends DiagramElement {
3478
3734
  }
3479
3735
  return result;
3480
3736
  }
3737
+ /**
3738
+ * Get all connections of all ports of this node, not including connections of sections of this node.
3739
+ * @public
3740
+ * @returns A list of connections.
3741
+ */
3481
3742
  getConnections() {
3482
3743
  const result = [];
3483
3744
  for (const port of this.ports) {
@@ -3490,6 +3751,11 @@ class DiagramNode extends DiagramElement {
3490
3751
  }
3491
3752
  return result;
3492
3753
  }
3754
+ /**
3755
+ * Get all nodes that have a direct connection to this node, not including nodes that have a direct connection to sections of this node.
3756
+ * @public
3757
+ * @returns A list of nodes.
3758
+ */
3493
3759
  getAdjacentNodes() {
3494
3760
  const result = [];
3495
3761
  for (const port of this.ports) {
@@ -3508,6 +3774,11 @@ class DiagramNode extends DiagramElement {
3508
3774
  }
3509
3775
  return result;
3510
3776
  }
3777
+ /**
3778
+ * Change the coordinates of this node to the given coordinates and move its sections, ports and labels correspondingly.
3779
+ * @public
3780
+ * @param coords A point in the diagram.
3781
+ */
3511
3782
  move(coords) {
3512
3783
  const coordDifferences = [
3513
3784
  coords[0] - this.coords[0],
@@ -3535,6 +3806,12 @@ class DiagramNode extends DiagramElement {
3535
3806
  this.getConnections().forEach((c) => c.tighten());
3536
3807
  this.updateInView();
3537
3808
  }
3809
+ /**
3810
+ * Change the dimensions of this node in the given direction by the given amount without changing the dimensions of the sections of this node.
3811
+ * @public
3812
+ * @param direction A direction.
3813
+ * @param distance A distance.
3814
+ */
3538
3815
  stretch(direction, distance) {
3539
3816
  const oldCoordsX = [this.coords[0], this.coords[0] + this.width];
3540
3817
  const oldCoordsY = [this.coords[1], this.coords[1] + this.height];
@@ -3588,6 +3865,14 @@ class DiagramNode extends DiagramElement {
3588
3865
  // we ignore this.sections, the stretching of a node with sections is handled in the stretchSections method
3589
3866
  this.updateInView();
3590
3867
  }
3868
+ /**
3869
+ * Change the dimensions of this node and its sections in the given direction by the given amount, with the sections of the given index being stretched.
3870
+ * @public
3871
+ * @param direction A direction.
3872
+ * @param distance A distance.
3873
+ * @param indexX A section index.
3874
+ * @param indexY A section index.
3875
+ */
3591
3876
  stretchSections(direction, distance, indexX, indexY) {
3592
3877
  let minimumStretchableDistance = Number.NEGATIVE_INFINITY;
3593
3878
  switch (direction) {
@@ -3668,50 +3953,71 @@ class DiagramNode extends DiagramElement {
3668
3953
  }
3669
3954
  }
3670
3955
  class DiagramNodeSet extends DiagramEntitySet {
3956
+ /**
3957
+ * Instance a set of nodes for the given model. This method is used internally.
3958
+ * @private
3959
+ */
3671
3960
  constructor(model) {
3672
3961
  super();
3962
+ /**
3963
+ * Set of the possible types of node that the nodes of this set can have.
3964
+ * @public
3965
+ */
3673
3966
  this.types = new DiagramEntitySet();
3674
3967
  this.model = model;
3675
3968
  }
3676
- new(type, coords, id, sectionIds, sectionPortIds, sectionPortLabelIds, portIds, portLabelIds, labelId) {
3677
- const node = new DiagramNode(this.model, type, coords, id);
3969
+ /**
3970
+ * Instance a new node and add it to this set.
3971
+ * @public
3972
+ * @param type The type of the node given as either the type itself or the id of the type.
3973
+ * @param coords The coordinates of the top left corner of the node in the diagram.
3974
+ * @param id The id of the node. Should be left undefined unless a specific id is required.
3975
+ * @returns The instanced node.
3976
+ */
3977
+ new(type, coords, id) {
3978
+ let nodeType;
3979
+ if (type instanceof DiagramNodeType) {
3980
+ nodeType = type;
3981
+ }
3982
+ else {
3983
+ const foundNodeType = this.types.get(type);
3984
+ if (foundNodeType === undefined) {
3985
+ throw new TypeError(`Node type with id '${type}' could not be found.`);
3986
+ }
3987
+ nodeType = foundNodeType;
3988
+ }
3989
+ const node = new DiagramNode(this.model, nodeType, coords, id);
3678
3990
  super.add(node);
3679
3991
  node.updateInView();
3680
3992
  // add node sections
3681
- if (type.sectionGrid !== null) {
3993
+ if (nodeType.sectionGrid !== null) {
3682
3994
  // create sections
3683
- let sectionCount = 0;
3684
- let heightAccumulator = node.coords[1] + (type.sectionGrid.margin || 0);
3685
- for (let j = 0; j < type.sectionGrid.sections.length; ++j) {
3686
- let widthAccumulator = node.coords[0] + (type.sectionGrid.margin || 0);
3687
- for (let i = 0; i < type.sectionGrid.sections[j].length; ++i) {
3688
- this.model.sections.new(node, i, j, [widthAccumulator, heightAccumulator], type.sectionGrid.defaultWidths?.[i] ||
3689
- DIAGRAM_SECTION_DEFAULT_WIDTH, type.sectionGrid.defaultHeights?.[j] ||
3690
- DIAGRAM_SECTION_DEFAULT_HEIGHT, sectionIds && sectionIds.length > sectionCount
3691
- ? sectionIds[sectionCount]
3692
- : undefined, sectionPortIds && sectionPortIds.length > sectionCount
3693
- ? sectionPortIds[sectionCount]
3694
- : undefined, sectionPortLabelIds && sectionPortLabelIds.length > sectionCount
3695
- ? sectionPortLabelIds[sectionCount]
3696
- : undefined);
3697
- ++sectionCount;
3995
+ let heightAccumulator = node.coords[1] + (nodeType.sectionGrid.margin || 0);
3996
+ for (let j = 0; j < nodeType.sectionGrid.sections.length; ++j) {
3997
+ let widthAccumulator = node.coords[0] + (nodeType.sectionGrid.margin || 0);
3998
+ for (let i = 0; i < nodeType.sectionGrid.sections[j].length; ++i) {
3999
+ this.model.sections.new(node, i, j, [widthAccumulator, heightAccumulator], nodeType.sectionGrid.defaultWidths?.[i] ||
4000
+ DIAGRAM_SECTION_DEFAULT_WIDTH, nodeType.sectionGrid.defaultHeights?.[j] ||
4001
+ DIAGRAM_SECTION_DEFAULT_HEIGHT);
3698
4002
  widthAccumulator +=
3699
- (type.sectionGrid.defaultWidths?.[i] ||
3700
- DIAGRAM_SECTION_DEFAULT_WIDTH) + (type.sectionGrid.margin || 0);
4003
+ (nodeType.sectionGrid.defaultWidths?.[i] ||
4004
+ DIAGRAM_SECTION_DEFAULT_WIDTH) +
4005
+ (nodeType.sectionGrid.margin || 0);
3701
4006
  }
3702
4007
  heightAccumulator +=
3703
- (type.sectionGrid.defaultHeights?.[j] ||
3704
- DIAGRAM_SECTION_DEFAULT_HEIGHT) + (type.sectionGrid.margin || 0);
4008
+ (nodeType.sectionGrid.defaultHeights?.[j] ||
4009
+ DIAGRAM_SECTION_DEFAULT_HEIGHT) +
4010
+ (nodeType.sectionGrid.margin || 0);
3705
4011
  }
3706
4012
  }
3707
4013
  // add node ports
3708
- if (type.ports.length > 0) {
3709
- for (let i = 0; i < type.ports.length; ++i) {
3710
- const portConfig = type.ports[i];
4014
+ if (nodeType.ports.length > 0) {
4015
+ for (let i = 0; i < nodeType.ports.length; ++i) {
4016
+ const portConfig = nodeType.ports[i];
3711
4017
  const port = this.model.ports.new(node, [
3712
4018
  node.coords[0] + portConfig.coords[0],
3713
4019
  node.coords[1] + portConfig.coords[1]
3714
- ], portConfig.direction, portIds && portIds.length > i ? portIds[i] : undefined);
4020
+ ], portConfig.direction);
3715
4021
  if (portConfig.label) {
3716
4022
  const labelConfiguration = {
3717
4023
  ...DIAGRAM_FIELD_DEFAULTS,
@@ -3741,23 +4047,22 @@ class DiagramNodeSet extends DiagramEntitySet {
3741
4047
  default:
3742
4048
  labelCoords = port.coords;
3743
4049
  }
3744
- this.model.fields.new(port, labelCoords, labelConfiguration.fontSize, labelConfiguration.fontFamily, labelConfiguration.color, labelConfiguration.selectedColor, labelConfiguration.fontSize, labelConfiguration.fontSize, labelConfiguration.horizontalAlign, labelConfiguration.verticalAlign, '', labelConfiguration.editable, labelConfiguration.fit, portLabelIds && portLabelIds.length > i
3745
- ? portLabelIds[i]
3746
- : undefined);
4050
+ this.model.fields.new(port, labelCoords, labelConfiguration.fontSize, labelConfiguration.fontFamily, labelConfiguration.color, labelConfiguration.selectedColor, labelConfiguration.fontSize, labelConfiguration.fontSize, labelConfiguration.horizontalAlign, labelConfiguration.verticalAlign, '', labelConfiguration.editable, labelConfiguration.fit);
3747
4051
  }
3748
4052
  }
3749
4053
  }
3750
4054
  // add node label
3751
- if (type.label) {
4055
+ if (nodeType.label) {
3752
4056
  const labelConfiguration = {
3753
4057
  ...DIAGRAM_FIELD_DEFAULTS,
3754
- ...type.label
4058
+ ...nodeType.label
3755
4059
  };
3756
4060
  this.model.fields.new(node, [
3757
4061
  node.coords[0] + labelConfiguration.margin,
3758
4062
  node.coords[1] + labelConfiguration.margin
3759
- ], labelConfiguration.fontSize, labelConfiguration.fontFamily, labelConfiguration.color, labelConfiguration.selectedColor, node.width - labelConfiguration.margin * 2, node.height - labelConfiguration.margin * 2, labelConfiguration.horizontalAlign, labelConfiguration.verticalAlign, '', labelConfiguration.editable, labelConfiguration.fit, labelId);
4063
+ ], labelConfiguration.fontSize, labelConfiguration.fontFamily, labelConfiguration.color, labelConfiguration.selectedColor, node.width - labelConfiguration.margin * 2, node.height - labelConfiguration.margin * 2, labelConfiguration.horizontalAlign, labelConfiguration.verticalAlign, '', labelConfiguration.editable, labelConfiguration.fit);
3760
4064
  }
4065
+ node.valueSet.resetValues();
3761
4066
  node.model.canvas?.fitNodeInView(node.id);
3762
4067
  return node;
3763
4068
  }
@@ -3782,14 +4087,6 @@ class DiagramNodeSet extends DiagramEntitySet {
3782
4087
  node.updateInView();
3783
4088
  }
3784
4089
  }
3785
- filter(type, threshold) {
3786
- return this.entities.filter((e) => (type !== undefined ? e.type.id === type : true) &&
3787
- (threshold !== undefined ? e.type.priority >= threshold : true));
3788
- }
3789
- find(type, threshold) {
3790
- return this.entities.find((e) => (type !== undefined ? e.type.id === type : true) &&
3791
- (threshold !== undefined ? e.type.priority >= threshold : true));
3792
- }
3793
4090
  }
3794
4091
 
3795
4092
  let idTicker$1 = 0;
@@ -3839,12 +4136,27 @@ class DiagramPort extends DiagramElement {
3839
4136
  updateInView() {
3840
4137
  this.model.canvas?.updatePortsInView(this.id);
3841
4138
  }
4139
+ /**
4140
+ * Add a connection to this port's list of outgoing connections.
4141
+ * @public
4142
+ * @param connection A connection.
4143
+ */
3842
4144
  startConnection(connection) {
3843
4145
  this.outgoingConnections.push(connection);
3844
4146
  }
4147
+ /**
4148
+ * Add a connection to this port's list of incoming connections.
4149
+ * @public
4150
+ * @param connection A connection.
4151
+ */
3845
4152
  finishConnection(connection) {
3846
4153
  this.incomingConnections.push(connection);
3847
4154
  }
4155
+ /**
4156
+ * Get the root node of this port, which is either its rootElement if it's a node or it's rootElement's node if it's a section.
4157
+ * @public
4158
+ * @returns A node if it could be found, `undefined` otherwise.
4159
+ */
3848
4160
  getNode() {
3849
4161
  if (this.rootElement instanceof DiagramNode) {
3850
4162
  return this.rootElement;
@@ -3857,6 +4169,11 @@ class DiagramPort extends DiagramElement {
3857
4169
  getPriority() {
3858
4170
  return this.rootElement?.getPriority() || DEFAULT_PRIORITY;
3859
4171
  }
4172
+ /**
4173
+ * Change the coordinates of this port to the given coordinates and move its labels correspondingly.
4174
+ * @public
4175
+ * @param coords A point in the diagram.
4176
+ */
3860
4177
  move(coords) {
3861
4178
  const coordDifferences = [
3862
4179
  coords[0] - this.coords[0],
@@ -3882,10 +4199,18 @@ class DiagramPort extends DiagramElement {
3882
4199
  }
3883
4200
  }
3884
4201
  class DiagramPortSet extends DiagramEntitySet {
4202
+ /**
4203
+ * Instance a set of ports for the given model. This method is used internally.
4204
+ * @private
4205
+ */
3885
4206
  constructor(model) {
3886
4207
  super();
3887
4208
  this.model = model;
3888
4209
  }
4210
+ /**
4211
+ * Instance a new port and add it to this set. This method is normally called when instancing an element with a port and it is rarely called by itself.
4212
+ * @private
4213
+ */
3889
4214
  new(rootElement, coords, direction, id) {
3890
4215
  const port = new DiagramPort(this.model, rootElement, coords, direction, id);
3891
4216
  super.add(port);
@@ -3921,22 +4246,6 @@ class DiagramPortSet extends DiagramEntitySet {
3921
4246
  port.updateInView();
3922
4247
  }
3923
4248
  }
3924
- filter(threshold) {
3925
- return this.entities.filter((e) => {
3926
- if (threshold !== undefined) {
3927
- return e.getPriority() >= threshold;
3928
- }
3929
- return true;
3930
- });
3931
- }
3932
- find(threshold) {
3933
- return this.entities.find((e) => {
3934
- if (threshold !== undefined) {
3935
- return e.getPriority() >= threshold;
3936
- }
3937
- return true;
3938
- });
3939
- }
3940
4249
  }
3941
4250
 
3942
4251
  let idTicker = 0;
@@ -4016,6 +4325,11 @@ class DiagramField extends DiagramElement {
4016
4325
  updateInView() {
4017
4326
  this.model.canvas?.updateFieldsInView(this.id);
4018
4327
  }
4328
+ /**
4329
+ * Change the coordinates of this field to the given coordinates.
4330
+ * @public
4331
+ * @param coords A point in the diagram.
4332
+ */
4019
4333
  move(coords) {
4020
4334
  this.coords = coords;
4021
4335
  this.updateInView();
@@ -4025,10 +4339,18 @@ class DiagramField extends DiagramElement {
4025
4339
  }
4026
4340
  }
4027
4341
  class DiagramFieldSet extends DiagramEntitySet {
4342
+ /**
4343
+ * Instance a set of fields for the given model. This method is used internally.
4344
+ * @private
4345
+ */
4028
4346
  constructor(model) {
4029
4347
  super();
4030
4348
  this.model = model;
4031
4349
  }
4350
+ /**
4351
+ * Instance a new field and add it to this set. This method is normally called when instancing an element with a field and it is rarely called by itself.
4352
+ * @private
4353
+ */
4032
4354
  new(rootElement, coords, fontSize, fontFamily, color, selectedColor, width, height, horizontalAlign, verticalAlign, text, editable, fit, id) {
4033
4355
  const field = new DiagramField(this.model, rootElement, coords, width, height, fontSize, fontFamily, color, selectedColor, horizontalAlign, verticalAlign, text, editable, fit, id);
4034
4356
  super.add(field);
@@ -4056,26 +4378,11 @@ class DiagramFieldSet extends DiagramEntitySet {
4056
4378
  field.updateInView();
4057
4379
  }
4058
4380
  }
4059
- filter(threshold) {
4060
- return this.entities.filter((e) => {
4061
- if (threshold !== undefined) {
4062
- return e.getPriority() >= threshold;
4063
- }
4064
- return true;
4065
- });
4066
- }
4067
- find(threshold) {
4068
- return this.entities.find((e) => {
4069
- if (threshold !== undefined) {
4070
- return e.getPriority() >= threshold;
4071
- }
4072
- return true;
4073
- });
4074
- }
4075
4381
  }
4076
4382
 
4077
4383
  /**
4078
4384
  * Stores the data of a diagram.
4385
+ * Represents the state of the diagram.
4079
4386
  * @public
4080
4387
  */
4081
4388
  class DiagramModel {
@@ -4117,6 +4424,7 @@ class DiagramModel {
4117
4424
  this.type = type;
4118
4425
  this.createdAt = new Date();
4119
4426
  this.updatedAt = new Date();
4427
+ this.logicalClock = 0;
4120
4428
  this.valueSet = new ValueSet(new PropertySet(properties), this);
4121
4429
  }
4122
4430
  /**
@@ -4130,6 +4438,7 @@ class DiagramModel {
4130
4438
  this.description = undefined;
4131
4439
  this.createdAt = new Date();
4132
4440
  this.updatedAt = new Date();
4441
+ this.logicalClock = 0;
4133
4442
  this.nodes.clear();
4134
4443
  this.sections.clear();
4135
4444
  this.ports.clear();
@@ -4140,21 +4449,77 @@ class DiagramModel {
4140
4449
  }
4141
4450
  }
4142
4451
 
4143
- var CursorStyle;
4144
- (function (CursorStyle) {
4145
- CursorStyle["AllScroll"] = "all-scroll";
4146
- CursorStyle["Auto"] = "auto";
4147
- CursorStyle["EWResize"] = "ew-resize";
4148
- CursorStyle["Grab"] = "grab";
4149
- CursorStyle["Grabbing"] = "grabbing";
4150
- CursorStyle["Move"] = "move";
4151
- CursorStyle["NoDrop"] = "no-drop";
4152
- CursorStyle["NSResize"] = "ns-resize";
4153
- CursorStyle["NotAllowed"] = "not-allowed";
4154
- CursorStyle["ZoomIn"] = "zoom-in";
4155
- CursorStyle["ZoomOut"] = "zoom-out";
4156
- })(CursorStyle || (CursorStyle = {}));
4157
-
4452
+ /**
4453
+ * A collaboration engine, which manages collaboration for a diagram.
4454
+ * @private
4455
+ */
4456
+ class CollabEngine {
4457
+ constructor(canvas) {
4458
+ /**
4459
+ * Whether we have joined (or are joining) a room on the server.
4460
+ */
4461
+ this.isInRoom = false;
4462
+ this.canvas = canvas;
4463
+ this.replicaId = v4();
4464
+ }
4465
+ /**
4466
+ * Returns a fresh timestamp, suitable for use by new CollabActions.
4467
+ */
4468
+ freshTimestamp() {
4469
+ this.canvas.model.logicalClock++;
4470
+ return [this.canvas.model.logicalClock, this.replicaId];
4471
+ }
4472
+ /**
4473
+ * Returns a fresh unique ID, suitable for use by new CollabActions.
4474
+ */
4475
+ freshId() {
4476
+ // For more compressible saves, consider using a [dot ID](https://mattweidner.com/2023/09/26/crdt-survey-3.html#unique-ids-dots) instead of a new UUID each time.
4477
+ return v4();
4478
+ }
4479
+ /**
4480
+ * Performs the action - immediately locally, and eventually for remote collaborators.
4481
+ */
4482
+ doCollaboratively(action) {
4483
+ // Perform the action locally.
4484
+ // Note: if this errors, it will prevent the action from performing collaboratively.
4485
+ action.do();
4486
+ if (this.onSend) {
4487
+ // Send the action to the server.
4488
+ const serialized = action.serialize();
4489
+ this.onSend(serialized);
4490
+ }
4491
+ }
4492
+ /**
4493
+ * Processes a message received from the server.
4494
+ *
4495
+ * The message is assumed to be non-redundant (in particular, not one of
4496
+ * our own messages) and delivered after all prior messages on the server.
4497
+ */
4498
+ receive(message) {
4499
+ // Update local logical clock.
4500
+ if ('timestamp' in message) {
4501
+ this.canvas.model.logicalClock = Math.max(this.canvas.model.logicalClock, message.timestamp[0]);
4502
+ }
4503
+ // Perform CollabAction.
4504
+ switch (message.type) {
4505
+ case 'add': {
4506
+ const action = AddNodeCollabAction.deserialize(this.canvas, message);
4507
+ action.do();
4508
+ break;
4509
+ }
4510
+ case 'move': {
4511
+ const action = MoveNodeCollabAction.deserialize(this.canvas, message);
4512
+ action.do();
4513
+ break;
4514
+ }
4515
+ default: {
4516
+ const exhaustiveMessage = message;
4517
+ console.error('Unknown CollabAction type, skipping:', exhaustiveMessage);
4518
+ }
4519
+ }
4520
+ }
4521
+ }
4522
+
4158
4523
  /**
4159
4524
  * Thickness of the invisible path around a connection used to make it easier to click on, in pixels.
4160
4525
  * @private
@@ -4206,6 +4571,7 @@ class DiagramCanvas {
4206
4571
  this.userActions = config.userActions || {};
4207
4572
  this.validators = [];
4208
4573
  this.actionQueue = new ActionQueue(this, ACTION_QUEUE_SIZE);
4574
+ this.collabEngine = new CollabEngine(this);
4209
4575
  // load node types
4210
4576
  if (config.nodeTypes) {
4211
4577
  for (const nodeTypeConfig of config.nodeTypes) {
@@ -4275,30 +4641,30 @@ class DiagramCanvas {
4275
4641
  this.canUserPerformAction(DiagramActions.Remove)) {
4276
4642
  // delete selection
4277
4643
  if (this.userSelection.length > 0) {
4278
- const nodesToBeDeleted = [];
4279
- const sectionsToBeDeleted = [];
4280
- const portsToBeDeleted = [];
4281
- const connectionsToBeDeleted = [];
4282
- const fieldsToBeDeleted = [];
4644
+ const nodeIdsToBeDeleted = [];
4645
+ const sectionIdsToBeDeleted = [];
4646
+ const portIdsToBeDeleted = [];
4647
+ const connectionIdsToBeDeleted = [];
4648
+ const fieldIdsToBeDeleted = [];
4283
4649
  for (const userSelectionElement of this.userSelection) {
4284
4650
  if (userSelectionElement instanceof DiagramNode) {
4285
- nodesToBeDeleted.push(userSelectionElement);
4651
+ nodeIdsToBeDeleted.push(userSelectionElement.id);
4286
4652
  }
4287
4653
  else if (userSelectionElement instanceof DiagramSection) {
4288
- sectionsToBeDeleted.push(userSelectionElement);
4654
+ sectionIdsToBeDeleted.push(userSelectionElement.id);
4289
4655
  }
4290
4656
  else if (userSelectionElement instanceof DiagramPort) {
4291
- portsToBeDeleted.push(userSelectionElement);
4657
+ portIdsToBeDeleted.push(userSelectionElement.id);
4292
4658
  }
4293
4659
  else if (userSelectionElement instanceof DiagramConnection) {
4294
- connectionsToBeDeleted.push(userSelectionElement);
4660
+ connectionIdsToBeDeleted.push(userSelectionElement.id);
4295
4661
  }
4296
4662
  else if (userSelectionElement instanceof DiagramField) {
4297
- fieldsToBeDeleted.push(userSelectionElement);
4663
+ fieldIdsToBeDeleted.push(userSelectionElement.id);
4298
4664
  }
4299
4665
  }
4300
- const removeAction = new RemoveAction(this.model, nodesToBeDeleted, sectionsToBeDeleted, portsToBeDeleted, connectionsToBeDeleted, fieldsToBeDeleted);
4301
- removeAction.redo();
4666
+ const removeAction = new RemoveAction(this.model, nodeIdsToBeDeleted, sectionIdsToBeDeleted, portIdsToBeDeleted, connectionIdsToBeDeleted, fieldIdsToBeDeleted);
4667
+ removeAction.do();
4302
4668
  this.actionQueue.add(removeAction);
4303
4669
  }
4304
4670
  this.cancelAllUserActions();
@@ -4487,6 +4853,13 @@ class DiagramCanvas {
4487
4853
  getPointerLocationRelativeToBody(event) {
4488
4854
  return d3.pointer(this.getEventHoldingCoordinates(event), d3.select('body').node());
4489
4855
  }
4856
+ getPointerLocationRelativeToScreen(event) {
4857
+ const pointerLocationRelativeToBody = this.getPointerLocationRelativeToBody(event);
4858
+ return [
4859
+ pointerLocationRelativeToBody[0] - window.scrollX,
4860
+ pointerLocationRelativeToBody[1] - window.scrollY
4861
+ ];
4862
+ }
4490
4863
  updateModelInView() {
4491
4864
  this.updateNodesInView();
4492
4865
  this.updateSectionsInView();
@@ -4497,7 +4870,10 @@ class DiagramCanvas {
4497
4870
  updateNodesInView(...ids) {
4498
4871
  let updateSelection = this.selectCanvasNodes()
4499
4872
  .selectAll('g.diagram-node')
4500
- .data(this.model.nodes.filter(undefined, this.priorityThreshold), (d) => d.id);
4873
+ .data(this.model.nodes.filter((e) => !e.removed &&
4874
+ (this.priorityThreshold !== undefined
4875
+ ? e.getPriority() >= this.priorityThreshold
4876
+ : true)), (d) => d.id);
4501
4877
  const exitSelection = updateSelection.exit();
4502
4878
  const enterSelection = updateSelection
4503
4879
  .enter()
@@ -4522,7 +4898,8 @@ class DiagramCanvas {
4522
4898
  .call(d3
4523
4899
  .drag()
4524
4900
  .on(DragEvents.Start, (event, d) => {
4525
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
4901
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
4902
+ !d.removed) {
4526
4903
  cursorStyle(CursorStyle.Grabbing);
4527
4904
  this.draggingFrom = [event.x, event.y];
4528
4905
  this.currentAction = new MoveNodeAction(this, d.id, d.coords, [0, 0]);
@@ -4532,7 +4909,8 @@ class DiagramCanvas {
4532
4909
  }
4533
4910
  })
4534
4911
  .on(DragEvents.Drag, (event, d) => {
4535
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
4912
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
4913
+ !d.removed) {
4536
4914
  const newNodeCoords = [
4537
4915
  event.x - d.width / 2,
4538
4916
  event.y - d.height / 2
@@ -4541,7 +4919,8 @@ class DiagramCanvas {
4541
4919
  }
4542
4920
  })
4543
4921
  .on(DragEvents.End, (event, d) => {
4544
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
4922
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
4923
+ !d.removed) {
4545
4924
  // prevent drag behavior if mouse hasn't moved
4546
4925
  if ((this.draggingFrom[0] !== event.x ||
4547
4926
  this.draggingFrom[1] !== event.y) &&
@@ -4554,10 +4933,8 @@ class DiagramCanvas {
4554
4933
  newNodeCoords = this.getClosestGridPoint(newNodeCoords);
4555
4934
  }
4556
4935
  this.currentAction.to = newNodeCoords;
4557
- this.currentAction.redo();
4936
+ this.currentAction.do();
4558
4937
  this.actionQueue.add(this.currentAction);
4559
- }
4560
- else {
4561
4938
  this.currentAction = undefined;
4562
4939
  }
4563
4940
  }
@@ -4621,13 +4998,15 @@ class DiagramCanvas {
4621
4998
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
4622
4999
  .on(Events.MouseOver, (event, d) => {
4623
5000
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4624
- d.type.resizableX) {
5001
+ d.type.resizableX &&
5002
+ !d.removed) {
4625
5003
  cursorStyle(CursorStyle.EWResize);
4626
5004
  }
4627
5005
  })
4628
5006
  .on(Events.MouseOut, (event, d) => {
4629
5007
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4630
- d.type.resizableX) {
5008
+ d.type.resizableX &&
5009
+ !d.removed) {
4631
5010
  cursorStyle();
4632
5011
  }
4633
5012
  })
@@ -4635,7 +5014,8 @@ class DiagramCanvas {
4635
5014
  .drag()
4636
5015
  .on(DragEvents.Start, (event, d) => {
4637
5016
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4638
- d.type.resizableX) {
5017
+ d.type.resizableX &&
5018
+ !d.removed) {
4639
5019
  cursorStyle(CursorStyle.EWResize);
4640
5020
  this.currentAction = new StretchNodeAction(this, d.id, Side.Left, d.width, d.width);
4641
5021
  }
@@ -4645,14 +5025,17 @@ class DiagramCanvas {
4645
5025
  })
4646
5026
  .on(DragEvents.Drag, (event, d) => {
4647
5027
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4648
- d.type.resizableX) {
5028
+ d.type.resizableX &&
5029
+ !d.removed) {
4649
5030
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4650
5031
  d.stretch(Side.Left, d.coords[0] - pointerCoords[0]);
4651
5032
  }
4652
5033
  })
4653
5034
  .on(DragEvents.End, (event, d) => {
4654
5035
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4655
- d.type.resizableX) {
5036
+ d.type.resizableX &&
5037
+ !d.removed &&
5038
+ this.currentAction instanceof StretchNodeAction) {
4656
5039
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4657
5040
  if (this.snapToGrid) {
4658
5041
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -4672,13 +5055,15 @@ class DiagramCanvas {
4672
5055
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
4673
5056
  .on(Events.MouseOver, (event, d) => {
4674
5057
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4675
- d.type.resizableY) {
5058
+ d.type.resizableY &&
5059
+ !d.removed) {
4676
5060
  cursorStyle(CursorStyle.NSResize);
4677
5061
  }
4678
5062
  })
4679
5063
  .on(Events.MouseOut, (event, d) => {
4680
5064
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4681
- d.type.resizableY) {
5065
+ d.type.resizableY &&
5066
+ !d.removed) {
4682
5067
  cursorStyle();
4683
5068
  }
4684
5069
  })
@@ -4686,7 +5071,8 @@ class DiagramCanvas {
4686
5071
  .drag()
4687
5072
  .on(DragEvents.Start, (event, d) => {
4688
5073
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4689
- d.type.resizableY) {
5074
+ d.type.resizableY &&
5075
+ !d.removed) {
4690
5076
  cursorStyle(CursorStyle.NSResize);
4691
5077
  this.currentAction = new StretchNodeAction(this, d.id, Side.Top, d.height, d.height);
4692
5078
  }
@@ -4696,14 +5082,17 @@ class DiagramCanvas {
4696
5082
  })
4697
5083
  .on(DragEvents.Drag, (event, d) => {
4698
5084
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4699
- d.type.resizableY) {
5085
+ d.type.resizableY &&
5086
+ !d.removed) {
4700
5087
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4701
5088
  d.stretch(Side.Top, d.coords[1] - pointerCoords[1]);
4702
5089
  }
4703
5090
  })
4704
5091
  .on(DragEvents.End, (event, d) => {
4705
5092
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4706
- d.type.resizableY) {
5093
+ d.type.resizableY &&
5094
+ !d.removed &&
5095
+ this.currentAction instanceof StretchNodeAction) {
4707
5096
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4708
5097
  if (this.snapToGrid) {
4709
5098
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -4723,13 +5112,15 @@ class DiagramCanvas {
4723
5112
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
4724
5113
  .on(Events.MouseOver, (event, d) => {
4725
5114
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4726
- d.type.resizableX) {
5115
+ d.type.resizableX &&
5116
+ !d.removed) {
4727
5117
  cursorStyle(CursorStyle.EWResize);
4728
5118
  }
4729
5119
  })
4730
5120
  .on(Events.MouseOut, (event, d) => {
4731
5121
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4732
- d.type.resizableX) {
5122
+ d.type.resizableX &&
5123
+ !d.removed) {
4733
5124
  cursorStyle();
4734
5125
  }
4735
5126
  })
@@ -4737,7 +5128,8 @@ class DiagramCanvas {
4737
5128
  .drag()
4738
5129
  .on(DragEvents.Start, (event, d) => {
4739
5130
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4740
- d.type.resizableX) {
5131
+ d.type.resizableX &&
5132
+ !d.removed) {
4741
5133
  cursorStyle(CursorStyle.EWResize);
4742
5134
  this.currentAction = new StretchNodeAction(this, d.id, Side.Right, d.width, d.width);
4743
5135
  }
@@ -4747,14 +5139,17 @@ class DiagramCanvas {
4747
5139
  })
4748
5140
  .on(DragEvents.Drag, (event, d) => {
4749
5141
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4750
- d.type.resizableX) {
5142
+ d.type.resizableX &&
5143
+ !d.removed) {
4751
5144
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4752
5145
  d.stretch(Side.Right, pointerCoords[0] - (d.coords[0] + d.width));
4753
5146
  }
4754
5147
  })
4755
5148
  .on(DragEvents.End, (event, d) => {
4756
5149
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4757
- d.type.resizableX) {
5150
+ d.type.resizableX &&
5151
+ !d.removed &&
5152
+ this.currentAction instanceof StretchNodeAction) {
4758
5153
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4759
5154
  if (this.snapToGrid) {
4760
5155
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -4774,13 +5169,15 @@ class DiagramCanvas {
4774
5169
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
4775
5170
  .on(Events.MouseOver, (event, d) => {
4776
5171
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4777
- d.type.resizableY) {
5172
+ d.type.resizableY &&
5173
+ !d.removed) {
4778
5174
  cursorStyle(CursorStyle.NSResize);
4779
5175
  }
4780
5176
  })
4781
5177
  .on(Events.MouseOut, (event, d) => {
4782
5178
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4783
- d.type.resizableY) {
5179
+ d.type.resizableY &&
5180
+ !d.removed) {
4784
5181
  cursorStyle();
4785
5182
  }
4786
5183
  })
@@ -4788,7 +5185,8 @@ class DiagramCanvas {
4788
5185
  .drag()
4789
5186
  .on(DragEvents.Start, (event, d) => {
4790
5187
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4791
- d.type.resizableY) {
5188
+ d.type.resizableY &&
5189
+ !d.removed) {
4792
5190
  cursorStyle(CursorStyle.NSResize);
4793
5191
  this.currentAction = new StretchNodeAction(this, d.id, Side.Bottom, d.height, d.height);
4794
5192
  }
@@ -4798,14 +5196,17 @@ class DiagramCanvas {
4798
5196
  })
4799
5197
  .on(DragEvents.Drag, (event, d) => {
4800
5198
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4801
- d.type.resizableY) {
5199
+ d.type.resizableY &&
5200
+ !d.removed) {
4802
5201
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4803
5202
  d.stretch(Side.Bottom, pointerCoords[1] - (d.coords[1] + d.height));
4804
5203
  }
4805
5204
  })
4806
5205
  .on(DragEvents.End, (event, d) => {
4807
5206
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4808
- d.type.resizableY) {
5207
+ d.type.resizableY &&
5208
+ !d.removed &&
5209
+ this.currentAction instanceof StretchNodeAction) {
4809
5210
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4810
5211
  if (this.snapToGrid) {
4811
5212
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -4817,7 +5218,9 @@ class DiagramCanvas {
4817
5218
  }
4818
5219
  cursorStyle();
4819
5220
  }));
4820
- mergeSelection.attr('transform', (d) => `translate(${d.coords[0]},${d.coords[1]})`);
5221
+ mergeSelection
5222
+ .attr('transform', (d) => `translate(${d.coords[0]},${d.coords[1]})`)
5223
+ .attr('opacity', (d) => (d.removed ? 0.5 : 1));
4821
5224
  mergeSelection
4822
5225
  .filter('.shaped-look')
4823
5226
  .select('path')
@@ -4981,7 +5384,10 @@ class DiagramCanvas {
4981
5384
  updateSectionsInView(...ids) {
4982
5385
  let updateSelection = this.selectCanvasSections()
4983
5386
  .selectAll('g.diagram-section')
4984
- .data(this.model.sections.filter(this.priorityThreshold), (d) => d.id);
5387
+ .data(this.model.sections.filter((e) => !e.removed &&
5388
+ (this.priorityThreshold !== undefined
5389
+ ? e.getPriority() >= this.priorityThreshold
5390
+ : true)), (d) => d.id);
4985
5391
  const exitSelection = updateSelection.exit();
4986
5392
  const enterSelection = updateSelection
4987
5393
  .enter()
@@ -5006,7 +5412,8 @@ class DiagramCanvas {
5006
5412
  .call(d3
5007
5413
  .drag()
5008
5414
  .on(DragEvents.Start, (event, d) => {
5009
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
5415
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
5416
+ !d.removed) {
5010
5417
  const node = d.node;
5011
5418
  cursorStyle(CursorStyle.Grabbing);
5012
5419
  this.draggingFrom = [event.x, event.y];
@@ -5017,7 +5424,8 @@ class DiagramCanvas {
5017
5424
  }
5018
5425
  })
5019
5426
  .on(DragEvents.Drag, (event, d) => {
5020
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
5427
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
5428
+ !d.removed) {
5021
5429
  const node = d.node;
5022
5430
  const newNodeCoords = [
5023
5431
  event.x - node.width / 2,
@@ -5027,7 +5435,8 @@ class DiagramCanvas {
5027
5435
  }
5028
5436
  })
5029
5437
  .on(DragEvents.End, (event, d) => {
5030
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
5438
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
5439
+ !d.removed) {
5031
5440
  const node = d.node;
5032
5441
  // prevent drag behavior if mouse hasn't moved
5033
5442
  if ((this.draggingFrom[0] !== event.x ||
@@ -5041,10 +5450,8 @@ class DiagramCanvas {
5041
5450
  newNodeCoords = this.getClosestGridPoint(newNodeCoords);
5042
5451
  }
5043
5452
  this.currentAction.to = newNodeCoords;
5044
- this.currentAction.redo();
5453
+ this.currentAction.do();
5045
5454
  this.actionQueue.add(this.currentAction);
5046
- }
5047
- else {
5048
5455
  this.currentAction = undefined;
5049
5456
  }
5050
5457
  }
@@ -5108,13 +5515,15 @@ class DiagramCanvas {
5108
5515
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
5109
5516
  .on(Events.MouseOver, (event, d) => {
5110
5517
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5111
- d.node?.type?.resizableX) {
5518
+ d.node?.type?.resizableX &&
5519
+ !d.removed) {
5112
5520
  cursorStyle(CursorStyle.EWResize);
5113
5521
  }
5114
5522
  })
5115
5523
  .on(Events.MouseOut, (event, d) => {
5116
5524
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5117
- d.node?.type?.resizableX) {
5525
+ d.node?.type?.resizableX &&
5526
+ !d.removed) {
5118
5527
  cursorStyle();
5119
5528
  }
5120
5529
  })
@@ -5122,7 +5531,8 @@ class DiagramCanvas {
5122
5531
  .drag()
5123
5532
  .on(DragEvents.Start, (event, d) => {
5124
5533
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5125
- d.node?.type?.resizableX) {
5534
+ d.node?.type?.resizableX &&
5535
+ !d.removed) {
5126
5536
  cursorStyle(CursorStyle.EWResize);
5127
5537
  this.currentAction = new StretchSectionAction(this, d.id, Side.Left, d.width, d.width);
5128
5538
  }
@@ -5132,14 +5542,17 @@ class DiagramCanvas {
5132
5542
  })
5133
5543
  .on(DragEvents.Drag, (event, d) => {
5134
5544
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5135
- d.node?.type?.resizableX) {
5545
+ d.node?.type?.resizableX &&
5546
+ !d.removed) {
5136
5547
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5137
5548
  d.node.stretchSections(Side.Left, d.coords[0] - pointerCoords[0], d.indexXInNode, d.indexYInNode);
5138
5549
  }
5139
5550
  })
5140
5551
  .on(DragEvents.End, (event, d) => {
5141
5552
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5142
- d.node?.type?.resizableX) {
5553
+ d.node?.type?.resizableX &&
5554
+ !d.removed &&
5555
+ this.currentAction instanceof StretchSectionAction) {
5143
5556
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5144
5557
  if (this.snapToGrid) {
5145
5558
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -5159,13 +5572,15 @@ class DiagramCanvas {
5159
5572
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
5160
5573
  .on(Events.MouseOver, (event, d) => {
5161
5574
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5162
- d.node?.type?.resizableY) {
5575
+ d.node?.type?.resizableY &&
5576
+ !d.removed) {
5163
5577
  cursorStyle(CursorStyle.NSResize);
5164
5578
  }
5165
5579
  })
5166
5580
  .on(Events.MouseOut, (event, d) => {
5167
5581
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5168
- d.node?.type?.resizableY) {
5582
+ d.node?.type?.resizableY &&
5583
+ !d.removed) {
5169
5584
  cursorStyle();
5170
5585
  }
5171
5586
  })
@@ -5173,7 +5588,8 @@ class DiagramCanvas {
5173
5588
  .drag()
5174
5589
  .on(DragEvents.Start, (event, d) => {
5175
5590
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5176
- d.node?.type?.resizableY) {
5591
+ d.node?.type?.resizableY &&
5592
+ !d.removed) {
5177
5593
  cursorStyle(CursorStyle.NSResize);
5178
5594
  this.currentAction = new StretchSectionAction(this, d.id, Side.Top, d.height, d.height);
5179
5595
  }
@@ -5183,14 +5599,17 @@ class DiagramCanvas {
5183
5599
  })
5184
5600
  .on(DragEvents.Drag, (event, d) => {
5185
5601
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5186
- d.node?.type?.resizableY) {
5602
+ d.node?.type?.resizableY &&
5603
+ !d.removed) {
5187
5604
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5188
5605
  d.node.stretchSections(Side.Top, d.coords[1] - pointerCoords[1], d.indexXInNode, d.indexYInNode);
5189
5606
  }
5190
5607
  })
5191
5608
  .on(DragEvents.End, (event, d) => {
5192
5609
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5193
- d.node?.type?.resizableY) {
5610
+ d.node?.type?.resizableY &&
5611
+ !d.removed &&
5612
+ this.currentAction instanceof StretchSectionAction) {
5194
5613
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5195
5614
  if (this.snapToGrid) {
5196
5615
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -5210,13 +5629,15 @@ class DiagramCanvas {
5210
5629
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
5211
5630
  .on(Events.MouseOver, (event, d) => {
5212
5631
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5213
- d.node?.type?.resizableX) {
5632
+ d.node?.type?.resizableX &&
5633
+ !d.removed) {
5214
5634
  cursorStyle(CursorStyle.EWResize);
5215
5635
  }
5216
5636
  })
5217
5637
  .on(Events.MouseOut, (event, d) => {
5218
5638
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5219
- d.node?.type?.resizableX) {
5639
+ d.node?.type?.resizableX &&
5640
+ !d.removed) {
5220
5641
  cursorStyle();
5221
5642
  }
5222
5643
  })
@@ -5224,7 +5645,8 @@ class DiagramCanvas {
5224
5645
  .drag()
5225
5646
  .on(DragEvents.Start, (event, d) => {
5226
5647
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5227
- d.node?.type?.resizableX) {
5648
+ d.node?.type?.resizableX &&
5649
+ !d.removed) {
5228
5650
  cursorStyle(CursorStyle.EWResize);
5229
5651
  this.currentAction = new StretchSectionAction(this, d.id, Side.Right, d.width, d.width);
5230
5652
  }
@@ -5234,14 +5656,17 @@ class DiagramCanvas {
5234
5656
  })
5235
5657
  .on(DragEvents.Drag, (event, d) => {
5236
5658
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5237
- d.node?.type?.resizableX) {
5659
+ d.node?.type?.resizableX &&
5660
+ !d.removed) {
5238
5661
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5239
5662
  d.node.stretchSections(Side.Right, pointerCoords[0] - (d.coords[0] + d.width), d.indexXInNode, d.indexYInNode);
5240
5663
  }
5241
5664
  })
5242
5665
  .on(DragEvents.End, (event, d) => {
5243
5666
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5244
- d.node?.type?.resizableX) {
5667
+ d.node?.type?.resizableX &&
5668
+ !d.removed &&
5669
+ this.currentAction instanceof StretchSectionAction) {
5245
5670
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5246
5671
  if (this.snapToGrid) {
5247
5672
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -5261,13 +5686,15 @@ class DiagramCanvas {
5261
5686
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
5262
5687
  .on(Events.MouseOver, (event, d) => {
5263
5688
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5264
- d.node?.type?.resizableY) {
5689
+ d.node?.type?.resizableY &&
5690
+ !d.removed) {
5265
5691
  cursorStyle(CursorStyle.NSResize);
5266
5692
  }
5267
5693
  })
5268
5694
  .on(Events.MouseOut, (event, d) => {
5269
5695
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5270
- d.node?.type?.resizableY) {
5696
+ d.node?.type?.resizableY &&
5697
+ !d.removed) {
5271
5698
  cursorStyle();
5272
5699
  }
5273
5700
  })
@@ -5275,7 +5702,8 @@ class DiagramCanvas {
5275
5702
  .drag()
5276
5703
  .on(DragEvents.Start, (event, d) => {
5277
5704
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5278
- d.node?.type?.resizableY) {
5705
+ d.node?.type?.resizableY &&
5706
+ !d.removed) {
5279
5707
  cursorStyle(CursorStyle.NSResize);
5280
5708
  this.currentAction = new StretchSectionAction(this, d.id, Side.Bottom, d.height, d.height);
5281
5709
  }
@@ -5285,14 +5713,17 @@ class DiagramCanvas {
5285
5713
  })
5286
5714
  .on(DragEvents.Drag, (event, d) => {
5287
5715
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5288
- d.node?.type?.resizableY) {
5716
+ d.node?.type?.resizableY &&
5717
+ !d.removed) {
5289
5718
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5290
5719
  d.node.stretchSections(Side.Bottom, pointerCoords[1] - (d.coords[1] + d.height), d.indexXInNode, d.indexYInNode);
5291
5720
  }
5292
5721
  })
5293
5722
  .on(DragEvents.End, (event, d) => {
5294
5723
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5295
- d.node?.type?.resizableY) {
5724
+ d.node?.type?.resizableY &&
5725
+ !d.removed &&
5726
+ this.currentAction instanceof StretchSectionAction) {
5296
5727
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5297
5728
  if (this.snapToGrid) {
5298
5729
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -5304,7 +5735,9 @@ class DiagramCanvas {
5304
5735
  }
5305
5736
  cursorStyle();
5306
5737
  }));
5307
- mergeSelection.attr('transform', (d) => `translate(${d.coords[0]},${d.coords[1]})`);
5738
+ mergeSelection
5739
+ .attr('transform', (d) => `translate(${d.coords[0]},${d.coords[1]})`)
5740
+ .attr('opacity', (d) => (d.removed ? 0.5 : 1));
5308
5741
  mergeSelection
5309
5742
  .filter('.shaped-look')
5310
5743
  .select('path')
@@ -5483,7 +5916,10 @@ class DiagramCanvas {
5483
5916
  updatePortsInView(...ids) {
5484
5917
  let updateSelection = this.selectCanvasPorts()
5485
5918
  .selectAll('g.diagram-port')
5486
- .data(this.model.ports.filter(this.priorityThreshold), (d) => d.id);
5919
+ .data(this.model.ports.filter((e) => !e.removed &&
5920
+ (this.priorityThreshold !== undefined
5921
+ ? e.getPriority() >= this.priorityThreshold
5922
+ : true)), (d) => d.id);
5487
5923
  const exitSelection = updateSelection.exit();
5488
5924
  const enterSelection = updateSelection
5489
5925
  .enter()
@@ -5522,7 +5958,8 @@ class DiagramCanvas {
5522
5958
  .call(d3
5523
5959
  .drag()
5524
5960
  .on(DragEvents.Start, (event, d) => {
5525
- if (this.canUserPerformAction(DiagramActions.AddConnection)) {
5961
+ if (this.canUserPerformAction(DiagramActions.AddConnection) &&
5962
+ !d.removed) {
5526
5963
  cursorStyle(CursorStyle.Grabbing);
5527
5964
  this.startConnection(d);
5528
5965
  // should be true after having called this.startConnection()
@@ -5533,9 +5970,13 @@ class DiagramCanvas {
5533
5970
  .attr('fill', 'none');
5534
5971
  }
5535
5972
  }
5973
+ else {
5974
+ cursorStyle(CursorStyle.NotAllowed);
5975
+ }
5536
5976
  })
5537
- .on(DragEvents.Drag, (event) => {
5538
- if (this.canUserPerformAction(DiagramActions.AddConnection)) {
5977
+ .on(DragEvents.Drag, (event, d) => {
5978
+ if (this.canUserPerformAction(DiagramActions.AddConnection) &&
5979
+ !d.removed) {
5539
5980
  if (this.unfinishedConnection !== undefined) {
5540
5981
  const endCoords = [event.x, event.y];
5541
5982
  this.unfinishedConnectionTracer?.attr('d', getConnectionPath(this.unfinishedConnection.type.shape, this.unfinishedConnection.startCoords, endCoords, this.unfinishedConnection.startDirection, this.unfinishedConnection.endDirection, this.unfinishedConnection.type.width, this.unfinishedConnection.startMarkerLook?.markerWidth, this.unfinishedConnection.endMarkerLook?.markerWidth));
@@ -5560,9 +6001,9 @@ class DiagramCanvas {
5560
6001
  }
5561
6002
  }
5562
6003
  })
5563
- .on(DragEvents.End, (event) => {
5564
- if (this.canUserPerformAction(DiagramActions.AddConnection)) {
5565
- cursorStyle();
6004
+ .on(DragEvents.End, (event, d) => {
6005
+ if (this.canUserPerformAction(DiagramActions.AddConnection) &&
6006
+ !d.removed) {
5566
6007
  this.unfinishedConnectionTracer?.remove();
5567
6008
  if (this.mainUserHighlight instanceof DiagramPort) {
5568
6009
  this.finishConnection(this.mainUserHighlight);
@@ -5598,10 +6039,12 @@ class DiagramCanvas {
5598
6039
  this.dropConnection();
5599
6040
  }
5600
6041
  }
6042
+ cursorStyle();
5601
6043
  }));
5602
6044
  enterSelection.append('circle');
5603
6045
  mergeSelection
5604
6046
  .attr('transform', (d) => `translate(${d.coords[0]},${d.coords[1]})`)
6047
+ .attr('opacity', (d) => (d.removed ? 0.5 : 1))
5605
6048
  .select('circle')
5606
6049
  .attr('cx', 0)
5607
6050
  .attr('cy', 0)
@@ -5612,7 +6055,10 @@ class DiagramCanvas {
5612
6055
  .attr('opacity', (d) => (d.highlighted || d.selected ? 0.5 : 0));
5613
6056
  }
5614
6057
  updateConnectionsInView(...ids) {
5615
- const connectionList = this.model.connections.filter(undefined, this.priorityThreshold);
6058
+ const connectionList = this.model.connections.filter((e) => !e.removed &&
6059
+ (this.priorityThreshold !== undefined
6060
+ ? e.getPriority() >= this.priorityThreshold
6061
+ : true));
5616
6062
  if (this.unfinishedConnection) {
5617
6063
  connectionList.push(this.unfinishedConnection);
5618
6064
  }
@@ -5675,6 +6121,7 @@ class DiagramCanvas {
5675
6121
  .append('text')
5676
6122
  .style('user-select', 'none');
5677
6123
  mergeSelection
6124
+ .attr('opacity', (d) => (d.removed ? 0.5 : 1))
5678
6125
  .select('path.diagram-connection-path')
5679
6126
  .attr('d', (d) => getConnectionPath(d.type.shape, d.startCoords, d.endCoords, d.startDirection, d.endDirection, d.type.width, d.startMarkerLook?.markerWidth, d.endMarkerLook?.markerWidth))
5680
6127
  .attr('marker-start', (d) => `url(#${d.id}-start-marker)`)
@@ -5700,7 +6147,10 @@ class DiagramCanvas {
5700
6147
  updateFieldsInView(...ids) {
5701
6148
  let updateSelection = this.selectCanvasFields()
5702
6149
  .selectAll('foreignObject.diagram-field')
5703
- .data(this.model.fields.filter(this.priorityThreshold), (d) => d.id);
6150
+ .data(this.model.fields.filter((e) => !e.removed &&
6151
+ (this.priorityThreshold !== undefined
6152
+ ? e.getPriority() >= this.priorityThreshold
6153
+ : true)), (d) => d.id);
5704
6154
  const exitSelection = updateSelection.exit();
5705
6155
  const enterSelection = updateSelection
5706
6156
  .enter()
@@ -5726,7 +6176,9 @@ class DiagramCanvas {
5726
6176
  }
5727
6177
  })
5728
6178
  .on(Events.DoubleClick, (event, d) => {
5729
- if (this.canUserPerformAction(DiagramActions.EditField) && d.editable) {
6179
+ if (this.canUserPerformAction(DiagramActions.EditField) &&
6180
+ d.editable &&
6181
+ !d.removed) {
5730
6182
  this.currentAction = new EditFieldAction(this, d.id, d.text, '');
5731
6183
  this.createInputField(d.text, d.coords, d.width, d.height, d.fontSize, d.fontFamily || DIAGRAM_FIELD_DEFAULTS.fontFamily, (text) => {
5732
6184
  d.text = text;
@@ -5743,7 +6195,8 @@ class DiagramCanvas {
5743
6195
  .call(d3
5744
6196
  .drag()
5745
6197
  .on(DragEvents.Start, (event, d) => {
5746
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
6198
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
6199
+ !d.removed) {
5747
6200
  if (d.rootElement instanceof DiagramNode ||
5748
6201
  d.rootElement instanceof DiagramSection) {
5749
6202
  const node = d.rootElement instanceof DiagramNode
@@ -5759,7 +6212,8 @@ class DiagramCanvas {
5759
6212
  }
5760
6213
  })
5761
6214
  .on(DragEvents.Drag, (event, d) => {
5762
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
6215
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
6216
+ !d.removed) {
5763
6217
  if (d.rootElement instanceof DiagramNode ||
5764
6218
  d.rootElement instanceof DiagramSection) {
5765
6219
  const node = d.rootElement instanceof DiagramNode
@@ -5774,7 +6228,8 @@ class DiagramCanvas {
5774
6228
  }
5775
6229
  })
5776
6230
  .on(DragEvents.End, (event, d) => {
5777
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
6231
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
6232
+ !d.removed) {
5778
6233
  if (d.rootElement instanceof DiagramNode ||
5779
6234
  d.rootElement instanceof DiagramSection) {
5780
6235
  const node = d.rootElement instanceof DiagramNode
@@ -5792,10 +6247,8 @@ class DiagramCanvas {
5792
6247
  newNodeCoords = this.getClosestGridPoint(newNodeCoords);
5793
6248
  }
5794
6249
  this.currentAction.to = newNodeCoords;
5795
- this.currentAction.redo();
6250
+ this.currentAction.do();
5796
6251
  this.actionQueue.add(this.currentAction);
5797
- }
5798
- else {
5799
6252
  this.currentAction = undefined;
5800
6253
  }
5801
6254
  }
@@ -5821,6 +6274,7 @@ class DiagramCanvas {
5821
6274
  .attr('width', (d) => `${d.width}px`)
5822
6275
  .attr('height', (d) => `${d.height}px`)
5823
6276
  .attr('transform', (d) => `translate(${d.coords[0]},${d.coords[1]})`)
6277
+ .attr('opacity', (d) => (d.removed ? 0.5 : 1))
5824
6278
  .select('div')
5825
6279
  .style('justify-content', (d) => d.horizontalAlign === HorizontalAlign.Center
5826
6280
  ? 'center'
@@ -6189,7 +6643,7 @@ class DiagramCanvas {
6189
6643
  const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, this.unfinishedConnection.start?.id, port.id);
6190
6644
  // clean up the previous unfinished connection
6191
6645
  this.dropConnection();
6192
- addConnectionAction.redo();
6646
+ addConnectionAction.do();
6193
6647
  this.actionQueue.add(addConnectionAction);
6194
6648
  }
6195
6649
  else if (this.unfinishedConnection.type.canFinishOnType(this.unfinishedConnection?.start?.getNode()?.type?.id || '') &&
@@ -6197,7 +6651,7 @@ class DiagramCanvas {
6197
6651
  const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, port.id, this.unfinishedConnection.start?.id);
6198
6652
  // clean up the previous unfinished connection
6199
6653
  this.dropConnection();
6200
- addConnectionAction.redo();
6654
+ addConnectionAction.do();
6201
6655
  this.actionQueue.add(addConnectionAction);
6202
6656
  }
6203
6657
  else {
@@ -6221,7 +6675,7 @@ class DiagramCanvas {
6221
6675
  : port.id);
6222
6676
  // clean up the previous unfinished connection
6223
6677
  this.dropConnection();
6224
- addConnectionAction.redo();
6678
+ addConnectionAction.do();
6225
6679
  this.actionQueue.add(addConnectionAction);
6226
6680
  }
6227
6681
  else {
@@ -6310,7 +6764,10 @@ class DiagramCanvas {
6310
6764
  // check if there have been changes in the previously selected ValueSet,
6311
6765
  // and create an UpdateValuesAction if there have
6312
6766
  if (!equals(this.propertyEditorValues, this.propertyEditorSelection?.valueSet.getValues())) {
6313
- const currentAction = new UpdateValuesAction(this.model, previousSelectionId, this.propertyEditorValues, structuredClone(this.propertyEditorSelection?.valueSet.getValues()));
6767
+ const from = this.propertyEditorValues;
6768
+ const to = structuredClone(this.propertyEditorSelection?.valueSet.getValues());
6769
+ const [fromDiff, toDiff] = diff(from, to);
6770
+ const currentAction = new UpdateValuesAction(this.model, previousSelectionId, fromDiff, toDiff);
6314
6771
  this.actionQueue.add(currentAction);
6315
6772
  this.propertyEditorValues = undefined;
6316
6773
  }
@@ -6674,7 +7131,7 @@ class DiagramButtonsComponent {
6674
7131
  this.canvas.actionQueue.redo();
6675
7132
  }
6676
7133
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DiagramButtonsComponent, deps: [{ token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
6677
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: DiagramButtonsComponent, isStandalone: true, selector: "daga-diagram-buttons", inputs: { location: "location", direction: "direction", enableFilter: "enableFilter", enableLayout: "enableLayout", enableUndoRedo: "enableUndoRedo", enableZoom: "enableZoom", zoomRate: "zoomRate" }, viewQueries: [{ propertyName: "collapsableButtons", first: true, predicate: ["collapsableButtons"], descendants: true }], ngImport: i0, template: "<div class=\"diagram-buttons {{ location }} {{ direction }}\">\r\n <button\r\n *ngIf=\"enableZoom && canvas.canUserPerformAction(DiagramActions.Zoom)\"\r\n class=\"zoom-in\"\r\n (click)=\"zoomIn()\"\r\n >\r\n <span class=\"tooltip\">Zoom in</span>\r\n </button>\r\n <button\r\n *ngIf=\"enableZoom && canvas.canUserPerformAction(DiagramActions.Zoom)\"\r\n class=\"zoom-out\"\r\n (click)=\"zoomOut()\"\r\n >\r\n <span class=\"tooltip\">Zoom out</span>\r\n </button>\r\n <div #collapsableButtons class=\"collapsable-buttons collapsed\">\r\n <button\r\n *ngIf=\"enableZoom && canvas.canUserPerformAction(DiagramActions.Zoom)\"\r\n class=\"center\"\r\n (click)=\"center()\"\r\n >\r\n <span class=\"tooltip\">Fit diagram to screen</span>\r\n </button>\r\n <button *ngIf=\"enableUndoRedo\" class=\"undo\" (click)=\"undo()\">\r\n <span class=\"tooltip\">Undo</span>\r\n </button>\r\n <button *ngIf=\"enableUndoRedo\" class=\"redo\" (click)=\"redo()\">\r\n <span class=\"tooltip\">Redo</span>\r\n </button>\r\n <button\r\n *ngIf=\"enableLayout && canvas.layoutFormat\"\r\n class=\"layout\"\r\n (click)=\"layout()\"\r\n >\r\n <span class=\"tooltip\">Apply layout</span>\r\n </button>\r\n <button\r\n *ngIf=\"enableFilter\"\r\n class=\"filter\"\r\n [class]=\"filterOn ? 'on' : 'off'\"\r\n (click)=\"filter()\"\r\n >\r\n <span class=\"tooltip\">Apply filter</span>\r\n </button>\r\n </div>\r\n <button class=\"more-options\" (click)=\"toggleCollapse()\">\r\n <span *ngIf=\"!collapsed\" class=\"tooltip\">Less options</span>\r\n <span *ngIf=\"collapsed\" class=\"tooltip\">More options</span>\r\n </button>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
7134
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: DiagramButtonsComponent, isStandalone: true, selector: "daga-diagram-buttons", inputs: { location: "location", direction: "direction", enableFilter: "enableFilter", enableLayout: "enableLayout", enableUndoRedo: "enableUndoRedo", enableZoom: "enableZoom", zoomRate: "zoomRate" }, viewQueries: [{ propertyName: "collapsableButtons", first: true, predicate: ["collapsableButtons"], descendants: true }], ngImport: i0, template: "<div class=\"diagram-buttons {{ location }} {{ direction }}\">\r\n <button\r\n *ngIf=\"enableZoom && canvas.canUserPerformAction(DiagramActions.Zoom)\"\r\n class=\"zoom-in\"\r\n (click)=\"zoomIn()\"\r\n >\r\n <span class=\"tooltip\">Zoom in</span>\r\n </button>\r\n <button\r\n *ngIf=\"enableZoom && canvas.canUserPerformAction(DiagramActions.Zoom)\"\r\n class=\"zoom-out\"\r\n (click)=\"zoomOut()\"\r\n >\r\n <span class=\"tooltip\">Zoom out</span>\r\n </button>\r\n <div #collapsableButtons class=\"collapsable-buttons collapsed\">\r\n <button\r\n *ngIf=\"enableZoom && canvas.canUserPerformAction(DiagramActions.Zoom)\"\r\n class=\"center\"\r\n (click)=\"center()\"\r\n >\r\n <span class=\"tooltip\">Fit diagram to screen</span>\r\n </button>\r\n <button *ngIf=\"enableUndoRedo\" class=\"undo\" (click)=\"undo()\">\r\n <span class=\"tooltip\">Undo</span>\r\n </button>\r\n <button *ngIf=\"enableUndoRedo\" class=\"redo\" (click)=\"redo()\">\r\n <span class=\"tooltip\">Redo</span>\r\n </button>\r\n <button\r\n *ngIf=\"enableLayout && canvas.layoutFormat\"\r\n class=\"layout\"\r\n (click)=\"layout()\"\r\n >\r\n <span class=\"tooltip\">Apply layout</span>\r\n </button>\r\n <button\r\n *ngIf=\"enableFilter\"\r\n class=\"filter\"\r\n [class]=\"filterOn ? 'on' : 'off'\"\r\n (click)=\"filter()\"\r\n >\r\n <span class=\"tooltip\">Apply filter</span>\r\n </button>\r\n </div>\r\n <button class=\"more-options\" (click)=\"toggleCollapse()\">\r\n <span *ngIf=\"!collapsed\" class=\"tooltip\">Less options</span>\r\n <span *ngIf=\"collapsed\" class=\"tooltip\">More options</span>\r\n </button>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
6678
7135
  }
6679
7136
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DiagramButtonsComponent, decorators: [{
6680
7137
  type: Component,
@@ -6713,7 +7170,7 @@ class ErrorsComponent {
6713
7170
  this.Side = Side;
6714
7171
  }
6715
7172
  ngAfterViewInit() {
6716
- merge([this.canvas.validatorChanges$, this.canvas.diagramChanges$])
7173
+ merge(this.canvas.validatorChanges$, this.canvas.diagramChanges$)
6717
7174
  .pipe(
6718
7175
  // delay to avoid errors of variables changing right after checking
6719
7176
  delay(1), map(() => this.validate()))
@@ -6742,7 +7199,7 @@ class ErrorsComponent {
6742
7199
  // TODO: IF ERROR IS IN AN ELEMENT BUT NOT IN A SPECIFIC PROPERTY, WE COULD HIGHLIGHT THE ELEMENT
6743
7200
  }
6744
7201
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: ErrorsComponent, deps: [{ token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
6745
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: ErrorsComponent, isStandalone: true, selector: "daga-errors", viewQueries: [{ propertyName: "errorsContainer", first: true, predicate: ["errors"], descendants: true }], ngImport: i0, template: "<div #errorsContainer class=\"errors\">\n <div\n *ngIf=\"errors.length === 0\"\n class=\"errors-summary no-errors prevent-user-select\"\n >\n <span>No errors found</span>\n </div>\n <div\n *ngIf=\"errors.length > 0\"\n class=\"errors-summary with-errors prevent-user-select\"\n >\n <span>{{ errors.length }} errors found</span>\n <div class=\"collapse-button-container\">\n <daga-collapse-button\n [collapsableSelector]=\"errorsContainer\"\n [collapsableAdditionalSelector]=\"'.error-panel'\"\n [direction]=\"Side.Top\"\n [rule]=\"'display'\"\n [collapsedValue]=\"'none'\"\n [visibleValue]=\"'block'\"\n />\n </div>\n </div>\n <div *ngIf=\"errors.length > 0\" class=\"error-panel\">\n <ol>\n <li\n *ngFor=\"let error of errors; index as i\"\n (click)=\"showError(error)\"\n [innerHTML]=\"error.message\"\n ></li>\n </ol>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CollapseButtonComponent, selector: "daga-collapse-button", inputs: ["collapsableSelector", "collapsableAdditionalSelector", "collapsed", "disabled", "direction", "rule", "collapsedValue", "visibleValue"] }] }); }
7202
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: ErrorsComponent, isStandalone: true, selector: "daga-errors", viewQueries: [{ propertyName: "errorsContainer", first: true, predicate: ["errors"], descendants: true }], ngImport: i0, template: "<div #errorsContainer class=\"errors\">\n <div\n *ngIf=\"errors.length === 0\"\n class=\"errors-summary no-errors prevent-user-select\"\n >\n <span>No errors found</span>\n </div>\n <div\n *ngIf=\"errors.length > 0\"\n class=\"errors-summary with-errors prevent-user-select\"\n >\n <span>{{ errors.length }} errors found</span>\n <div class=\"collapse-button-container\">\n <daga-collapse-button\n [collapsableSelector]=\"errorsContainer\"\n [collapsableAdditionalSelector]=\"'.error-panel'\"\n [direction]=\"Side.Top\"\n [rule]=\"'display'\"\n [collapsedValue]=\"'none'\"\n [visibleValue]=\"'block'\"\n />\n </div>\n </div>\n <div *ngIf=\"errors.length > 0\" class=\"error-panel\">\n <ol>\n <li\n *ngFor=\"let error of errors; index as i\"\n (click)=\"showError(error)\"\n [innerHTML]=\"error.message\"\n ></li>\n </ol>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CollapseButtonComponent, selector: "daga-collapse-button", inputs: ["collapsableSelector", "collapsableAdditionalSelector", "collapsed", "disabled", "direction", "rule", "collapsedValue", "visibleValue"] }] }); }
6746
7203
  }
6747
7204
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: ErrorsComponent, decorators: [{
6748
7205
  type: Component,
@@ -6877,7 +7334,7 @@ class PaletteComponent {
6877
7334
  .drag()
6878
7335
  .on(DragEvents.Drag, (event) => {
6879
7336
  if (this.canvas.canUserPerformAction(DiagramActions.AddNode)) {
6880
- const pointerCoords = this.canvas.getPointerLocationRelativeToBody(event);
7337
+ const pointerCoords = this.canvas.getPointerLocationRelativeToScreen(event);
6881
7338
  if (pointerCoords.length < 2 ||
6882
7339
  isNaN(pointerCoords[0]) ||
6883
7340
  isNaN(pointerCoords[1])) {
@@ -6893,7 +7350,7 @@ class PaletteComponent {
6893
7350
  .on(DragEvents.Start, (event) => {
6894
7351
  if (this.canvas.canUserPerformAction(DiagramActions.AddNode)) {
6895
7352
  d3.select('body').style('cursor', 'grabbing');
6896
- const pointerCoords = this.canvas.getPointerLocationRelativeToBody(event);
7353
+ const pointerCoords = this.canvas.getPointerLocationRelativeToScreen(event);
6897
7354
  if (pointerCoords.length < 2 ||
6898
7355
  isNaN(pointerCoords[0]) ||
6899
7356
  isNaN(pointerCoords[1])) {
@@ -6906,7 +7363,7 @@ class PaletteComponent {
6906
7363
  .style('z-index', 1);
6907
7364
  // when trying to place a unique node in a diagram that already has a node of that type, set cursor style to not allowed
6908
7365
  if (type.isUnique &&
6909
- this.canvas.model.nodes.find(type.id) !== undefined) {
7366
+ this.canvas.model.nodes.find((n) => !n.removed && n.type.id === type.id) !== undefined) {
6910
7367
  cursorStyle(CursorStyle.NotAllowed);
6911
7368
  }
6912
7369
  }
@@ -6922,18 +7379,18 @@ class PaletteComponent {
6922
7379
  .style('z-index', 'auto');
6923
7380
  // try to place node
6924
7381
  if (type.isUnique &&
6925
- this.canvas.model.nodes.find(type.id) !== undefined) {
7382
+ this.canvas.model.nodes.find((n) => !n.removed && n.type.id === type.id) !== undefined) {
6926
7383
  // can't place, it's unique and that node is already in the model
6927
7384
  return;
6928
7385
  }
6929
- const pointerCoordsRelativeToBody = this.canvas.getPointerLocationRelativeToBody(event);
6930
- if (pointerCoordsRelativeToBody.length < 2 ||
6931
- isNaN(pointerCoordsRelativeToBody[0]) ||
6932
- isNaN(pointerCoordsRelativeToBody[1])) {
7386
+ const pointerCoordsRelativeToScreen = this.canvas.getPointerLocationRelativeToScreen(event);
7387
+ if (pointerCoordsRelativeToScreen.length < 2 ||
7388
+ isNaN(pointerCoordsRelativeToScreen[0]) ||
7389
+ isNaN(pointerCoordsRelativeToScreen[1])) {
6933
7390
  // can't place, position is incorrect
6934
7391
  return;
6935
7392
  }
6936
- const element = document.elementFromPoint(pointerCoordsRelativeToBody[0], pointerCoordsRelativeToBody[1]);
7393
+ const element = document.elementFromPoint(pointerCoordsRelativeToScreen[0], pointerCoordsRelativeToScreen[1]);
6937
7394
  if (element &&
6938
7395
  !this.canvas.selectCanvasView().node()?.contains(element)) {
6939
7396
  // can't place, node hasn't been dropped on the canvas
@@ -6954,7 +7411,7 @@ class PaletteComponent {
6954
7411
  newNodeCoords = this.canvas.getClosestGridPoint(newNodeCoords);
6955
7412
  }
6956
7413
  const addNodeAction = new AddNodeAction(this.canvas, type, newNodeCoords, templateConfig.label, templateConfig.values);
6957
- addNodeAction.redo();
7414
+ addNodeAction.do();
6958
7415
  this.canvas.actionQueue.add(addNodeAction);
6959
7416
  // reset cursor
6960
7417
  cursorStyle();
@@ -7131,7 +7588,7 @@ class PaletteComponent {
7131
7588
  }
7132
7589
  }
7133
7590
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: PaletteComponent, deps: [{ token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
7134
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: PaletteComponent, isStandalone: true, selector: "daga-palette", inputs: { palettes: "palettes", currentPalette: "currentPalette", currentCategory: "currentCategory", location: "location", direction: "direction", width: "width" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true }], ngImport: i0, template: "<div #panel class=\"panel {{ location }} {{ direction }}\">\n <daga-collapse-button\n #collapseButton\n [direction]=\"direction\"\n [collapsableSelector]=\"panel\"\n collapsableAdditionalSelector=\".panel-content\"\n rule=\"display\"\n collapsedValue=\"none\"\n visibleValue=\"block\"\n />\n <div class=\"panel-content\">\n <div *ngIf=\"palettes.length > 1\" class=\"panel-tabs\">\n <div\n *ngFor=\"let palette of palettes\"\n class=\"panel-tab\"\n [class]=\"palette === currentPalette ? 'current-tab' : ''\"\n (click)=\"switchPalette(palette)\"\n >\n {{ palette.name }}\n </div>\n </div>\n <div class=\"palette-view\"></div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CollapseButtonComponent, selector: "daga-collapse-button", inputs: ["collapsableSelector", "collapsableAdditionalSelector", "collapsed", "disabled", "direction", "rule", "collapsedValue", "visibleValue"] }] }); }
7591
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: PaletteComponent, isStandalone: true, selector: "daga-palette", inputs: { palettes: "palettes", currentPalette: "currentPalette", currentCategory: "currentCategory", location: "location", direction: "direction", width: "width" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true }], ngImport: i0, template: "<div #panel class=\"panel {{ location }} {{ direction }}\">\n <daga-collapse-button\n #collapseButton\n [direction]=\"direction\"\n [collapsableSelector]=\"panel\"\n collapsableAdditionalSelector=\".panel-content\"\n rule=\"display\"\n collapsedValue=\"none\"\n visibleValue=\"block\"\n />\n <div class=\"panel-content\">\n <div *ngIf=\"palettes.length > 1\" class=\"panel-tabs\">\n <div\n *ngFor=\"let palette of palettes\"\n class=\"panel-tab\"\n [class]=\"palette === currentPalette ? 'current-tab' : ''\"\n (click)=\"switchPalette(palette)\"\n >\n {{ palette.name }}\n </div>\n </div>\n <div class=\"palette-view\"></div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CollapseButtonComponent, selector: "daga-collapse-button", inputs: ["collapsableSelector", "collapsableAdditionalSelector", "collapsed", "disabled", "direction", "rule", "collapsedValue", "visibleValue"] }] }); }
7135
7592
  }
7136
7593
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: PaletteComponent, decorators: [{
7137
7594
  type: Component,
@@ -7153,20 +7610,291 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
7153
7610
  type: Input
7154
7611
  }] } });
7155
7612
 
7613
+ const LOST_FOCUS_TIME_WINDOW_MS = 200;
7614
+ /**
7615
+ * Combobox with autocomplete.
7616
+ * @see PropertyEditor
7617
+ * @see ValueSet
7618
+ * @private
7619
+ */
7620
+ class AutocompleteComponent {
7621
+ set value(value) {
7622
+ if (value === this._value) {
7623
+ return;
7624
+ }
7625
+ this._value = value;
7626
+ this.valueInput = this.getLabelOfValue(value);
7627
+ }
7628
+ get value() {
7629
+ return this._value;
7630
+ }
7631
+ constructor(cdr) {
7632
+ this.cdr = cdr;
7633
+ this.valueInput = '';
7634
+ this.disabled = false;
7635
+ this.showOptions = false;
7636
+ this.valueChange = new EventEmitter();
7637
+ }
7638
+ getLabelOfValue(value) {
7639
+ for (const option of this.options) {
7640
+ if (option.key === value) {
7641
+ return option.label;
7642
+ }
7643
+ }
7644
+ return `${value}`;
7645
+ }
7646
+ onKeyup(event) {
7647
+ if (!this.disabled) {
7648
+ switch (event.key) {
7649
+ case Keys.PageDown:
7650
+ if (this.focusIndex === undefined) {
7651
+ this.focusOnOption(0);
7652
+ this.scrollToOption(0);
7653
+ }
7654
+ else {
7655
+ this.focusOnOption(this.focusIndex + 5);
7656
+ this.scrollToOption(this.focusIndex);
7657
+ }
7658
+ event.preventDefault();
7659
+ break;
7660
+ case Keys.ArrowDown:
7661
+ if (this.focusIndex === undefined) {
7662
+ this.focusOnOption(0);
7663
+ this.scrollToOption(0);
7664
+ }
7665
+ else {
7666
+ this.focusOnOption(this.focusIndex + 1);
7667
+ this.scrollToOption(this.focusIndex);
7668
+ }
7669
+ event.preventDefault();
7670
+ break;
7671
+ case Keys.PageUp:
7672
+ if (this.focusIndex === undefined) {
7673
+ this.focusOnOption(this.options.length - 1);
7674
+ this.scrollToOption(this.options.length - 1);
7675
+ }
7676
+ else {
7677
+ this.focusOnOption(this.focusIndex - 5);
7678
+ this.scrollToOption(this.focusIndex);
7679
+ }
7680
+ event.preventDefault();
7681
+ break;
7682
+ case Keys.ArrowUp:
7683
+ if (this.focusIndex === undefined) {
7684
+ this.focusOnOption(this.options.length - 1);
7685
+ this.scrollToOption(this.options.length - 1);
7686
+ }
7687
+ else {
7688
+ this.focusOnOption(this.focusIndex - 1);
7689
+ this.scrollToOption(this.focusIndex);
7690
+ }
7691
+ event.preventDefault();
7692
+ break;
7693
+ case Keys.Escape:
7694
+ this.closeOptions();
7695
+ event.preventDefault();
7696
+ break;
7697
+ case Keys.Enter:
7698
+ this.complete(this.options[this.focusIndex || 0]);
7699
+ event.preventDefault();
7700
+ break;
7701
+ default:
7702
+ this.openOptions();
7703
+ }
7704
+ }
7705
+ }
7706
+ openOptions() {
7707
+ if (!this.disabled) {
7708
+ const mustContain = normalizeString(this.valueInput.trim());
7709
+ this.currentOptions = [];
7710
+ this.currentLabelStarts = [];
7711
+ this.currentLabelMatches = [];
7712
+ this.currentLabelEnds = [];
7713
+ for (const option of this.options) {
7714
+ const normalizedLabel = normalizeString(option.label);
7715
+ const position = normalizedLabel.indexOf(mustContain);
7716
+ if (position >= 0) {
7717
+ const labelStart = option.label.substring(0, position);
7718
+ const labelMatch = option.label.substring(position, position + mustContain.length);
7719
+ const labelEnd = option.label.substring(position + mustContain.length);
7720
+ this.currentOptions.push(option);
7721
+ this.currentLabelStarts.push(labelStart);
7722
+ this.currentLabelMatches.push(labelMatch);
7723
+ this.currentLabelEnds.push(labelEnd);
7724
+ }
7725
+ }
7726
+ this.showOptions = true;
7727
+ }
7728
+ }
7729
+ closeOptions() {
7730
+ this.showOptions = false;
7731
+ }
7732
+ focusOnOption(index) {
7733
+ if (!this.disabled) {
7734
+ this.focusIndex = index;
7735
+ if (index === undefined) {
7736
+ return;
7737
+ }
7738
+ if (index < 0) {
7739
+ this.focusIndex = 0;
7740
+ }
7741
+ if (index >= this.options.length) {
7742
+ this.focusIndex = this.options.length - 1;
7743
+ }
7744
+ }
7745
+ }
7746
+ onLostFocus() {
7747
+ setTimeout(() => {
7748
+ this.closeOptions();
7749
+ }, LOST_FOCUS_TIME_WINDOW_MS);
7750
+ }
7751
+ scrollToOption(index) {
7752
+ const target = this.mainElement.nativeElement.querySelectorAll('li')[index];
7753
+ if (target) {
7754
+ target.scrollIntoView({ block: 'center' });
7755
+ }
7756
+ }
7757
+ clearInput() {
7758
+ if (!this.disabled) {
7759
+ this.value = undefined;
7760
+ this.valueInput = '';
7761
+ this.showOptions = false;
7762
+ this.focusIndex = undefined;
7763
+ }
7764
+ }
7765
+ complete(option) {
7766
+ if (!this.disabled) {
7767
+ this.value = option;
7768
+ this.valueInput = option.label;
7769
+ this.showOptions = false;
7770
+ this.focusIndex = undefined;
7771
+ this.valueChange.emit(option.key);
7772
+ }
7773
+ }
7774
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AutocompleteComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
7775
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: AutocompleteComponent, isStandalone: true, selector: "daga-autocomplete", inputs: { value: "value", valueInput: "valueInput", options: "options", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, viewQueries: [{ propertyName: "mainElement", first: true, predicate: ["main"], descendants: true, static: true }], ngImport: i0, template: "<div\r\n #main\r\n class=\"autocomplete\"\r\n [class]=\"showOptions ? 'showing-options' : ''\"\r\n (blur)=\"closeOptions()\"\r\n>\r\n <div class=\"autocomplete-input\">\r\n <input\r\n [(ngModel)]=\"valueInput\"\r\n [disabled]=\"disabled\"\r\n (keyup)=\"onKeyup($event)\"\r\n (focus)=\"openOptions()\"\r\n (blur)=\"onLostFocus()\"\r\n />\r\n <button\r\n *ngIf=\"valueInput !== ''\"\r\n class=\"clear\"\r\n (click)=\"clearInput()\"\r\n ></button>\r\n </div>\r\n <div class=\"autocomplete-options\">\r\n <ul class=\"autocomplete-option-list\">\r\n <li\r\n *ngFor=\"let option of currentOptions; let index = index\"\r\n class=\"autocomplete-option\"\r\n [class]=\"index === focusIndex ? 'focused' : ''\"\r\n (mousemove)=\"focusOnOption(index)\"\r\n (click)=\"complete(option)\"\r\n >\r\n <span>{{ currentLabelStarts[index] }}</span>\r\n <span class=\"match\">{{ currentLabelMatches[index] }}</span>\r\n <span>{{ currentLabelEnds[index] }}</span>\r\n </li>\r\n </ul>\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
7776
+ }
7777
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AutocompleteComponent, decorators: [{
7778
+ type: Component,
7779
+ args: [{ standalone: true, selector: 'daga-autocomplete', imports: [CommonModule, FormsModule], template: "<div\r\n #main\r\n class=\"autocomplete\"\r\n [class]=\"showOptions ? 'showing-options' : ''\"\r\n (blur)=\"closeOptions()\"\r\n>\r\n <div class=\"autocomplete-input\">\r\n <input\r\n [(ngModel)]=\"valueInput\"\r\n [disabled]=\"disabled\"\r\n (keyup)=\"onKeyup($event)\"\r\n (focus)=\"openOptions()\"\r\n (blur)=\"onLostFocus()\"\r\n />\r\n <button\r\n *ngIf=\"valueInput !== ''\"\r\n class=\"clear\"\r\n (click)=\"clearInput()\"\r\n ></button>\r\n </div>\r\n <div class=\"autocomplete-options\">\r\n <ul class=\"autocomplete-option-list\">\r\n <li\r\n *ngFor=\"let option of currentOptions; let index = index\"\r\n class=\"autocomplete-option\"\r\n [class]=\"index === focusIndex ? 'focused' : ''\"\r\n (mousemove)=\"focusOnOption(index)\"\r\n (click)=\"complete(option)\"\r\n >\r\n <span>{{ currentLabelStarts[index] }}</span>\r\n <span class=\"match\">{{ currentLabelMatches[index] }}</span>\r\n <span>{{ currentLabelEnds[index] }}</span>\r\n </li>\r\n </ul>\r\n </div>\r\n</div>\r\n" }]
7780
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { mainElement: [{
7781
+ type: ViewChild,
7782
+ args: ['main', { static: true }]
7783
+ }], value: [{
7784
+ type: Input
7785
+ }], valueInput: [{
7786
+ type: Input
7787
+ }], options: [{
7788
+ type: Input
7789
+ }], disabled: [{
7790
+ type: Input
7791
+ }], valueChange: [{
7792
+ type: Output
7793
+ }] } });
7794
+ const normalizeString = (a) => (a || '')
7795
+ .toLowerCase()
7796
+ .normalize('NFD')
7797
+ .replace(/[\u0300-\u036f]/g, '');
7798
+
7799
+ /**
7800
+ * Editor of a property of text list type within a property editor.
7801
+ * @see Type.TextList
7802
+ * @see PropertyEditor
7803
+ * @see ValueSet
7804
+ * @private
7805
+ */
7806
+ class OptionListEditorComponent {
7807
+ set value(value) {
7808
+ if (value === this._value) {
7809
+ return;
7810
+ }
7811
+ const initialAndEmpty = value.length === 0 && this._value.length === 0;
7812
+ this._value = value;
7813
+ this.labelsOfValue = [];
7814
+ for (const key of this._value) {
7815
+ this.labelsOfValue.push(this.getLabelOfValue(key));
7816
+ }
7817
+ if (initialAndEmpty) {
7818
+ this.valueChange.emit(this._value);
7819
+ }
7820
+ }
7821
+ get value() {
7822
+ return this._value;
7823
+ }
7824
+ constructor(cdr) {
7825
+ this.cdr = cdr;
7826
+ this._value = [];
7827
+ this.labelsOfValue = [];
7828
+ this.options = [];
7829
+ this.valueInput = undefined;
7830
+ this.disabled = false;
7831
+ this.valueChange = new EventEmitter();
7832
+ }
7833
+ getLabelOfValue(value) {
7834
+ for (const option of this.options) {
7835
+ if (option.key === value) {
7836
+ return option.label;
7837
+ }
7838
+ }
7839
+ return `${value}`;
7840
+ }
7841
+ removeFromValue(index) {
7842
+ if (this._value.length > index) {
7843
+ this._value.splice(index, 1);
7844
+ this.labelsOfValue.splice(index, 1);
7845
+ this.valueChange.emit(this._value);
7846
+ }
7847
+ this.cdr.detectChanges();
7848
+ }
7849
+ addToValue() {
7850
+ if (this.valueInput !== undefined) {
7851
+ this._value.push(this.valueInput);
7852
+ this.labelsOfValue.push(this.getLabelOfValue(this.valueInput));
7853
+ this.valueChange.emit(this._value);
7854
+ this.clearInput();
7855
+ this.cdr.detectChanges();
7856
+ }
7857
+ }
7858
+ clearInput() {
7859
+ this.valueInput = '';
7860
+ }
7861
+ onKeyUp(event) {
7862
+ if (event.key === 'Enter') {
7863
+ this.addToValue();
7864
+ }
7865
+ }
7866
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: OptionListEditorComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
7867
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: OptionListEditorComponent, isStandalone: true, selector: "daga-option-list-editor", inputs: { value: "value", options: "options", valueInput: "valueInput", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div *ngFor=\"let item of value; let index = index\" class=\"value-item-element\">\r\n <span class=\"input\">{{ labelsOfValue[index] }}</span>\r\n <button\r\n *ngIf=\"!disabled\"\r\n class=\"property-button\"\r\n (click)=\"removeFromValue(index)\"\r\n >\r\n <div class=\"icon close-icon\"></div>\r\n </button>\r\n</div>\r\n<div *ngIf=\"!disabled\" class=\"value-item-input\">\r\n <div class=\"input relatively-positioned\">\r\n <daga-autocomplete\r\n [disabled]=\"disabled\"\r\n [options]=\"options || []\"\r\n [(value)]=\"valueInput\"\r\n />\r\n </div>\r\n <button class=\"property-button\" (click)=\"addToValue()\">\r\n <div class=\"icon add-icon\"></div>\r\n </button>\r\n</div>\r\n", dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "daga-autocomplete", inputs: ["value", "valueInput", "options", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }] }); }
7868
+ }
7869
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: OptionListEditorComponent, decorators: [{
7870
+ type: Component,
7871
+ args: [{ standalone: true, selector: 'daga-option-list-editor', imports: [AutocompleteComponent, CommonModule, FormsModule], template: "<div *ngFor=\"let item of value; let index = index\" class=\"value-item-element\">\r\n <span class=\"input\">{{ labelsOfValue[index] }}</span>\r\n <button\r\n *ngIf=\"!disabled\"\r\n class=\"property-button\"\r\n (click)=\"removeFromValue(index)\"\r\n >\r\n <div class=\"icon close-icon\"></div>\r\n </button>\r\n</div>\r\n<div *ngIf=\"!disabled\" class=\"value-item-input\">\r\n <div class=\"input relatively-positioned\">\r\n <daga-autocomplete\r\n [disabled]=\"disabled\"\r\n [options]=\"options || []\"\r\n [(value)]=\"valueInput\"\r\n />\r\n </div>\r\n <button class=\"property-button\" (click)=\"addToValue()\">\r\n <div class=\"icon add-icon\"></div>\r\n </button>\r\n</div>\r\n" }]
7872
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { value: [{
7873
+ type: Input
7874
+ }], options: [{
7875
+ type: Input
7876
+ }], valueInput: [{
7877
+ type: Input
7878
+ }], disabled: [{
7879
+ type: Input
7880
+ }], valueChange: [{
7881
+ type: Output
7882
+ }] } });
7883
+
7156
7884
  /**
7157
7885
  * Editor of a property of text list type within a property editor.
7158
- * @see TextList
7886
+ * @see Type.TextList
7159
7887
  * @see PropertyEditor
7160
7888
  * @see ValueSet
7161
7889
  * @private
7162
7890
  */
7163
7891
  class TextListEditorComponent {
7164
- set value(val) {
7165
- if (val === this._value) {
7892
+ set value(value) {
7893
+ if (value === this._value) {
7166
7894
  return;
7167
7895
  }
7168
- const initialAndEmpty = val.length === 0 && this._value.length === 0;
7169
- this._value = val;
7896
+ const initialAndEmpty = value.length === 0 && this._value.length === 0;
7897
+ this._value = value;
7170
7898
  if (initialAndEmpty) {
7171
7899
  this.valueChange.emit(this._value);
7172
7900
  }
@@ -7215,11 +7943,11 @@ class TextListEditorComponent {
7215
7943
  }
7216
7944
  }
7217
7945
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TextListEditorComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
7218
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: TextListEditorComponent, isStandalone: true, selector: "daga-text-list-editor", inputs: { value: "value", valueInput: "valueInput", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div *ngFor=\"let item of value; let index = index\" class=\"value-item-element\">\n <lux-input\n type=\"text\"\n [disabled]=\"disabled\"\n [value]=\"item\"\n (focusout)=\"editFromValue(getValueFromEvent($event), index)\"\n ></lux-input>\n <button\n *ngIf=\"!disabled\"\n class=\"property-button\"\n (click)=\"removeFromValue(index)\"\n >\n <div class=\"icon close-icon\"></div>\n </button>\n</div>\n<div *ngIf=\"!disabled\" class=\"value-item-input\">\n <div class=\"relatively-positioned\">\n <lux-input\n type=\"text\"\n (keyup)=\"onKeyUp($event)\"\n [(ngModel)]=\"valueInput\"\n ></lux-input>\n <button\n *ngIf=\"valueInput !== ''\"\n class=\"clear\"\n (click)=\"clearInput()\"\n ></button>\n </div>\n <button class=\"property-button\" (click)=\"addToValue()\">\n <div class=\"icon add-icon\"></div>\n </button>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: LuxModule }, { kind: "component", type: i3.InputComponent, selector: "lux-input", inputs: ["rows", "cols", "step", "min", "max", "lang", "inlineErrors", "inputId", "aria-label", "readonly", "disabled", "pattern", "currency", "placeholder", "required", "type", "value"], outputs: ["valueChange", "keyPress"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
7946
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: TextListEditorComponent, isStandalone: true, selector: "daga-text-list-editor", inputs: { value: "value", valueInput: "valueInput", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div *ngFor=\"let item of value; let index = index\" class=\"value-item-element\">\r\n <input\r\n class=\"input\"\r\n type=\"text\"\r\n [disabled]=\"disabled\"\r\n [value]=\"item\"\r\n (focusout)=\"editFromValue(getValueFromEvent($event), index)\"\r\n />\r\n <button\r\n *ngIf=\"!disabled\"\r\n class=\"property-button\"\r\n (click)=\"removeFromValue(index)\"\r\n >\r\n <div class=\"icon close-icon\"></div>\r\n </button>\r\n</div>\r\n<div *ngIf=\"!disabled\" class=\"value-item-input\">\r\n <div class=\"input relatively-positioned\">\r\n <input type=\"text\" (keyup)=\"onKeyUp($event)\" [(ngModel)]=\"valueInput\" />\r\n <button\r\n *ngIf=\"valueInput !== ''\"\r\n class=\"clear\"\r\n (click)=\"clearInput()\"\r\n ></button>\r\n </div>\r\n <button class=\"property-button\" (click)=\"addToValue()\">\r\n <div class=\"icon add-icon\"></div>\r\n </button>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
7219
7947
  }
7220
7948
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TextListEditorComponent, decorators: [{
7221
7949
  type: Component,
7222
- args: [{ standalone: true, selector: 'daga-text-list-editor', imports: [CommonModule, LuxModule, FormsModule], template: "<div *ngFor=\"let item of value; let index = index\" class=\"value-item-element\">\n <lux-input\n type=\"text\"\n [disabled]=\"disabled\"\n [value]=\"item\"\n (focusout)=\"editFromValue(getValueFromEvent($event), index)\"\n ></lux-input>\n <button\n *ngIf=\"!disabled\"\n class=\"property-button\"\n (click)=\"removeFromValue(index)\"\n >\n <div class=\"icon close-icon\"></div>\n </button>\n</div>\n<div *ngIf=\"!disabled\" class=\"value-item-input\">\n <div class=\"relatively-positioned\">\n <lux-input\n type=\"text\"\n (keyup)=\"onKeyUp($event)\"\n [(ngModel)]=\"valueInput\"\n ></lux-input>\n <button\n *ngIf=\"valueInput !== ''\"\n class=\"clear\"\n (click)=\"clearInput()\"\n ></button>\n </div>\n <button class=\"property-button\" (click)=\"addToValue()\">\n <div class=\"icon add-icon\"></div>\n </button>\n</div>\n" }]
7950
+ args: [{ standalone: true, selector: 'daga-text-list-editor', imports: [CommonModule, FormsModule], template: "<div *ngFor=\"let item of value; let index = index\" class=\"value-item-element\">\r\n <input\r\n class=\"input\"\r\n type=\"text\"\r\n [disabled]=\"disabled\"\r\n [value]=\"item\"\r\n (focusout)=\"editFromValue(getValueFromEvent($event), index)\"\r\n />\r\n <button\r\n *ngIf=\"!disabled\"\r\n class=\"property-button\"\r\n (click)=\"removeFromValue(index)\"\r\n >\r\n <div class=\"icon close-icon\"></div>\r\n </button>\r\n</div>\r\n<div *ngIf=\"!disabled\" class=\"value-item-input\">\r\n <div class=\"input relatively-positioned\">\r\n <input type=\"text\" (keyup)=\"onKeyUp($event)\" [(ngModel)]=\"valueInput\" />\r\n <button\r\n *ngIf=\"valueInput !== ''\"\r\n class=\"clear\"\r\n (click)=\"clearInput()\"\r\n ></button>\r\n </div>\r\n <button class=\"property-button\" (click)=\"addToValue()\">\r\n <div class=\"icon add-icon\"></div>\r\n </button>\r\n</div>\r\n" }]
7223
7951
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { value: [{
7224
7952
  type: Input
7225
7953
  }], valueInput: [{
@@ -7232,18 +7960,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
7232
7960
 
7233
7961
  /**
7234
7962
  * Editor of a property of text map type within a property editor.
7235
- * @see TextMap
7963
+ * @see Type.TextMap
7236
7964
  * @see PropertyEditor
7237
7965
  * @see ValueSet
7238
7966
  * @private
7239
7967
  */
7240
7968
  class TextMapEditorComponent {
7241
- set value(val) {
7242
- if (val === this._value) {
7969
+ set value(value) {
7970
+ if (value === this._value) {
7243
7971
  return;
7244
7972
  }
7245
- const initialAndEmpty = Object.keys(val).length === 0 && Object.keys(this._value).length === 0;
7246
- this._value = val;
7973
+ const initialAndEmpty = Object.keys(value).length === 0 && Object.keys(this._value).length === 0;
7974
+ this._value = value;
7247
7975
  if (initialAndEmpty) {
7248
7976
  this.valueChange.emit(this._value);
7249
7977
  }
@@ -7298,11 +8026,11 @@ class TextMapEditorComponent {
7298
8026
  }
7299
8027
  }
7300
8028
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TextMapEditorComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
7301
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: TextMapEditorComponent, isStandalone: true, selector: "daga-text-map-editor", inputs: { value: "value", keyInput: "keyInput", valueInput: "valueInput", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div *ngFor=\"let item of value | keyvalue\" class=\"value-item-element\">\n <lux-input\n type=\"text\"\n [disabled]=\"disabled\"\n [value]=\"item.key\"\n (focusout)=\"editKey(item.key, getValueFromEvent($event))\"\n ></lux-input>\n <lux-input\n type=\"text\"\n [disabled]=\"disabled\"\n [value]=\"item.value\"\n (focusout)=\"editValue(item.key, getValueFromEvent($event))\"\n ></lux-input>\n <button\n *ngIf=\"!disabled\"\n class=\"property-button\"\n (click)=\"removeFromValue(item.key)\"\n >\n <div class=\"icon close-icon\"></div>\n </button>\n</div>\n<div *ngIf=\"!disabled\" class=\"value-item-input\">\n <div class=\"relatively-positioned\">\n <lux-input\n type=\"text\"\n (keyup)=\"onKeyUp($event)\"\n [(ngModel)]=\"keyInput\"\n ></lux-input>\n <button\n *ngIf=\"keyInput !== ''\"\n class=\"clear\"\n (click)=\"clearKeyInput()\"\n ></button>\n </div>\n <div class=\"relatively-positioned\">\n <lux-input\n type=\"text\"\n (keyup)=\"onKeyUp($event)\"\n [(ngModel)]=\"valueInput\"\n ></lux-input>\n <button\n *ngIf=\"valueInput !== ''\"\n class=\"clear\"\n (click)=\"clearValueInput()\"\n ></button>\n </div>\n <button class=\"property-button\" (click)=\"addToValue()\">\n <div class=\"icon add-icon\"></div>\n </button>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2.KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: LuxModule }, { kind: "component", type: i3.InputComponent, selector: "lux-input", inputs: ["rows", "cols", "step", "min", "max", "lang", "inlineErrors", "inputId", "aria-label", "readonly", "disabled", "pattern", "currency", "placeholder", "required", "type", "value"], outputs: ["valueChange", "keyPress"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
8029
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: TextMapEditorComponent, isStandalone: true, selector: "daga-text-map-editor", inputs: { value: "value", keyInput: "keyInput", valueInput: "valueInput", disabled: "disabled" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div *ngFor=\"let item of value | keyvalue\" class=\"value-item-element\">\r\n <input\r\n class=\"input\"\r\n type=\"text\"\r\n [disabled]=\"disabled\"\r\n [value]=\"item.key\"\r\n (focusout)=\"editKey(item.key, getValueFromEvent($event))\"\r\n />\r\n <input\r\n class=\"input\"\r\n type=\"text\"\r\n [disabled]=\"disabled\"\r\n [value]=\"item.value\"\r\n (focusout)=\"editValue(item.key, getValueFromEvent($event))\"\r\n />\r\n <button\r\n *ngIf=\"!disabled\"\r\n class=\"property-button\"\r\n (click)=\"removeFromValue(item.key)\"\r\n >\r\n <div class=\"icon close-icon\"></div>\r\n </button>\r\n</div>\r\n<div *ngIf=\"!disabled\" class=\"value-item-input\">\r\n <div class=\"input relatively-positioned\">\r\n <input type=\"text\" (keyup)=\"onKeyUp($event)\" [(ngModel)]=\"keyInput\" />\r\n <button\r\n *ngIf=\"keyInput !== ''\"\r\n class=\"clear\"\r\n (click)=\"clearKeyInput()\"\r\n ></button>\r\n </div>\r\n <div class=\"input relatively-positioned\">\r\n <input type=\"text\" (keyup)=\"onKeyUp($event)\" [(ngModel)]=\"valueInput\" />\r\n <button\r\n *ngIf=\"valueInput !== ''\"\r\n class=\"clear\"\r\n (click)=\"clearValueInput()\"\r\n ></button>\r\n </div>\r\n <button class=\"property-button\" (click)=\"addToValue()\">\r\n <div class=\"icon add-icon\"></div>\r\n </button>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
7302
8030
  }
7303
8031
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TextMapEditorComponent, decorators: [{
7304
8032
  type: Component,
7305
- args: [{ standalone: true, selector: 'daga-text-map-editor', imports: [CommonModule, LuxModule, FormsModule], template: "<div *ngFor=\"let item of value | keyvalue\" class=\"value-item-element\">\n <lux-input\n type=\"text\"\n [disabled]=\"disabled\"\n [value]=\"item.key\"\n (focusout)=\"editKey(item.key, getValueFromEvent($event))\"\n ></lux-input>\n <lux-input\n type=\"text\"\n [disabled]=\"disabled\"\n [value]=\"item.value\"\n (focusout)=\"editValue(item.key, getValueFromEvent($event))\"\n ></lux-input>\n <button\n *ngIf=\"!disabled\"\n class=\"property-button\"\n (click)=\"removeFromValue(item.key)\"\n >\n <div class=\"icon close-icon\"></div>\n </button>\n</div>\n<div *ngIf=\"!disabled\" class=\"value-item-input\">\n <div class=\"relatively-positioned\">\n <lux-input\n type=\"text\"\n (keyup)=\"onKeyUp($event)\"\n [(ngModel)]=\"keyInput\"\n ></lux-input>\n <button\n *ngIf=\"keyInput !== ''\"\n class=\"clear\"\n (click)=\"clearKeyInput()\"\n ></button>\n </div>\n <div class=\"relatively-positioned\">\n <lux-input\n type=\"text\"\n (keyup)=\"onKeyUp($event)\"\n [(ngModel)]=\"valueInput\"\n ></lux-input>\n <button\n *ngIf=\"valueInput !== ''\"\n class=\"clear\"\n (click)=\"clearValueInput()\"\n ></button>\n </div>\n <button class=\"property-button\" (click)=\"addToValue()\">\n <div class=\"icon add-icon\"></div>\n </button>\n</div>\n" }]
8033
+ args: [{ standalone: true, selector: 'daga-text-map-editor', imports: [CommonModule, FormsModule], template: "<div *ngFor=\"let item of value | keyvalue\" class=\"value-item-element\">\r\n <input\r\n class=\"input\"\r\n type=\"text\"\r\n [disabled]=\"disabled\"\r\n [value]=\"item.key\"\r\n (focusout)=\"editKey(item.key, getValueFromEvent($event))\"\r\n />\r\n <input\r\n class=\"input\"\r\n type=\"text\"\r\n [disabled]=\"disabled\"\r\n [value]=\"item.value\"\r\n (focusout)=\"editValue(item.key, getValueFromEvent($event))\"\r\n />\r\n <button\r\n *ngIf=\"!disabled\"\r\n class=\"property-button\"\r\n (click)=\"removeFromValue(item.key)\"\r\n >\r\n <div class=\"icon close-icon\"></div>\r\n </button>\r\n</div>\r\n<div *ngIf=\"!disabled\" class=\"value-item-input\">\r\n <div class=\"input relatively-positioned\">\r\n <input type=\"text\" (keyup)=\"onKeyUp($event)\" [(ngModel)]=\"keyInput\" />\r\n <button\r\n *ngIf=\"keyInput !== ''\"\r\n class=\"clear\"\r\n (click)=\"clearKeyInput()\"\r\n ></button>\r\n </div>\r\n <div class=\"input relatively-positioned\">\r\n <input type=\"text\" (keyup)=\"onKeyUp($event)\" [(ngModel)]=\"valueInput\" />\r\n <button\r\n *ngIf=\"valueInput !== ''\"\r\n class=\"clear\"\r\n (click)=\"clearValueInput()\"\r\n ></button>\r\n </div>\r\n <button class=\"property-button\" (click)=\"addToValue()\">\r\n <div class=\"icon add-icon\"></div>\r\n </button>\r\n</div>\r\n" }]
7306
8034
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { value: [{
7307
8035
  type: Input
7308
8036
  }], keyInput: [{
@@ -7326,7 +8054,8 @@ class ObjectEditorComponent {
7326
8054
  return this.canvasProvider.getCanvas();
7327
8055
  }
7328
8056
  get userCanEdit() {
7329
- return this.canvas.canUserPerformAction(DiagramActions.UpdateValues);
8057
+ return (this.canvas.canUserPerformAction(DiagramActions.UpdateValues) &&
8058
+ this.valueSet?.rootElement?.['removed'] !== true);
7330
8059
  }
7331
8060
  get valueSet() {
7332
8061
  return this._valueSet;
@@ -7343,10 +8072,6 @@ class ObjectEditorComponent {
7343
8072
  this.depth = 0;
7344
8073
  // Attributes for the template
7345
8074
  this.Type = Type;
7346
- this.booleanRadioItems = [
7347
- { value: false, label: 'No' },
7348
- { value: true, label: 'Yes' }
7349
- ];
7350
8075
  }
7351
8076
  displayProperty(property) {
7352
8077
  if (this.valueSet === undefined) {
@@ -7398,24 +8123,35 @@ class ObjectEditorComponent {
7398
8123
  }
7399
8124
  }
7400
8125
  }
7401
- convertDate(date) {
8126
+ dateToLocalDatetimeString(date) {
7402
8127
  if (typeof date === 'string') {
7403
8128
  return date;
7404
8129
  }
7405
- return date && !isNaN(date.valueOf()) ? new Date(date).toISOString() : '';
8130
+ if (date === null || date === undefined || isNaN(date.valueOf())) {
8131
+ return '';
8132
+ }
8133
+ const offsetDate = new Date(date);
8134
+ const timezoneOffset = offsetDate.getTimezoneOffset();
8135
+ offsetDate.setMinutes(offsetDate.getMinutes() - timezoneOffset);
8136
+ return offsetDate.toISOString().substring(0, 19); // for user friendliness, we trim off the milliseconds
8137
+ }
8138
+ localDatetimeStringToDate(string) {
8139
+ // with no timezone specified, the local time is assumed
8140
+ return new Date(string);
7406
8141
  }
7407
8142
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: ObjectEditorComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
7408
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: ObjectEditorComponent, isStandalone: true, selector: "daga-object-editor", inputs: { valueSet: "valueSet", depth: "depth" }, ngImport: i0, template: "<div\n *ngFor=\"let property of valueSet?.displayedProperties || []\"\n class=\"property\"\n [class]=\"property.name + ' depth-' + depth\"\n>\n <p class=\"property-name\">\n {{ property.name }}\n <button class=\"property-button\" (click)=\"hideProperty(property.name)\">\n <div class=\"icon hide-icon\"></div>\n </button>\n </p>\n\n <lux-input\n *ngIf=\"property.type === Type.Text\"\n type=\"text\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <textarea\n *ngIf=\"property.type === Type.TextArea\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [ngModel]=\"valueSet?.getValue(property.name)\"\n (ngModelChange)=\"setValue(property, $event)\"\n ></textarea>\n <lux-input\n *ngIf=\"property.type === Type.Number\"\n type=\"number\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <lux-input\n *ngIf=\"property.type === Type.Color\"\n type=\"color\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <lux-input\n *ngIf=\"property.type === Type.Date\"\n type=\"date\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"convertDate(valueSet?.getValue(property.name))\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <lux-input\n *ngIf=\"property.type === Type.Time\"\n type=\"time\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"convertDate(valueSet?.getValue(property.name))\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <lux-input\n *ngIf=\"property.type === Type.Url\"\n type=\"url\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <lux-radiogroup\n *ngIf=\"property.type === Type.Boolean\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [items]=\"booleanRadioItems\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-radiogroup>\n <lux-autocomplete\n *ngIf=\"property.type === Type.Option\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [dataSource]=\"property.options || []\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-autocomplete>\n <lux-autocomplete-list\n *ngIf=\"property.type === Type.OptionList\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [dataSource]=\"property.options || []\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-autocomplete-list>\n <daga-text-list-editor\n *ngIf=\"property.type === Type.TextList\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></daga-text-list-editor>\n <daga-text-map-editor\n *ngIf=\"property.type === Type.TextMap\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></daga-text-map-editor>\n <div *ngIf=\"property.type === Type.Object\" class=\"left-bar\">\n <daga-object-editor\n [valueSet]=\"valueSet?.getSubValueSet(property.name)\"\n [depth]=\"depth + 1\"\n ></daga-object-editor>\n </div>\n</div>\n<div class=\"property\" *ngIf=\"valueSet && valueSet.hiddenProperties.length > 0\">\n <p class=\"property-name\">Add property:</p>\n <select (change)=\"displayProperty($event)\">\n <option value=\"\">Select a property</option>\n <option\n *ngFor=\"let property of valueSet?.hiddenProperties || []\"\n [value]=\"property.name\"\n >\n {{ property.name }}\n </option>\n </select>\n</div>\n", dependencies: [{ kind: "component", type: ObjectEditorComponent, selector: "daga-object-editor", inputs: ["valueSet", "depth"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: LuxModule }, { kind: "component", type: i3.AutocompleteComponent, selector: "lux-autocomplete", inputs: ["inputId", "disabled", "readonly", "label", "canAddNewValues", "keepOpenAfterDelete", "value", "dataSource", "required", "placeholder", "resolveLabelsFunction", "populateFunction", "instance"], outputs: ["valueChange", "dataSourceChange"] }, { kind: "component", type: i3.AutocompleteListComponent, selector: "lux-autocomplete-list", inputs: ["value", "lang", "inputId", "dataSource", "placeholder", "disabled", "deleteLabelTemplate", "addMessage", "required", "resolveLabelsFunction", "populateFunction", "instance"], outputs: ["valueChange"] }, { kind: "component", type: i3.InputComponent, selector: "lux-input", inputs: ["rows", "cols", "step", "min", "max", "lang", "inlineErrors", "inputId", "aria-label", "readonly", "disabled", "pattern", "currency", "placeholder", "required", "type", "value"], outputs: ["valueChange", "keyPress"] }, { kind: "component", type: i3.RadiogroupComponent, selector: "lux-radiogroup", inputs: ["name", "disabled", "readonly", "required", "items", "value"], outputs: ["itemsChange", "valueChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: TextListEditorComponent, selector: "daga-text-list-editor", inputs: ["value", "valueInput", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: TextMapEditorComponent, selector: "daga-text-map-editor", inputs: ["value", "keyInput", "valueInput", "disabled"], outputs: ["valueChange"] }] }); }
8143
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: ObjectEditorComponent, isStandalone: true, selector: "daga-object-editor", inputs: { valueSet: "valueSet", depth: "depth" }, ngImport: i0, template: "<div\r\n *ngFor=\"let property of valueSet?.displayedProperties || []\"\r\n class=\"property\"\r\n [class]=\"property.name + ' depth-' + depth\"\r\n>\r\n <p class=\"property-name\">\r\n {{ property.name }}\r\n <button class=\"property-button\" (click)=\"hideProperty(property.name)\">\r\n <div class=\"icon hide-icon\"></div>\r\n </button>\r\n </p>\r\n\r\n <input\r\n *ngIf=\"property.type === Type.Text\"\r\n type=\"text\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <textarea\r\n *ngIf=\"property.type === Type.TextArea\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n ></textarea>\r\n <input\r\n *ngIf=\"property.type === Type.Number\"\r\n type=\"number\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <input\r\n *ngIf=\"property.type === Type.Color\"\r\n type=\"text\"\r\n pattern=\"#\\d{6}\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <input\r\n *ngIf=\"property.type === Type.Datetime\"\r\n type=\"datetime-local\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"dateToLocalDatetimeString(valueSet?.getValue(property.name))\"\r\n (ngModelChange)=\"setValue(property, localDatetimeStringToDate($event))\"\r\n />\r\n <input\r\n *ngIf=\"property.type === Type.Date\"\r\n type=\"date\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <input\r\n *ngIf=\"property.type === Type.Time\"\r\n type=\"time\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <input\r\n *ngIf=\"property.type === Type.Url\"\r\n type=\"url\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <div class=\"radio\" *ngIf=\"property.type === Type.Boolean\">\r\n <label class=\"radio-item radio-start\">\r\n <input\r\n type=\"radio\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [name]=\"property.name\"\r\n value=\"false\"\r\n [checked]=\"valueSet?.getValue(property.name) === false\"\r\n (change)=\"setValue(property, false)\"\r\n />\r\n No\r\n </label>\r\n <label class=\"radio-item radio-end\">\r\n <input\r\n type=\"radio\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [name]=\"property.name\"\r\n value=\"true\"\r\n [checked]=\"valueSet?.getValue(property.name) === true\"\r\n (change)=\"setValue(property, true)\"\r\n />\r\n Yes\r\n </label>\r\n </div>\r\n <div class=\"relatively-positioned\" *ngIf=\"property.type === Type.Option\">\r\n <daga-autocomplete\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [options]=\"property.options || []\"\r\n [value]=\"valueSet?.getValue(property.name)\"\r\n (valueChange)=\"setValue(property, $event)\"\r\n />\r\n </div>\r\n <daga-option-list-editor\r\n *ngIf=\"property.type === Type.OptionList\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [options]=\"property.options || []\"\r\n [value]=\"valueSet?.getValue(property.name)\"\r\n (valueChange)=\"setValue(property, $event)\"\r\n ></daga-option-list-editor>\r\n <daga-text-list-editor\r\n *ngIf=\"property.type === Type.TextList\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [value]=\"valueSet?.getValue(property.name)\"\r\n (valueChange)=\"setValue(property, $event)\"\r\n ></daga-text-list-editor>\r\n <daga-text-map-editor\r\n *ngIf=\"property.type === Type.TextMap\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [value]=\"valueSet?.getValue(property.name)\"\r\n (valueChange)=\"setValue(property, $event)\"\r\n ></daga-text-map-editor>\r\n <div *ngIf=\"property.type === Type.Object\" class=\"left-bar\">\r\n <daga-object-editor\r\n [valueSet]=\"valueSet?.getSubValueSet(property.name)\"\r\n [depth]=\"depth + 1\"\r\n ></daga-object-editor>\r\n </div>\r\n</div>\r\n<div class=\"property\" *ngIf=\"valueSet && valueSet.hiddenProperties.length > 0\">\r\n <p class=\"property-name\">Add property:</p>\r\n <select (change)=\"displayProperty($event)\">\r\n <option value=\"\">Select a property</option>\r\n <option\r\n *ngFor=\"let property of valueSet?.hiddenProperties || []\"\r\n [value]=\"property.name\"\r\n >\r\n {{ property.name }}\r\n </option>\r\n </select>\r\n</div>\r\n", dependencies: [{ kind: "component", type: ObjectEditorComponent, selector: "daga-object-editor", inputs: ["valueSet", "depth"] }, { kind: "component", type: AutocompleteComponent, selector: "daga-autocomplete", inputs: ["value", "valueInput", "options", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: OptionListEditorComponent, selector: "daga-option-list-editor", inputs: ["value", "options", "valueInput", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: TextListEditorComponent, selector: "daga-text-list-editor", inputs: ["value", "valueInput", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: TextMapEditorComponent, selector: "daga-text-map-editor", inputs: ["value", "keyInput", "valueInput", "disabled"], outputs: ["valueChange"] }] }); }
7409
8144
  }
7410
8145
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: ObjectEditorComponent, decorators: [{
7411
8146
  type: Component,
7412
8147
  args: [{ standalone: true, selector: 'daga-object-editor', imports: [
8148
+ AutocompleteComponent,
7413
8149
  CommonModule,
7414
- LuxModule,
7415
8150
  FormsModule,
8151
+ OptionListEditorComponent,
7416
8152
  TextListEditorComponent,
7417
8153
  TextMapEditorComponent
7418
- ], template: "<div\n *ngFor=\"let property of valueSet?.displayedProperties || []\"\n class=\"property\"\n [class]=\"property.name + ' depth-' + depth\"\n>\n <p class=\"property-name\">\n {{ property.name }}\n <button class=\"property-button\" (click)=\"hideProperty(property.name)\">\n <div class=\"icon hide-icon\"></div>\n </button>\n </p>\n\n <lux-input\n *ngIf=\"property.type === Type.Text\"\n type=\"text\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <textarea\n *ngIf=\"property.type === Type.TextArea\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [ngModel]=\"valueSet?.getValue(property.name)\"\n (ngModelChange)=\"setValue(property, $event)\"\n ></textarea>\n <lux-input\n *ngIf=\"property.type === Type.Number\"\n type=\"number\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <lux-input\n *ngIf=\"property.type === Type.Color\"\n type=\"color\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <lux-input\n *ngIf=\"property.type === Type.Date\"\n type=\"date\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"convertDate(valueSet?.getValue(property.name))\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <lux-input\n *ngIf=\"property.type === Type.Time\"\n type=\"time\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"convertDate(valueSet?.getValue(property.name))\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <lux-input\n *ngIf=\"property.type === Type.Url\"\n type=\"url\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-input>\n <lux-radiogroup\n *ngIf=\"property.type === Type.Boolean\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [items]=\"booleanRadioItems\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-radiogroup>\n <lux-autocomplete\n *ngIf=\"property.type === Type.Option\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [dataSource]=\"property.options || []\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-autocomplete>\n <lux-autocomplete-list\n *ngIf=\"property.type === Type.OptionList\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [dataSource]=\"property.options || []\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></lux-autocomplete-list>\n <daga-text-list-editor\n *ngIf=\"property.type === Type.TextList\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></daga-text-list-editor>\n <daga-text-map-editor\n *ngIf=\"property.type === Type.TextMap\"\n [disabled]=\"!property.editable || !userCanEdit\"\n [value]=\"valueSet?.getValue(property.name)\"\n (valueChange)=\"setValue(property, $event)\"\n ></daga-text-map-editor>\n <div *ngIf=\"property.type === Type.Object\" class=\"left-bar\">\n <daga-object-editor\n [valueSet]=\"valueSet?.getSubValueSet(property.name)\"\n [depth]=\"depth + 1\"\n ></daga-object-editor>\n </div>\n</div>\n<div class=\"property\" *ngIf=\"valueSet && valueSet.hiddenProperties.length > 0\">\n <p class=\"property-name\">Add property:</p>\n <select (change)=\"displayProperty($event)\">\n <option value=\"\">Select a property</option>\n <option\n *ngFor=\"let property of valueSet?.hiddenProperties || []\"\n [value]=\"property.name\"\n >\n {{ property.name }}\n </option>\n </select>\n</div>\n" }]
8154
+ ], template: "<div\r\n *ngFor=\"let property of valueSet?.displayedProperties || []\"\r\n class=\"property\"\r\n [class]=\"property.name + ' depth-' + depth\"\r\n>\r\n <p class=\"property-name\">\r\n {{ property.name }}\r\n <button class=\"property-button\" (click)=\"hideProperty(property.name)\">\r\n <div class=\"icon hide-icon\"></div>\r\n </button>\r\n </p>\r\n\r\n <input\r\n *ngIf=\"property.type === Type.Text\"\r\n type=\"text\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <textarea\r\n *ngIf=\"property.type === Type.TextArea\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n ></textarea>\r\n <input\r\n *ngIf=\"property.type === Type.Number\"\r\n type=\"number\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <input\r\n *ngIf=\"property.type === Type.Color\"\r\n type=\"text\"\r\n pattern=\"#\\d{6}\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <input\r\n *ngIf=\"property.type === Type.Datetime\"\r\n type=\"datetime-local\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"dateToLocalDatetimeString(valueSet?.getValue(property.name))\"\r\n (ngModelChange)=\"setValue(property, localDatetimeStringToDate($event))\"\r\n />\r\n <input\r\n *ngIf=\"property.type === Type.Date\"\r\n type=\"date\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <input\r\n *ngIf=\"property.type === Type.Time\"\r\n type=\"time\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <input\r\n *ngIf=\"property.type === Type.Url\"\r\n type=\"url\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [ngModel]=\"valueSet?.getValue(property.name)\"\r\n (ngModelChange)=\"setValue(property, $event)\"\r\n />\r\n <div class=\"radio\" *ngIf=\"property.type === Type.Boolean\">\r\n <label class=\"radio-item radio-start\">\r\n <input\r\n type=\"radio\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [name]=\"property.name\"\r\n value=\"false\"\r\n [checked]=\"valueSet?.getValue(property.name) === false\"\r\n (change)=\"setValue(property, false)\"\r\n />\r\n No\r\n </label>\r\n <label class=\"radio-item radio-end\">\r\n <input\r\n type=\"radio\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [name]=\"property.name\"\r\n value=\"true\"\r\n [checked]=\"valueSet?.getValue(property.name) === true\"\r\n (change)=\"setValue(property, true)\"\r\n />\r\n Yes\r\n </label>\r\n </div>\r\n <div class=\"relatively-positioned\" *ngIf=\"property.type === Type.Option\">\r\n <daga-autocomplete\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [options]=\"property.options || []\"\r\n [value]=\"valueSet?.getValue(property.name)\"\r\n (valueChange)=\"setValue(property, $event)\"\r\n />\r\n </div>\r\n <daga-option-list-editor\r\n *ngIf=\"property.type === Type.OptionList\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [options]=\"property.options || []\"\r\n [value]=\"valueSet?.getValue(property.name)\"\r\n (valueChange)=\"setValue(property, $event)\"\r\n ></daga-option-list-editor>\r\n <daga-text-list-editor\r\n *ngIf=\"property.type === Type.TextList\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [value]=\"valueSet?.getValue(property.name)\"\r\n (valueChange)=\"setValue(property, $event)\"\r\n ></daga-text-list-editor>\r\n <daga-text-map-editor\r\n *ngIf=\"property.type === Type.TextMap\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [value]=\"valueSet?.getValue(property.name)\"\r\n (valueChange)=\"setValue(property, $event)\"\r\n ></daga-text-map-editor>\r\n <div *ngIf=\"property.type === Type.Object\" class=\"left-bar\">\r\n <daga-object-editor\r\n [valueSet]=\"valueSet?.getSubValueSet(property.name)\"\r\n [depth]=\"depth + 1\"\r\n ></daga-object-editor>\r\n </div>\r\n</div>\r\n<div class=\"property\" *ngIf=\"valueSet && valueSet.hiddenProperties.length > 0\">\r\n <p class=\"property-name\">Add property:</p>\r\n <select (change)=\"displayProperty($event)\">\r\n <option value=\"\">Select a property</option>\r\n <option\r\n *ngFor=\"let property of valueSet?.hiddenProperties || []\"\r\n [value]=\"property.name\"\r\n >\r\n {{ property.name }}\r\n </option>\r\n </select>\r\n</div>\r\n" }]
7419
8155
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: CanvasProviderService }], propDecorators: { valueSet: [{
7420
8156
  type: Input
7421
8157
  }], depth: [{
@@ -7474,11 +8210,11 @@ class PropertyEditorComponent {
7474
8210
  }
7475
8211
  }
7476
8212
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: PropertyEditorComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
7477
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: PropertyEditorComponent, isStandalone: true, selector: "daga-property-editor", inputs: { location: "location", direction: "direction", width: "width", valueSet: "valueSet" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true }], ngImport: i0, template: "<div #panel class=\"panel bottom {{ location }} {{ direction }}\">\n <daga-collapse-button\n [disabled]=\"\n !valueSet ||\n !valueSet.propertySet ||\n !valueSet.propertySet.hasProperties()\n \"\n [direction]=\"direction\"\n [collapsableSelector]=\"panel\"\n collapsableAdditionalSelector=\".panel-content\"\n rule=\"display\"\n collapsedValue=\"none\"\n visibleValue=\"block\"\n />\n <div\n *ngIf=\"\n valueSet && valueSet.propertySet && valueSet.propertySet.hasProperties()\n \"\n class=\"panel-content\"\n >\n <p *ngIf=\"title\" class=\"title\">{{ title }}</p>\n <daga-object-editor [valueSet]=\"valueSet\"></daga-object-editor>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CollapseButtonComponent, selector: "daga-collapse-button", inputs: ["collapsableSelector", "collapsableAdditionalSelector", "collapsed", "disabled", "direction", "rule", "collapsedValue", "visibleValue"] }, { kind: "component", type: ObjectEditorComponent, selector: "daga-object-editor", inputs: ["valueSet", "depth"] }] }); }
8213
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: PropertyEditorComponent, isStandalone: true, selector: "daga-property-editor", inputs: { location: "location", direction: "direction", width: "width", valueSet: "valueSet" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true }], ngImport: i0, template: "<div #panel class=\"panel bottom {{ location }} {{ direction }}\">\r\n <daga-collapse-button\r\n #collapse\r\n [disabled]=\"\r\n !valueSet ||\r\n !valueSet.propertySet ||\r\n !valueSet.propertySet.hasProperties()\r\n \"\r\n [direction]=\"direction\"\r\n [collapsableSelector]=\"panel\"\r\n collapsableAdditionalSelector=\".panel-content\"\r\n rule=\"display\"\r\n collapsedValue=\"none\"\r\n visibleValue=\"block\"\r\n />\r\n <div\r\n *ngIf=\"\r\n valueSet &&\r\n valueSet.propertySet &&\r\n valueSet.propertySet.hasProperties() &&\r\n !collapse.collapsed\r\n \"\r\n class=\"panel-content\"\r\n >\r\n <p *ngIf=\"title\" class=\"title\">{{ title }}</p>\r\n <daga-object-editor [valueSet]=\"valueSet\"></daga-object-editor>\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CollapseButtonComponent, selector: "daga-collapse-button", inputs: ["collapsableSelector", "collapsableAdditionalSelector", "collapsed", "disabled", "direction", "rule", "collapsedValue", "visibleValue"] }, { kind: "component", type: ObjectEditorComponent, selector: "daga-object-editor", inputs: ["valueSet", "depth"] }] }); }
7478
8214
  }
7479
8215
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: PropertyEditorComponent, decorators: [{
7480
8216
  type: Component,
7481
- args: [{ standalone: true, selector: 'daga-property-editor', imports: [CommonModule, CollapseButtonComponent, ObjectEditorComponent], template: "<div #panel class=\"panel bottom {{ location }} {{ direction }}\">\n <daga-collapse-button\n [disabled]=\"\n !valueSet ||\n !valueSet.propertySet ||\n !valueSet.propertySet.hasProperties()\n \"\n [direction]=\"direction\"\n [collapsableSelector]=\"panel\"\n collapsableAdditionalSelector=\".panel-content\"\n rule=\"display\"\n collapsedValue=\"none\"\n visibleValue=\"block\"\n />\n <div\n *ngIf=\"\n valueSet && valueSet.propertySet && valueSet.propertySet.hasProperties()\n \"\n class=\"panel-content\"\n >\n <p *ngIf=\"title\" class=\"title\">{{ title }}</p>\n <daga-object-editor [valueSet]=\"valueSet\"></daga-object-editor>\n </div>\n</div>\n" }]
8217
+ args: [{ standalone: true, selector: 'daga-property-editor', imports: [CommonModule, CollapseButtonComponent, ObjectEditorComponent], template: "<div #panel class=\"panel bottom {{ location }} {{ direction }}\">\r\n <daga-collapse-button\r\n #collapse\r\n [disabled]=\"\r\n !valueSet ||\r\n !valueSet.propertySet ||\r\n !valueSet.propertySet.hasProperties()\r\n \"\r\n [direction]=\"direction\"\r\n [collapsableSelector]=\"panel\"\r\n collapsableAdditionalSelector=\".panel-content\"\r\n rule=\"display\"\r\n collapsedValue=\"none\"\r\n visibleValue=\"block\"\r\n />\r\n <div\r\n *ngIf=\"\r\n valueSet &&\r\n valueSet.propertySet &&\r\n valueSet.propertySet.hasProperties() &&\r\n !collapse.collapsed\r\n \"\r\n class=\"panel-content\"\r\n >\r\n <p *ngIf=\"title\" class=\"title\">{{ title }}</p>\r\n <daga-object-editor [valueSet]=\"valueSet\"></daga-object-editor>\r\n </div>\r\n</div>\r\n" }]
7482
8218
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { panel: [{
7483
8219
  type: ViewChild,
7484
8220
  args: ['panel']
@@ -7544,7 +8280,7 @@ class DiagramEditorComponent {
7544
8280
  this.canvasProvider.initCanvasView(this.appendTo.nativeElement);
7545
8281
  }
7546
8282
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DiagramEditorComponent, deps: [{ token: DagaConfigurationService }, { token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
7547
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: DiagramEditorComponent, isStandalone: true, selector: "daga-diagram-editor", viewQueries: [{ propertyName: "appendTo", first: true, predicate: ["appendTo"], descendants: true }, { propertyName: "diagramButtons", first: true, predicate: ["diagramButtons"], descendants: true }, { propertyName: "palette", first: true, predicate: ["palette"], descendants: true }, { propertyName: "propertyEditor", first: true, predicate: ["propertyEditor"], descendants: true }], ngImport: i0, template: "<div class=\"append-to\" #appendTo></div>\n<daga-diagram-buttons\n *ngIf=\"config.components?.buttons?.exists !== false\"\n #diagramButtons\n [location]=\"config.components?.buttons?.location || Corner.BottomRight\"\n [direction]=\"config.components?.buttons?.direction || Side.Top\"\n [enableFilter]=\"config.components?.buttons?.enableFilter !== false\"\n [enableLayout]=\"config.components?.buttons?.enableLayout !== false\"\n [enableUndoRedo]=\"config.components?.buttons?.enableUndoRedo !== false\"\n [enableZoom]=\"config.components?.buttons?.enableZoom !== false\"\n [zoomRate]=\"config.components?.buttons?.zoomRate || 2\"\n/>\n<daga-palette\n *ngIf=\"\n config.components?.palette?.exists !== false &&\n config.components?.palette?.sections &&\n (config.components?.palette?.sections?.length || 0) > 0\n \"\n #palette\n [location]=\"config.components?.palette?.location || Corner.TopLeft\"\n [direction]=\"config.components?.palette?.direction || Side.Bottom\"\n [width]=\"config.components?.palette?.width || '12rem'\"\n [palettes]=\"config.components?.palette?.sections || []\"\n/>\n<daga-property-editor\n *ngIf=\"config.components?.propertyEditor?.exists !== false\"\n #propertyEditor\n [location]=\"config.components?.propertyEditor?.location || Corner.TopRight\"\n [direction]=\"config.components?.propertyEditor?.direction || Side.Bottom\"\n [width]=\"config.components?.propertyEditor?.width || '24rem'\"\n/>\n<daga-errors *ngIf=\"config.components?.errors?.exists !== false\" />\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DiagramButtonsComponent, selector: "daga-diagram-buttons", inputs: ["location", "direction", "enableFilter", "enableLayout", "enableUndoRedo", "enableZoom", "zoomRate"] }, { kind: "component", type: PaletteComponent, selector: "daga-palette", inputs: ["palettes", "currentPalette", "currentCategory", "location", "direction", "width"] }, { kind: "component", type: PropertyEditorComponent, selector: "daga-property-editor", inputs: ["location", "direction", "width", "valueSet"] }, { kind: "component", type: ErrorsComponent, selector: "daga-errors" }] }); }
8283
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: DiagramEditorComponent, isStandalone: true, selector: "daga-diagram-editor", viewQueries: [{ propertyName: "appendTo", first: true, predicate: ["appendTo"], descendants: true }, { propertyName: "diagramButtons", first: true, predicate: ["diagramButtons"], descendants: true }, { propertyName: "palette", first: true, predicate: ["palette"], descendants: true }, { propertyName: "propertyEditor", first: true, predicate: ["propertyEditor"], descendants: true }], ngImport: i0, template: "<div class=\"append-to\" #appendTo></div>\n<daga-diagram-buttons\n *ngIf=\"config.components?.buttons?.exists !== false\"\n #diagramButtons\n [location]=\"config.components?.buttons?.location || Corner.BottomRight\"\n [direction]=\"config.components?.buttons?.direction || Side.Top\"\n [enableFilter]=\"config.components?.buttons?.enableFilter !== false\"\n [enableLayout]=\"config.components?.buttons?.enableLayout !== false\"\n [enableUndoRedo]=\"config.components?.buttons?.enableUndoRedo !== false\"\n [enableZoom]=\"config.components?.buttons?.enableZoom !== false\"\n [zoomRate]=\"config.components?.buttons?.zoomRate || 2\"\n/>\n<daga-palette\n *ngIf=\"\n config.components?.palette?.exists !== false &&\n config.components?.palette?.sections &&\n (config.components?.palette?.sections?.length || 0) > 0\n \"\n #palette\n [location]=\"config.components?.palette?.location || Corner.TopLeft\"\n [direction]=\"config.components?.palette?.direction || Side.Bottom\"\n [width]=\"config.components?.palette?.width || '12rem'\"\n [palettes]=\"config.components?.palette?.sections || []\"\n/>\n<daga-property-editor\n *ngIf=\"config.components?.propertyEditor?.exists !== false\"\n #propertyEditor\n [location]=\"config.components?.propertyEditor?.location || Corner.TopRight\"\n [direction]=\"config.components?.propertyEditor?.direction || Side.Bottom\"\n [width]=\"config.components?.propertyEditor?.width || '24rem'\"\n/>\n<daga-errors *ngIf=\"config.components?.errors?.exists !== false\" />\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DiagramButtonsComponent, selector: "daga-diagram-buttons", inputs: ["location", "direction", "enableFilter", "enableLayout", "enableUndoRedo", "enableZoom", "zoomRate"] }, { kind: "component", type: PaletteComponent, selector: "daga-palette", inputs: ["palettes", "currentPalette", "currentCategory", "location", "direction", "width"] }, { kind: "component", type: PropertyEditorComponent, selector: "daga-property-editor", inputs: ["location", "direction", "width", "valueSet"] }, { kind: "component", type: ErrorsComponent, selector: "daga-errors" }] }); }
7548
8284
  }
7549
8285
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DiagramEditorComponent, decorators: [{
7550
8286
  type: Component,
@@ -7585,7 +8321,8 @@ class DagaExporter {
7585
8321
  updatedAt: model.updatedAt,
7586
8322
  nodes: [],
7587
8323
  connections: [],
7588
- data: model.valueSet.getValues()
8324
+ data: model.valueSet.getValues(),
8325
+ logicalClock: model.logicalClock
7589
8326
  };
7590
8327
  if (model.id) {
7591
8328
  result.id = model.id;
@@ -7593,7 +8330,7 @@ class DagaExporter {
7593
8330
  if (model.description) {
7594
8331
  result.description = model.description;
7595
8332
  }
7596
- for (const node of model.nodes) {
8333
+ for (const node of model.nodes.filter((e) => !e.removed)) {
7597
8334
  const sections = [];
7598
8335
  for (const section of node.sections) {
7599
8336
  const ports = [];
@@ -7625,7 +8362,7 @@ class DagaExporter {
7625
8362
  label: port.label?.text || ''
7626
8363
  });
7627
8364
  }
7628
- result.nodes.push({
8365
+ const newNode = {
7629
8366
  id: node.id,
7630
8367
  type: node.type.id,
7631
8368
  sections,
@@ -7635,9 +8372,13 @@ class DagaExporter {
7635
8372
  width: node.width,
7636
8373
  height: node.height,
7637
8374
  data: node.valueSet.getValues()
7638
- });
8375
+ };
8376
+ if (node.coordsTimestamp) {
8377
+ newNode.coordsTimestamp = node.coordsTimestamp;
8378
+ }
8379
+ result.nodes.push(newNode);
7639
8380
  }
7640
- for (const connection of model.connections) {
8381
+ for (const connection of model.connections.filter((e) => !e.removed)) {
7641
8382
  result.connections.push({
7642
8383
  id: connection.id,
7643
8384
  type: connection.type.id,
@@ -7670,6 +8411,9 @@ class DagaImporter {
7670
8411
  model.type = data.type;
7671
8412
  model.createdAt = new Date(data.createdAt);
7672
8413
  model.updatedAt = new Date(data.updatedAt);
8414
+ if (data.logicalClock) {
8415
+ model.logicalClock = data.logicalClock;
8416
+ }
7673
8417
  for (const node of data.nodes || []) {
7674
8418
  const newNodeType = model.nodes.types.get(node.type);
7675
8419
  if (newNodeType) {
@@ -7677,6 +8421,9 @@ class DagaImporter {
7677
8421
  model.nodes.add(newNode);
7678
8422
  newNode.width = node.width;
7679
8423
  newNode.height = node.height;
8424
+ if (node.coordsTimestamp) {
8425
+ newNode.coordsTimestamp = node.coordsTimestamp;
8426
+ }
7680
8427
  if (node.label) {
7681
8428
  // add node label
7682
8429
  if (newNodeType.label) {
@@ -7920,8 +8667,7 @@ class DagaModule {
7920
8667
  TextMapEditorComponent,
7921
8668
  PropertyEditorComponent,
7922
8669
  CommonModule,
7923
- FormsModule,
7924
- LuxModule], exports: [DiagramComponent, DiagramEditorComponent] }); }
8670
+ FormsModule], exports: [DiagramComponent, DiagramEditorComponent] }); }
7925
8671
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DagaModule, providers: [DagaConfigurationService, CanvasProviderService], imports: [CollapseButtonComponent,
7926
8672
  DiagramButtonsComponent,
7927
8673
  DiagramEditorComponent,
@@ -7932,8 +8678,7 @@ class DagaModule {
7932
8678
  TextMapEditorComponent,
7933
8679
  PropertyEditorComponent,
7934
8680
  CommonModule,
7935
- FormsModule,
7936
- LuxModule] }); }
8681
+ FormsModule] }); }
7937
8682
  }
7938
8683
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DagaModule, decorators: [{
7939
8684
  type: NgModule,
@@ -7951,17 +8696,224 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
7951
8696
  TextMapEditorComponent,
7952
8697
  PropertyEditorComponent,
7953
8698
  CommonModule,
7954
- FormsModule,
7955
- LuxModule
8699
+ FormsModule
7956
8700
  ],
7957
8701
  exports: [DiagramComponent, DiagramEditorComponent],
7958
8702
  providers: [DagaConfigurationService, CanvasProviderService]
7959
8703
  }]
7960
8704
  }] });
7961
8705
 
8706
+ const VERSION = '0.0.1';
8707
+ /**
8708
+ * The client that communicates with the daga-server over WebSockets.
8709
+ *
8710
+ * Use this to create and join rooms.
8711
+ * @public
8712
+ */
8713
+ class CollabClient {
8714
+ constructor(dagaServer, userId) {
8715
+ this.sendQueue = [];
8716
+ /**
8717
+ * Joined sessions by locator.
8718
+ */
8719
+ this.sessions = new Map();
8720
+ /**
8721
+ * Created but unacked sessions by refId.
8722
+ */
8723
+ this.pendingSessions = new Map();
8724
+ this.dagaServer = dagaServer;
8725
+ this.userId = userId;
8726
+ this.clientId = v4();
8727
+ this.ws = new WebSocket(this.dagaServer, 'esgrima');
8728
+ this.ws.addEventListener('open', this.onOpen.bind(this));
8729
+ this.ws.addEventListener('message', this.onMessage.bind(this));
8730
+ this.ws.addEventListener('close', this.onClose.bind(this));
8731
+ this.ws.addEventListener('error', this.onError.bind(this));
8732
+ this.send({
8733
+ type: 'HELO',
8734
+ clientId: this.clientId,
8735
+ userId: this.userId,
8736
+ version: VERSION,
8737
+ ts: now()
8738
+ });
8739
+ }
8740
+ onOpen() {
8741
+ // Send queued messages.
8742
+ for (const serialized of this.sendQueue) {
8743
+ this.ws.send(serialized);
8744
+ }
8745
+ this.sendQueue = [];
8746
+ }
8747
+ onMessage(e) {
8748
+ const serialized = e.data;
8749
+ const message = JSON.parse(serialized);
8750
+ this.receive(message);
8751
+ }
8752
+ onClose(e) {
8753
+ console.error('WebSocket closed', e);
8754
+ }
8755
+ onError(e) {
8756
+ console.error('WebSocket error', e);
8757
+ }
8758
+ /**
8759
+ * Internal send method. Use instead of calling ws.send directly.
8760
+ */
8761
+ send(message) {
8762
+ const serialized = JSON.stringify(message);
8763
+ if (this.ws.readyState === WebSocket.OPEN) {
8764
+ this.ws.send(serialized);
8765
+ }
8766
+ else {
8767
+ // Queue until after we connect.
8768
+ this.sendQueue.push(serialized);
8769
+ }
8770
+ }
8771
+ /**
8772
+ * Internal receive method. Use instead of handling ws receive events directly.
8773
+ */
8774
+ receive(message) {
8775
+ switch (message.type) {
8776
+ case 'EACK': {
8777
+ const session = this.sessions.get(message.locator);
8778
+ if (session === undefined) {
8779
+ throw new Error(`EACK for unknown locator: "${message.locator}"`);
8780
+ }
8781
+ const initialModel = message.initialModel;
8782
+ new DagaImporter().import(session.canvas.model, initialModel);
8783
+ this.startSyncing(session);
8784
+ session.joinRoomResolve?.();
8785
+ session.joinRoomResolve = undefined;
8786
+ break;
8787
+ }
8788
+ case 'ADD': {
8789
+ const session = this.sessions.get(message.locator);
8790
+ if (session === undefined) {
8791
+ throw new Error(`ADD for unknown locator: "${message.locator}"`);
8792
+ }
8793
+ const action = message.payload;
8794
+ session.canvas.collabEngine.receive(action);
8795
+ break;
8796
+ }
8797
+ case 'CACK': {
8798
+ const session = this.pendingSessions.get(message.refId);
8799
+ if (session === undefined) {
8800
+ throw new Error(`CACK for unknown refId: "${message.refId}"`);
8801
+ }
8802
+ session.locator = message.locator;
8803
+ this.pendingSessions.delete(message.refId);
8804
+ this.sessions.set(session.locator, session);
8805
+ session.createRoomResolve?.(session.locator);
8806
+ session.createRoomResolve = undefined;
8807
+ // Deliver queued AddMessages now that we have a locator.
8808
+ for (const message of session.addQueue) {
8809
+ this.send({ ...message, locator: session.locator });
8810
+ }
8811
+ session.addQueue = [];
8812
+ }
8813
+ }
8814
+ }
8815
+ /**
8816
+ * Creates and joins a room. The given canvas becomes collaborative, starting
8817
+ * with its current model.
8818
+ *
8819
+ * Returns a Promise that resolves with the new room's locator once joined.
8820
+ * @public
8821
+ */
8822
+ createAndJoinRoom(canvas) {
8823
+ if (canvas.collabEngine.isInRoom) {
8824
+ throw new Error('Canvas is already in a room');
8825
+ }
8826
+ canvas.collabEngine.isInRoom = true;
8827
+ const refId = v4();
8828
+ const initialModel = new DagaExporter().export(canvas.model);
8829
+ this.send({
8830
+ type: 'CREA',
8831
+ clientId: this.clientId,
8832
+ userId: this.userId,
8833
+ refId,
8834
+ initialModel,
8835
+ ts: now()
8836
+ });
8837
+ const session = {
8838
+ canvas,
8839
+ locator: null,
8840
+ addQueue: []
8841
+ };
8842
+ this.pendingSessions.set(refId, session);
8843
+ // We need to start queuing local AddMessages immediately, in case the user edits
8844
+ // the diagram while the CREA message is in-flight.
8845
+ this.startSyncing(session);
8846
+ return new Promise((resolve) => {
8847
+ session.createRoomResolve = resolve;
8848
+ });
8849
+ }
8850
+ /**
8851
+ * Joins the room with the given locator. The given canvas becomes collaborative,
8852
+ * **replacing** its current model with the room's model.
8853
+ *
8854
+ * Returns a Promise that resolves once the room has been joined and loaded into
8855
+ * the canvas. You should hide or lock the canvas until it resolves, since any
8856
+ * user inputs before then will be overwritten.
8857
+ * @public
8858
+ */
8859
+ joinRoom(canvas, locator) {
8860
+ if (canvas.collabEngine.isInRoom) {
8861
+ throw new Error('Canvas is already in a room');
8862
+ }
8863
+ canvas.collabEngine.isInRoom = true;
8864
+ this.send({
8865
+ type: 'ENRO',
8866
+ clientId: this.clientId,
8867
+ userId: this.userId,
8868
+ locator,
8869
+ ts: now()
8870
+ });
8871
+ const session = {
8872
+ canvas,
8873
+ locator,
8874
+ addQueue: []
8875
+ };
8876
+ this.sessions.set(locator, session);
8877
+ return new Promise((resolve) => {
8878
+ session.joinRoomResolve = resolve;
8879
+ });
8880
+ }
8881
+ startSyncing(session) {
8882
+ // Sync AddMessages from us to the server.
8883
+ session.canvas.collabEngine.onSend = (action) => {
8884
+ if (session.locator === null) {
8885
+ // Queue until we receive the locator in the CACK message.
8886
+ session.addQueue.push({
8887
+ type: 'ADD',
8888
+ clientId: this.clientId,
8889
+ userId: this.userId,
8890
+ payload: action,
8891
+ ts: now()
8892
+ });
8893
+ }
8894
+ else {
8895
+ this.send({
8896
+ type: 'ADD',
8897
+ clientId: this.clientId,
8898
+ userId: this.userId,
8899
+ locator: session.locator,
8900
+ payload: action,
8901
+ ts: now()
8902
+ });
8903
+ }
8904
+ };
8905
+ }
8906
+ }
8907
+ /**
8908
+ * Returns the current timestamp for a message's ts field.
8909
+ */
8910
+ function now() {
8911
+ return new Date().toISOString();
8912
+ }
8913
+
7962
8914
  /**
7963
8915
  * Generated bundle index. Do not edit.
7964
8916
  */
7965
8917
 
7966
- export { ACTION_QUEUE_SIZE, ActionQueue, AddConnectionAction, AddNodeAction, AdjacencyLayout, BreadthAdjacencyLayout, BreadthLayout, CanvasProviderService, ClosedShape, CollapseButtonComponent, Corner, DagaConfigurationService, DagaExporter, DagaImporter, DagaModule, DiagramActions, DiagramButtonsComponent, DiagramCanvas, DiagramComponent, DiagramConnection, DiagramConnectionSet, DiagramConnectionType, DiagramEditorComponent, DiagramElement, DiagramEntitySet, DiagramField, DiagramFieldSet, DiagramModel, DiagramNode, DiagramNodeSet, DiagramNodeType, DiagramPort, DiagramPortSet, DiagramSection, DiagramSectionSet, EditFieldAction, ErrorsComponent, ForceLayout, HorizontalAlign, HorizontalLayout, LineShape, LineStyle, MoveNodeAction, ObjectEditorComponent, PaletteComponent, PriorityLayout, Property, PropertyEditorComponent, PropertySet, RemoveAction, Side, StretchNodeAction, TextListEditorComponent, TextMapEditorComponent, Type, UpdateValuesAction, ValueSet, VerticalAlign, VerticalLayout, layouts };
8918
+ export { ACTION_QUEUE_SIZE, ActionQueue, AddConnectionAction, AddNodeAction, AdjacencyLayout, BreadthAdjacencyLayout, BreadthLayout, CanvasProviderService, ClosedShape, CollabClient, CollapseButtonComponent, Corner, DagaConfigurationService, DagaExporter, DagaImporter, DagaModule, DiagramActions, DiagramButtonsComponent, DiagramCanvas, DiagramComponent, DiagramConnection, DiagramConnectionSet, DiagramConnectionType, DiagramEditorComponent, DiagramElement, DiagramEntitySet, DiagramField, DiagramFieldSet, DiagramModel, DiagramNode, DiagramNodeSet, DiagramNodeType, DiagramPort, DiagramPortSet, DiagramSection, DiagramSectionSet, EditFieldAction, ErrorsComponent, ForceLayout, HorizontalAlign, HorizontalLayout, LineShape, LineStyle, MoveNodeAction, ObjectEditorComponent, PaletteComponent, PriorityLayout, Property, PropertyEditorComponent, PropertySet, RemoveAction, Side, StretchNodeAction, TextListEditorComponent, TextMapEditorComponent, Type, UpdateValuesAction, ValueSet, VerticalAlign, VerticalLayout, layouts };
7967
8919
  //# sourceMappingURL=metadev-daga.mjs.map