@metadev/daga 1.4.1 → 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 +18 -1
  2. package/assets/styles/_property-editor.scss +225 -157
  3. package/assets/styles/daga.scss +149 -156
  4. package/fesm2022/metadev-daga.mjs +1537 -592
  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 +3 -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 +7 -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,44 +4424,102 @@ 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
  /**
4123
- * Deletes everything in this diagram.
4124
- * @public
4431
+ * Deletes everything in this diagram.
4432
+ * @public
4433
+ */
4434
+ clear() {
4435
+ this.canvas?.cancelAllUserActions();
4436
+ this.id = undefined;
4437
+ this.name = '';
4438
+ this.description = undefined;
4439
+ this.createdAt = new Date();
4440
+ this.updatedAt = new Date();
4441
+ this.logicalClock = 0;
4442
+ this.nodes.clear();
4443
+ this.sections.clear();
4444
+ this.ports.clear();
4445
+ this.connections.clear();
4446
+ this.fields.clear();
4447
+ this.valueSet.resetValues();
4448
+ this.canvas?.updateModelInView();
4449
+ }
4450
+ }
4451
+
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.
4125
4497
  */
4126
- clear() {
4127
- this.canvas?.cancelAllUserActions();
4128
- this.id = undefined;
4129
- this.name = '';
4130
- this.description = undefined;
4131
- this.createdAt = new Date();
4132
- this.updatedAt = new Date();
4133
- this.nodes.clear();
4134
- this.sections.clear();
4135
- this.ports.clear();
4136
- this.connections.clear();
4137
- this.fields.clear();
4138
- this.valueSet.resetValues();
4139
- this.canvas?.updateModelInView();
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
+ }
4140
4520
  }
4141
4521
  }
4142
4522
 
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
-
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();
@@ -4504,7 +4870,10 @@ class DiagramCanvas {
4504
4870
  updateNodesInView(...ids) {
4505
4871
  let updateSelection = this.selectCanvasNodes()
4506
4872
  .selectAll('g.diagram-node')
4507
- .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);
4508
4877
  const exitSelection = updateSelection.exit();
4509
4878
  const enterSelection = updateSelection
4510
4879
  .enter()
@@ -4529,7 +4898,8 @@ class DiagramCanvas {
4529
4898
  .call(d3
4530
4899
  .drag()
4531
4900
  .on(DragEvents.Start, (event, d) => {
4532
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
4901
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
4902
+ !d.removed) {
4533
4903
  cursorStyle(CursorStyle.Grabbing);
4534
4904
  this.draggingFrom = [event.x, event.y];
4535
4905
  this.currentAction = new MoveNodeAction(this, d.id, d.coords, [0, 0]);
@@ -4539,7 +4909,8 @@ class DiagramCanvas {
4539
4909
  }
4540
4910
  })
4541
4911
  .on(DragEvents.Drag, (event, d) => {
4542
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
4912
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
4913
+ !d.removed) {
4543
4914
  const newNodeCoords = [
4544
4915
  event.x - d.width / 2,
4545
4916
  event.y - d.height / 2
@@ -4548,7 +4919,8 @@ class DiagramCanvas {
4548
4919
  }
4549
4920
  })
4550
4921
  .on(DragEvents.End, (event, d) => {
4551
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
4922
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
4923
+ !d.removed) {
4552
4924
  // prevent drag behavior if mouse hasn't moved
4553
4925
  if ((this.draggingFrom[0] !== event.x ||
4554
4926
  this.draggingFrom[1] !== event.y) &&
@@ -4561,10 +4933,8 @@ class DiagramCanvas {
4561
4933
  newNodeCoords = this.getClosestGridPoint(newNodeCoords);
4562
4934
  }
4563
4935
  this.currentAction.to = newNodeCoords;
4564
- this.currentAction.redo();
4936
+ this.currentAction.do();
4565
4937
  this.actionQueue.add(this.currentAction);
4566
- }
4567
- else {
4568
4938
  this.currentAction = undefined;
4569
4939
  }
4570
4940
  }
@@ -4628,13 +4998,15 @@ class DiagramCanvas {
4628
4998
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
4629
4999
  .on(Events.MouseOver, (event, d) => {
4630
5000
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4631
- d.type.resizableX) {
5001
+ d.type.resizableX &&
5002
+ !d.removed) {
4632
5003
  cursorStyle(CursorStyle.EWResize);
4633
5004
  }
4634
5005
  })
4635
5006
  .on(Events.MouseOut, (event, d) => {
4636
5007
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4637
- d.type.resizableX) {
5008
+ d.type.resizableX &&
5009
+ !d.removed) {
4638
5010
  cursorStyle();
4639
5011
  }
4640
5012
  })
@@ -4642,7 +5014,8 @@ class DiagramCanvas {
4642
5014
  .drag()
4643
5015
  .on(DragEvents.Start, (event, d) => {
4644
5016
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4645
- d.type.resizableX) {
5017
+ d.type.resizableX &&
5018
+ !d.removed) {
4646
5019
  cursorStyle(CursorStyle.EWResize);
4647
5020
  this.currentAction = new StretchNodeAction(this, d.id, Side.Left, d.width, d.width);
4648
5021
  }
@@ -4652,14 +5025,17 @@ class DiagramCanvas {
4652
5025
  })
4653
5026
  .on(DragEvents.Drag, (event, d) => {
4654
5027
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4655
- d.type.resizableX) {
5028
+ d.type.resizableX &&
5029
+ !d.removed) {
4656
5030
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4657
5031
  d.stretch(Side.Left, d.coords[0] - pointerCoords[0]);
4658
5032
  }
4659
5033
  })
4660
5034
  .on(DragEvents.End, (event, d) => {
4661
5035
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4662
- d.type.resizableX) {
5036
+ d.type.resizableX &&
5037
+ !d.removed &&
5038
+ this.currentAction instanceof StretchNodeAction) {
4663
5039
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4664
5040
  if (this.snapToGrid) {
4665
5041
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -4679,13 +5055,15 @@ class DiagramCanvas {
4679
5055
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
4680
5056
  .on(Events.MouseOver, (event, d) => {
4681
5057
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4682
- d.type.resizableY) {
5058
+ d.type.resizableY &&
5059
+ !d.removed) {
4683
5060
  cursorStyle(CursorStyle.NSResize);
4684
5061
  }
4685
5062
  })
4686
5063
  .on(Events.MouseOut, (event, d) => {
4687
5064
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4688
- d.type.resizableY) {
5065
+ d.type.resizableY &&
5066
+ !d.removed) {
4689
5067
  cursorStyle();
4690
5068
  }
4691
5069
  })
@@ -4693,7 +5071,8 @@ class DiagramCanvas {
4693
5071
  .drag()
4694
5072
  .on(DragEvents.Start, (event, d) => {
4695
5073
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4696
- d.type.resizableY) {
5074
+ d.type.resizableY &&
5075
+ !d.removed) {
4697
5076
  cursorStyle(CursorStyle.NSResize);
4698
5077
  this.currentAction = new StretchNodeAction(this, d.id, Side.Top, d.height, d.height);
4699
5078
  }
@@ -4703,14 +5082,17 @@ class DiagramCanvas {
4703
5082
  })
4704
5083
  .on(DragEvents.Drag, (event, d) => {
4705
5084
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4706
- d.type.resizableY) {
5085
+ d.type.resizableY &&
5086
+ !d.removed) {
4707
5087
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4708
5088
  d.stretch(Side.Top, d.coords[1] - pointerCoords[1]);
4709
5089
  }
4710
5090
  })
4711
5091
  .on(DragEvents.End, (event, d) => {
4712
5092
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4713
- d.type.resizableY) {
5093
+ d.type.resizableY &&
5094
+ !d.removed &&
5095
+ this.currentAction instanceof StretchNodeAction) {
4714
5096
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4715
5097
  if (this.snapToGrid) {
4716
5098
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -4730,13 +5112,15 @@ class DiagramCanvas {
4730
5112
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
4731
5113
  .on(Events.MouseOver, (event, d) => {
4732
5114
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4733
- d.type.resizableX) {
5115
+ d.type.resizableX &&
5116
+ !d.removed) {
4734
5117
  cursorStyle(CursorStyle.EWResize);
4735
5118
  }
4736
5119
  })
4737
5120
  .on(Events.MouseOut, (event, d) => {
4738
5121
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4739
- d.type.resizableX) {
5122
+ d.type.resizableX &&
5123
+ !d.removed) {
4740
5124
  cursorStyle();
4741
5125
  }
4742
5126
  })
@@ -4744,7 +5128,8 @@ class DiagramCanvas {
4744
5128
  .drag()
4745
5129
  .on(DragEvents.Start, (event, d) => {
4746
5130
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4747
- d.type.resizableX) {
5131
+ d.type.resizableX &&
5132
+ !d.removed) {
4748
5133
  cursorStyle(CursorStyle.EWResize);
4749
5134
  this.currentAction = new StretchNodeAction(this, d.id, Side.Right, d.width, d.width);
4750
5135
  }
@@ -4754,14 +5139,17 @@ class DiagramCanvas {
4754
5139
  })
4755
5140
  .on(DragEvents.Drag, (event, d) => {
4756
5141
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4757
- d.type.resizableX) {
5142
+ d.type.resizableX &&
5143
+ !d.removed) {
4758
5144
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4759
5145
  d.stretch(Side.Right, pointerCoords[0] - (d.coords[0] + d.width));
4760
5146
  }
4761
5147
  })
4762
5148
  .on(DragEvents.End, (event, d) => {
4763
5149
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4764
- d.type.resizableX) {
5150
+ d.type.resizableX &&
5151
+ !d.removed &&
5152
+ this.currentAction instanceof StretchNodeAction) {
4765
5153
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4766
5154
  if (this.snapToGrid) {
4767
5155
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -4781,13 +5169,15 @@ class DiagramCanvas {
4781
5169
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
4782
5170
  .on(Events.MouseOver, (event, d) => {
4783
5171
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4784
- d.type.resizableY) {
5172
+ d.type.resizableY &&
5173
+ !d.removed) {
4785
5174
  cursorStyle(CursorStyle.NSResize);
4786
5175
  }
4787
5176
  })
4788
5177
  .on(Events.MouseOut, (event, d) => {
4789
5178
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4790
- d.type.resizableY) {
5179
+ d.type.resizableY &&
5180
+ !d.removed) {
4791
5181
  cursorStyle();
4792
5182
  }
4793
5183
  })
@@ -4795,7 +5185,8 @@ class DiagramCanvas {
4795
5185
  .drag()
4796
5186
  .on(DragEvents.Start, (event, d) => {
4797
5187
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4798
- d.type.resizableY) {
5188
+ d.type.resizableY &&
5189
+ !d.removed) {
4799
5190
  cursorStyle(CursorStyle.NSResize);
4800
5191
  this.currentAction = new StretchNodeAction(this, d.id, Side.Bottom, d.height, d.height);
4801
5192
  }
@@ -4805,14 +5196,17 @@ class DiagramCanvas {
4805
5196
  })
4806
5197
  .on(DragEvents.Drag, (event, d) => {
4807
5198
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4808
- d.type.resizableY) {
5199
+ d.type.resizableY &&
5200
+ !d.removed) {
4809
5201
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4810
5202
  d.stretch(Side.Bottom, pointerCoords[1] - (d.coords[1] + d.height));
4811
5203
  }
4812
5204
  })
4813
5205
  .on(DragEvents.End, (event, d) => {
4814
5206
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
4815
- d.type.resizableY) {
5207
+ d.type.resizableY &&
5208
+ !d.removed &&
5209
+ this.currentAction instanceof StretchNodeAction) {
4816
5210
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
4817
5211
  if (this.snapToGrid) {
4818
5212
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -4824,7 +5218,9 @@ class DiagramCanvas {
4824
5218
  }
4825
5219
  cursorStyle();
4826
5220
  }));
4827
- 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));
4828
5224
  mergeSelection
4829
5225
  .filter('.shaped-look')
4830
5226
  .select('path')
@@ -4988,7 +5384,10 @@ class DiagramCanvas {
4988
5384
  updateSectionsInView(...ids) {
4989
5385
  let updateSelection = this.selectCanvasSections()
4990
5386
  .selectAll('g.diagram-section')
4991
- .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);
4992
5391
  const exitSelection = updateSelection.exit();
4993
5392
  const enterSelection = updateSelection
4994
5393
  .enter()
@@ -5013,7 +5412,8 @@ class DiagramCanvas {
5013
5412
  .call(d3
5014
5413
  .drag()
5015
5414
  .on(DragEvents.Start, (event, d) => {
5016
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
5415
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
5416
+ !d.removed) {
5017
5417
  const node = d.node;
5018
5418
  cursorStyle(CursorStyle.Grabbing);
5019
5419
  this.draggingFrom = [event.x, event.y];
@@ -5024,7 +5424,8 @@ class DiagramCanvas {
5024
5424
  }
5025
5425
  })
5026
5426
  .on(DragEvents.Drag, (event, d) => {
5027
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
5427
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
5428
+ !d.removed) {
5028
5429
  const node = d.node;
5029
5430
  const newNodeCoords = [
5030
5431
  event.x - node.width / 2,
@@ -5034,7 +5435,8 @@ class DiagramCanvas {
5034
5435
  }
5035
5436
  })
5036
5437
  .on(DragEvents.End, (event, d) => {
5037
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
5438
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
5439
+ !d.removed) {
5038
5440
  const node = d.node;
5039
5441
  // prevent drag behavior if mouse hasn't moved
5040
5442
  if ((this.draggingFrom[0] !== event.x ||
@@ -5048,10 +5450,8 @@ class DiagramCanvas {
5048
5450
  newNodeCoords = this.getClosestGridPoint(newNodeCoords);
5049
5451
  }
5050
5452
  this.currentAction.to = newNodeCoords;
5051
- this.currentAction.redo();
5453
+ this.currentAction.do();
5052
5454
  this.actionQueue.add(this.currentAction);
5053
- }
5054
- else {
5055
5455
  this.currentAction = undefined;
5056
5456
  }
5057
5457
  }
@@ -5115,13 +5515,15 @@ class DiagramCanvas {
5115
5515
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
5116
5516
  .on(Events.MouseOver, (event, d) => {
5117
5517
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5118
- d.node?.type?.resizableX) {
5518
+ d.node?.type?.resizableX &&
5519
+ !d.removed) {
5119
5520
  cursorStyle(CursorStyle.EWResize);
5120
5521
  }
5121
5522
  })
5122
5523
  .on(Events.MouseOut, (event, d) => {
5123
5524
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5124
- d.node?.type?.resizableX) {
5525
+ d.node?.type?.resizableX &&
5526
+ !d.removed) {
5125
5527
  cursorStyle();
5126
5528
  }
5127
5529
  })
@@ -5129,7 +5531,8 @@ class DiagramCanvas {
5129
5531
  .drag()
5130
5532
  .on(DragEvents.Start, (event, d) => {
5131
5533
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5132
- d.node?.type?.resizableX) {
5534
+ d.node?.type?.resizableX &&
5535
+ !d.removed) {
5133
5536
  cursorStyle(CursorStyle.EWResize);
5134
5537
  this.currentAction = new StretchSectionAction(this, d.id, Side.Left, d.width, d.width);
5135
5538
  }
@@ -5139,14 +5542,17 @@ class DiagramCanvas {
5139
5542
  })
5140
5543
  .on(DragEvents.Drag, (event, d) => {
5141
5544
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5142
- d.node?.type?.resizableX) {
5545
+ d.node?.type?.resizableX &&
5546
+ !d.removed) {
5143
5547
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5144
5548
  d.node.stretchSections(Side.Left, d.coords[0] - pointerCoords[0], d.indexXInNode, d.indexYInNode);
5145
5549
  }
5146
5550
  })
5147
5551
  .on(DragEvents.End, (event, d) => {
5148
5552
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5149
- d.node?.type?.resizableX) {
5553
+ d.node?.type?.resizableX &&
5554
+ !d.removed &&
5555
+ this.currentAction instanceof StretchSectionAction) {
5150
5556
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5151
5557
  if (this.snapToGrid) {
5152
5558
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -5166,13 +5572,15 @@ class DiagramCanvas {
5166
5572
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
5167
5573
  .on(Events.MouseOver, (event, d) => {
5168
5574
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5169
- d.node?.type?.resizableY) {
5575
+ d.node?.type?.resizableY &&
5576
+ !d.removed) {
5170
5577
  cursorStyle(CursorStyle.NSResize);
5171
5578
  }
5172
5579
  })
5173
5580
  .on(Events.MouseOut, (event, d) => {
5174
5581
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5175
- d.node?.type?.resizableY) {
5582
+ d.node?.type?.resizableY &&
5583
+ !d.removed) {
5176
5584
  cursorStyle();
5177
5585
  }
5178
5586
  })
@@ -5180,7 +5588,8 @@ class DiagramCanvas {
5180
5588
  .drag()
5181
5589
  .on(DragEvents.Start, (event, d) => {
5182
5590
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5183
- d.node?.type?.resizableY) {
5591
+ d.node?.type?.resizableY &&
5592
+ !d.removed) {
5184
5593
  cursorStyle(CursorStyle.NSResize);
5185
5594
  this.currentAction = new StretchSectionAction(this, d.id, Side.Top, d.height, d.height);
5186
5595
  }
@@ -5190,14 +5599,17 @@ class DiagramCanvas {
5190
5599
  })
5191
5600
  .on(DragEvents.Drag, (event, d) => {
5192
5601
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5193
- d.node?.type?.resizableY) {
5602
+ d.node?.type?.resizableY &&
5603
+ !d.removed) {
5194
5604
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5195
5605
  d.node.stretchSections(Side.Top, d.coords[1] - pointerCoords[1], d.indexXInNode, d.indexYInNode);
5196
5606
  }
5197
5607
  })
5198
5608
  .on(DragEvents.End, (event, d) => {
5199
5609
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5200
- d.node?.type?.resizableY) {
5610
+ d.node?.type?.resizableY &&
5611
+ !d.removed &&
5612
+ this.currentAction instanceof StretchSectionAction) {
5201
5613
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5202
5614
  if (this.snapToGrid) {
5203
5615
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -5217,13 +5629,15 @@ class DiagramCanvas {
5217
5629
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
5218
5630
  .on(Events.MouseOver, (event, d) => {
5219
5631
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5220
- d.node?.type?.resizableX) {
5632
+ d.node?.type?.resizableX &&
5633
+ !d.removed) {
5221
5634
  cursorStyle(CursorStyle.EWResize);
5222
5635
  }
5223
5636
  })
5224
5637
  .on(Events.MouseOut, (event, d) => {
5225
5638
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5226
- d.node?.type?.resizableX) {
5639
+ d.node?.type?.resizableX &&
5640
+ !d.removed) {
5227
5641
  cursorStyle();
5228
5642
  }
5229
5643
  })
@@ -5231,7 +5645,8 @@ class DiagramCanvas {
5231
5645
  .drag()
5232
5646
  .on(DragEvents.Start, (event, d) => {
5233
5647
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5234
- d.node?.type?.resizableX) {
5648
+ d.node?.type?.resizableX &&
5649
+ !d.removed) {
5235
5650
  cursorStyle(CursorStyle.EWResize);
5236
5651
  this.currentAction = new StretchSectionAction(this, d.id, Side.Right, d.width, d.width);
5237
5652
  }
@@ -5241,14 +5656,17 @@ class DiagramCanvas {
5241
5656
  })
5242
5657
  .on(DragEvents.Drag, (event, d) => {
5243
5658
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5244
- d.node?.type?.resizableX) {
5659
+ d.node?.type?.resizableX &&
5660
+ !d.removed) {
5245
5661
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5246
5662
  d.node.stretchSections(Side.Right, pointerCoords[0] - (d.coords[0] + d.width), d.indexXInNode, d.indexYInNode);
5247
5663
  }
5248
5664
  })
5249
5665
  .on(DragEvents.End, (event, d) => {
5250
5666
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5251
- d.node?.type?.resizableX) {
5667
+ d.node?.type?.resizableX &&
5668
+ !d.removed &&
5669
+ this.currentAction instanceof StretchSectionAction) {
5252
5670
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5253
5671
  if (this.snapToGrid) {
5254
5672
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -5268,13 +5686,15 @@ class DiagramCanvas {
5268
5686
  .attr('stroke-width', `${RESIZER_THICKNESS}px`)
5269
5687
  .on(Events.MouseOver, (event, d) => {
5270
5688
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5271
- d.node?.type?.resizableY) {
5689
+ d.node?.type?.resizableY &&
5690
+ !d.removed) {
5272
5691
  cursorStyle(CursorStyle.NSResize);
5273
5692
  }
5274
5693
  })
5275
5694
  .on(Events.MouseOut, (event, d) => {
5276
5695
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5277
- d.node?.type?.resizableY) {
5696
+ d.node?.type?.resizableY &&
5697
+ !d.removed) {
5278
5698
  cursorStyle();
5279
5699
  }
5280
5700
  })
@@ -5282,7 +5702,8 @@ class DiagramCanvas {
5282
5702
  .drag()
5283
5703
  .on(DragEvents.Start, (event, d) => {
5284
5704
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5285
- d.node?.type?.resizableY) {
5705
+ d.node?.type?.resizableY &&
5706
+ !d.removed) {
5286
5707
  cursorStyle(CursorStyle.NSResize);
5287
5708
  this.currentAction = new StretchSectionAction(this, d.id, Side.Bottom, d.height, d.height);
5288
5709
  }
@@ -5292,14 +5713,17 @@ class DiagramCanvas {
5292
5713
  })
5293
5714
  .on(DragEvents.Drag, (event, d) => {
5294
5715
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5295
- d.node?.type?.resizableY) {
5716
+ d.node?.type?.resizableY &&
5717
+ !d.removed) {
5296
5718
  const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5297
5719
  d.node.stretchSections(Side.Bottom, pointerCoords[1] - (d.coords[1] + d.height), d.indexXInNode, d.indexYInNode);
5298
5720
  }
5299
5721
  })
5300
5722
  .on(DragEvents.End, (event, d) => {
5301
5723
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5302
- d.node?.type?.resizableY) {
5724
+ d.node?.type?.resizableY &&
5725
+ !d.removed &&
5726
+ this.currentAction instanceof StretchSectionAction) {
5303
5727
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5304
5728
  if (this.snapToGrid) {
5305
5729
  pointerCoords = this.getClosestGridPoint(pointerCoords);
@@ -5311,7 +5735,9 @@ class DiagramCanvas {
5311
5735
  }
5312
5736
  cursorStyle();
5313
5737
  }));
5314
- 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));
5315
5741
  mergeSelection
5316
5742
  .filter('.shaped-look')
5317
5743
  .select('path')
@@ -5490,7 +5916,10 @@ class DiagramCanvas {
5490
5916
  updatePortsInView(...ids) {
5491
5917
  let updateSelection = this.selectCanvasPorts()
5492
5918
  .selectAll('g.diagram-port')
5493
- .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);
5494
5923
  const exitSelection = updateSelection.exit();
5495
5924
  const enterSelection = updateSelection
5496
5925
  .enter()
@@ -5529,7 +5958,8 @@ class DiagramCanvas {
5529
5958
  .call(d3
5530
5959
  .drag()
5531
5960
  .on(DragEvents.Start, (event, d) => {
5532
- if (this.canUserPerformAction(DiagramActions.AddConnection)) {
5961
+ if (this.canUserPerformAction(DiagramActions.AddConnection) &&
5962
+ !d.removed) {
5533
5963
  cursorStyle(CursorStyle.Grabbing);
5534
5964
  this.startConnection(d);
5535
5965
  // should be true after having called this.startConnection()
@@ -5540,9 +5970,13 @@ class DiagramCanvas {
5540
5970
  .attr('fill', 'none');
5541
5971
  }
5542
5972
  }
5973
+ else {
5974
+ cursorStyle(CursorStyle.NotAllowed);
5975
+ }
5543
5976
  })
5544
- .on(DragEvents.Drag, (event) => {
5545
- if (this.canUserPerformAction(DiagramActions.AddConnection)) {
5977
+ .on(DragEvents.Drag, (event, d) => {
5978
+ if (this.canUserPerformAction(DiagramActions.AddConnection) &&
5979
+ !d.removed) {
5546
5980
  if (this.unfinishedConnection !== undefined) {
5547
5981
  const endCoords = [event.x, event.y];
5548
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));
@@ -5567,9 +6001,9 @@ class DiagramCanvas {
5567
6001
  }
5568
6002
  }
5569
6003
  })
5570
- .on(DragEvents.End, (event) => {
5571
- if (this.canUserPerformAction(DiagramActions.AddConnection)) {
5572
- cursorStyle();
6004
+ .on(DragEvents.End, (event, d) => {
6005
+ if (this.canUserPerformAction(DiagramActions.AddConnection) &&
6006
+ !d.removed) {
5573
6007
  this.unfinishedConnectionTracer?.remove();
5574
6008
  if (this.mainUserHighlight instanceof DiagramPort) {
5575
6009
  this.finishConnection(this.mainUserHighlight);
@@ -5605,10 +6039,12 @@ class DiagramCanvas {
5605
6039
  this.dropConnection();
5606
6040
  }
5607
6041
  }
6042
+ cursorStyle();
5608
6043
  }));
5609
6044
  enterSelection.append('circle');
5610
6045
  mergeSelection
5611
6046
  .attr('transform', (d) => `translate(${d.coords[0]},${d.coords[1]})`)
6047
+ .attr('opacity', (d) => (d.removed ? 0.5 : 1))
5612
6048
  .select('circle')
5613
6049
  .attr('cx', 0)
5614
6050
  .attr('cy', 0)
@@ -5619,7 +6055,10 @@ class DiagramCanvas {
5619
6055
  .attr('opacity', (d) => (d.highlighted || d.selected ? 0.5 : 0));
5620
6056
  }
5621
6057
  updateConnectionsInView(...ids) {
5622
- 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));
5623
6062
  if (this.unfinishedConnection) {
5624
6063
  connectionList.push(this.unfinishedConnection);
5625
6064
  }
@@ -5682,6 +6121,7 @@ class DiagramCanvas {
5682
6121
  .append('text')
5683
6122
  .style('user-select', 'none');
5684
6123
  mergeSelection
6124
+ .attr('opacity', (d) => (d.removed ? 0.5 : 1))
5685
6125
  .select('path.diagram-connection-path')
5686
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))
5687
6127
  .attr('marker-start', (d) => `url(#${d.id}-start-marker)`)
@@ -5707,7 +6147,10 @@ class DiagramCanvas {
5707
6147
  updateFieldsInView(...ids) {
5708
6148
  let updateSelection = this.selectCanvasFields()
5709
6149
  .selectAll('foreignObject.diagram-field')
5710
- .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);
5711
6154
  const exitSelection = updateSelection.exit();
5712
6155
  const enterSelection = updateSelection
5713
6156
  .enter()
@@ -5733,7 +6176,9 @@ class DiagramCanvas {
5733
6176
  }
5734
6177
  })
5735
6178
  .on(Events.DoubleClick, (event, d) => {
5736
- if (this.canUserPerformAction(DiagramActions.EditField) && d.editable) {
6179
+ if (this.canUserPerformAction(DiagramActions.EditField) &&
6180
+ d.editable &&
6181
+ !d.removed) {
5737
6182
  this.currentAction = new EditFieldAction(this, d.id, d.text, '');
5738
6183
  this.createInputField(d.text, d.coords, d.width, d.height, d.fontSize, d.fontFamily || DIAGRAM_FIELD_DEFAULTS.fontFamily, (text) => {
5739
6184
  d.text = text;
@@ -5750,7 +6195,8 @@ class DiagramCanvas {
5750
6195
  .call(d3
5751
6196
  .drag()
5752
6197
  .on(DragEvents.Start, (event, d) => {
5753
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
6198
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
6199
+ !d.removed) {
5754
6200
  if (d.rootElement instanceof DiagramNode ||
5755
6201
  d.rootElement instanceof DiagramSection) {
5756
6202
  const node = d.rootElement instanceof DiagramNode
@@ -5766,7 +6212,8 @@ class DiagramCanvas {
5766
6212
  }
5767
6213
  })
5768
6214
  .on(DragEvents.Drag, (event, d) => {
5769
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
6215
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
6216
+ !d.removed) {
5770
6217
  if (d.rootElement instanceof DiagramNode ||
5771
6218
  d.rootElement instanceof DiagramSection) {
5772
6219
  const node = d.rootElement instanceof DiagramNode
@@ -5781,7 +6228,8 @@ class DiagramCanvas {
5781
6228
  }
5782
6229
  })
5783
6230
  .on(DragEvents.End, (event, d) => {
5784
- if (this.canUserPerformAction(DiagramActions.MoveNode)) {
6231
+ if (this.canUserPerformAction(DiagramActions.MoveNode) &&
6232
+ !d.removed) {
5785
6233
  if (d.rootElement instanceof DiagramNode ||
5786
6234
  d.rootElement instanceof DiagramSection) {
5787
6235
  const node = d.rootElement instanceof DiagramNode
@@ -5799,10 +6247,8 @@ class DiagramCanvas {
5799
6247
  newNodeCoords = this.getClosestGridPoint(newNodeCoords);
5800
6248
  }
5801
6249
  this.currentAction.to = newNodeCoords;
5802
- this.currentAction.redo();
6250
+ this.currentAction.do();
5803
6251
  this.actionQueue.add(this.currentAction);
5804
- }
5805
- else {
5806
6252
  this.currentAction = undefined;
5807
6253
  }
5808
6254
  }
@@ -5828,6 +6274,7 @@ class DiagramCanvas {
5828
6274
  .attr('width', (d) => `${d.width}px`)
5829
6275
  .attr('height', (d) => `${d.height}px`)
5830
6276
  .attr('transform', (d) => `translate(${d.coords[0]},${d.coords[1]})`)
6277
+ .attr('opacity', (d) => (d.removed ? 0.5 : 1))
5831
6278
  .select('div')
5832
6279
  .style('justify-content', (d) => d.horizontalAlign === HorizontalAlign.Center
5833
6280
  ? 'center'
@@ -6196,7 +6643,7 @@ class DiagramCanvas {
6196
6643
  const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, this.unfinishedConnection.start?.id, port.id);
6197
6644
  // clean up the previous unfinished connection
6198
6645
  this.dropConnection();
6199
- addConnectionAction.redo();
6646
+ addConnectionAction.do();
6200
6647
  this.actionQueue.add(addConnectionAction);
6201
6648
  }
6202
6649
  else if (this.unfinishedConnection.type.canFinishOnType(this.unfinishedConnection?.start?.getNode()?.type?.id || '') &&
@@ -6204,7 +6651,7 @@ class DiagramCanvas {
6204
6651
  const addConnectionAction = new AddConnectionAction(this, this.unfinishedConnection.type, port.id, this.unfinishedConnection.start?.id);
6205
6652
  // clean up the previous unfinished connection
6206
6653
  this.dropConnection();
6207
- addConnectionAction.redo();
6654
+ addConnectionAction.do();
6208
6655
  this.actionQueue.add(addConnectionAction);
6209
6656
  }
6210
6657
  else {
@@ -6228,7 +6675,7 @@ class DiagramCanvas {
6228
6675
  : port.id);
6229
6676
  // clean up the previous unfinished connection
6230
6677
  this.dropConnection();
6231
- addConnectionAction.redo();
6678
+ addConnectionAction.do();
6232
6679
  this.actionQueue.add(addConnectionAction);
6233
6680
  }
6234
6681
  else {
@@ -6317,7 +6764,10 @@ class DiagramCanvas {
6317
6764
  // check if there have been changes in the previously selected ValueSet,
6318
6765
  // and create an UpdateValuesAction if there have
6319
6766
  if (!equals(this.propertyEditorValues, this.propertyEditorSelection?.valueSet.getValues())) {
6320
- 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);
6321
6771
  this.actionQueue.add(currentAction);
6322
6772
  this.propertyEditorValues = undefined;
6323
6773
  }
@@ -6681,7 +7131,7 @@ class DiagramButtonsComponent {
6681
7131
  this.canvas.actionQueue.redo();
6682
7132
  }
6683
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 }); }
6684
- 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"] }] }); }
6685
7135
  }
6686
7136
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DiagramButtonsComponent, decorators: [{
6687
7137
  type: Component,
@@ -6749,7 +7199,7 @@ class ErrorsComponent {
6749
7199
  // TODO: IF ERROR IS IN AN ELEMENT BUT NOT IN A SPECIFIC PROPERTY, WE COULD HIGHLIGHT THE ELEMENT
6750
7200
  }
6751
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 }); }
6752
- 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"] }] }); }
6753
7203
  }
6754
7204
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: ErrorsComponent, decorators: [{
6755
7205
  type: Component,
@@ -6913,7 +7363,7 @@ class PaletteComponent {
6913
7363
  .style('z-index', 1);
6914
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
6915
7365
  if (type.isUnique &&
6916
- this.canvas.model.nodes.find(type.id) !== undefined) {
7366
+ this.canvas.model.nodes.find((n) => !n.removed && n.type.id === type.id) !== undefined) {
6917
7367
  cursorStyle(CursorStyle.NotAllowed);
6918
7368
  }
6919
7369
  }
@@ -6929,7 +7379,7 @@ class PaletteComponent {
6929
7379
  .style('z-index', 'auto');
6930
7380
  // try to place node
6931
7381
  if (type.isUnique &&
6932
- this.canvas.model.nodes.find(type.id) !== undefined) {
7382
+ this.canvas.model.nodes.find((n) => !n.removed && n.type.id === type.id) !== undefined) {
6933
7383
  // can't place, it's unique and that node is already in the model
6934
7384
  return;
6935
7385
  }
@@ -6961,7 +7411,7 @@ class PaletteComponent {
6961
7411
  newNodeCoords = this.canvas.getClosestGridPoint(newNodeCoords);
6962
7412
  }
6963
7413
  const addNodeAction = new AddNodeAction(this.canvas, type, newNodeCoords, templateConfig.label, templateConfig.values);
6964
- addNodeAction.redo();
7414
+ addNodeAction.do();
6965
7415
  this.canvas.actionQueue.add(addNodeAction);
6966
7416
  // reset cursor
6967
7417
  cursorStyle();
@@ -7138,7 +7588,7 @@ class PaletteComponent {
7138
7588
  }
7139
7589
  }
7140
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 }); }
7141
- 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"] }] }); }
7142
7592
  }
7143
7593
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: PaletteComponent, decorators: [{
7144
7594
  type: Component,
@@ -7160,20 +7610,291 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
7160
7610
  type: Input
7161
7611
  }] } });
7162
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
+
7163
7884
  /**
7164
7885
  * Editor of a property of text list type within a property editor.
7165
- * @see TextList
7886
+ * @see Type.TextList
7166
7887
  * @see PropertyEditor
7167
7888
  * @see ValueSet
7168
7889
  * @private
7169
7890
  */
7170
7891
  class TextListEditorComponent {
7171
- set value(val) {
7172
- if (val === this._value) {
7892
+ set value(value) {
7893
+ if (value === this._value) {
7173
7894
  return;
7174
7895
  }
7175
- const initialAndEmpty = val.length === 0 && this._value.length === 0;
7176
- this._value = val;
7896
+ const initialAndEmpty = value.length === 0 && this._value.length === 0;
7897
+ this._value = value;
7177
7898
  if (initialAndEmpty) {
7178
7899
  this.valueChange.emit(this._value);
7179
7900
  }
@@ -7222,11 +7943,11 @@ class TextListEditorComponent {
7222
7943
  }
7223
7944
  }
7224
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 }); }
7225
- 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"] }] }); }
7226
7947
  }
7227
7948
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TextListEditorComponent, decorators: [{
7228
7949
  type: Component,
7229
- 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" }]
7230
7951
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { value: [{
7231
7952
  type: Input
7232
7953
  }], valueInput: [{
@@ -7239,18 +7960,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
7239
7960
 
7240
7961
  /**
7241
7962
  * Editor of a property of text map type within a property editor.
7242
- * @see TextMap
7963
+ * @see Type.TextMap
7243
7964
  * @see PropertyEditor
7244
7965
  * @see ValueSet
7245
7966
  * @private
7246
7967
  */
7247
7968
  class TextMapEditorComponent {
7248
- set value(val) {
7249
- if (val === this._value) {
7969
+ set value(value) {
7970
+ if (value === this._value) {
7250
7971
  return;
7251
7972
  }
7252
- const initialAndEmpty = Object.keys(val).length === 0 && Object.keys(this._value).length === 0;
7253
- this._value = val;
7973
+ const initialAndEmpty = Object.keys(value).length === 0 && Object.keys(this._value).length === 0;
7974
+ this._value = value;
7254
7975
  if (initialAndEmpty) {
7255
7976
  this.valueChange.emit(this._value);
7256
7977
  }
@@ -7305,11 +8026,11 @@ class TextMapEditorComponent {
7305
8026
  }
7306
8027
  }
7307
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 }); }
7308
- 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"] }] }); }
7309
8030
  }
7310
8031
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TextMapEditorComponent, decorators: [{
7311
8032
  type: Component,
7312
- 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" }]
7313
8034
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { value: [{
7314
8035
  type: Input
7315
8036
  }], keyInput: [{
@@ -7333,7 +8054,8 @@ class ObjectEditorComponent {
7333
8054
  return this.canvasProvider.getCanvas();
7334
8055
  }
7335
8056
  get userCanEdit() {
7336
- return this.canvas.canUserPerformAction(DiagramActions.UpdateValues);
8057
+ return (this.canvas.canUserPerformAction(DiagramActions.UpdateValues) &&
8058
+ this.valueSet?.rootElement?.['removed'] !== true);
7337
8059
  }
7338
8060
  get valueSet() {
7339
8061
  return this._valueSet;
@@ -7350,10 +8072,6 @@ class ObjectEditorComponent {
7350
8072
  this.depth = 0;
7351
8073
  // Attributes for the template
7352
8074
  this.Type = Type;
7353
- this.booleanRadioItems = [
7354
- { value: false, label: 'No' },
7355
- { value: true, label: 'Yes' }
7356
- ];
7357
8075
  }
7358
8076
  displayProperty(property) {
7359
8077
  if (this.valueSet === undefined) {
@@ -7405,24 +8123,35 @@ class ObjectEditorComponent {
7405
8123
  }
7406
8124
  }
7407
8125
  }
7408
- convertDate(date) {
8126
+ dateToLocalDatetimeString(date) {
7409
8127
  if (typeof date === 'string') {
7410
8128
  return date;
7411
8129
  }
7412
- 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);
7413
8141
  }
7414
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 }); }
7415
- 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"] }] }); }
7416
8144
  }
7417
8145
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: ObjectEditorComponent, decorators: [{
7418
8146
  type: Component,
7419
8147
  args: [{ standalone: true, selector: 'daga-object-editor', imports: [
8148
+ AutocompleteComponent,
7420
8149
  CommonModule,
7421
- LuxModule,
7422
8150
  FormsModule,
8151
+ OptionListEditorComponent,
7423
8152
  TextListEditorComponent,
7424
8153
  TextMapEditorComponent
7425
- ], 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" }]
7426
8155
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: CanvasProviderService }], propDecorators: { valueSet: [{
7427
8156
  type: Input
7428
8157
  }], depth: [{
@@ -7481,11 +8210,11 @@ class PropertyEditorComponent {
7481
8210
  }
7482
8211
  }
7483
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 }); }
7484
- 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"] }] }); }
7485
8214
  }
7486
8215
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: PropertyEditorComponent, decorators: [{
7487
8216
  type: Component,
7488
- 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" }]
7489
8218
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { panel: [{
7490
8219
  type: ViewChild,
7491
8220
  args: ['panel']
@@ -7551,7 +8280,7 @@ class DiagramEditorComponent {
7551
8280
  this.canvasProvider.initCanvasView(this.appendTo.nativeElement);
7552
8281
  }
7553
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 }); }
7554
- 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" }] }); }
7555
8284
  }
7556
8285
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DiagramEditorComponent, decorators: [{
7557
8286
  type: Component,
@@ -7592,7 +8321,8 @@ class DagaExporter {
7592
8321
  updatedAt: model.updatedAt,
7593
8322
  nodes: [],
7594
8323
  connections: [],
7595
- data: model.valueSet.getValues()
8324
+ data: model.valueSet.getValues(),
8325
+ logicalClock: model.logicalClock
7596
8326
  };
7597
8327
  if (model.id) {
7598
8328
  result.id = model.id;
@@ -7600,7 +8330,7 @@ class DagaExporter {
7600
8330
  if (model.description) {
7601
8331
  result.description = model.description;
7602
8332
  }
7603
- for (const node of model.nodes) {
8333
+ for (const node of model.nodes.filter((e) => !e.removed)) {
7604
8334
  const sections = [];
7605
8335
  for (const section of node.sections) {
7606
8336
  const ports = [];
@@ -7632,7 +8362,7 @@ class DagaExporter {
7632
8362
  label: port.label?.text || ''
7633
8363
  });
7634
8364
  }
7635
- result.nodes.push({
8365
+ const newNode = {
7636
8366
  id: node.id,
7637
8367
  type: node.type.id,
7638
8368
  sections,
@@ -7642,9 +8372,13 @@ class DagaExporter {
7642
8372
  width: node.width,
7643
8373
  height: node.height,
7644
8374
  data: node.valueSet.getValues()
7645
- });
8375
+ };
8376
+ if (node.coordsTimestamp) {
8377
+ newNode.coordsTimestamp = node.coordsTimestamp;
8378
+ }
8379
+ result.nodes.push(newNode);
7646
8380
  }
7647
- for (const connection of model.connections) {
8381
+ for (const connection of model.connections.filter((e) => !e.removed)) {
7648
8382
  result.connections.push({
7649
8383
  id: connection.id,
7650
8384
  type: connection.type.id,
@@ -7677,6 +8411,9 @@ class DagaImporter {
7677
8411
  model.type = data.type;
7678
8412
  model.createdAt = new Date(data.createdAt);
7679
8413
  model.updatedAt = new Date(data.updatedAt);
8414
+ if (data.logicalClock) {
8415
+ model.logicalClock = data.logicalClock;
8416
+ }
7680
8417
  for (const node of data.nodes || []) {
7681
8418
  const newNodeType = model.nodes.types.get(node.type);
7682
8419
  if (newNodeType) {
@@ -7684,6 +8421,9 @@ class DagaImporter {
7684
8421
  model.nodes.add(newNode);
7685
8422
  newNode.width = node.width;
7686
8423
  newNode.height = node.height;
8424
+ if (node.coordsTimestamp) {
8425
+ newNode.coordsTimestamp = node.coordsTimestamp;
8426
+ }
7687
8427
  if (node.label) {
7688
8428
  // add node label
7689
8429
  if (newNodeType.label) {
@@ -7927,8 +8667,7 @@ class DagaModule {
7927
8667
  TextMapEditorComponent,
7928
8668
  PropertyEditorComponent,
7929
8669
  CommonModule,
7930
- FormsModule,
7931
- LuxModule], exports: [DiagramComponent, DiagramEditorComponent] }); }
8670
+ FormsModule], exports: [DiagramComponent, DiagramEditorComponent] }); }
7932
8671
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DagaModule, providers: [DagaConfigurationService, CanvasProviderService], imports: [CollapseButtonComponent,
7933
8672
  DiagramButtonsComponent,
7934
8673
  DiagramEditorComponent,
@@ -7939,8 +8678,7 @@ class DagaModule {
7939
8678
  TextMapEditorComponent,
7940
8679
  PropertyEditorComponent,
7941
8680
  CommonModule,
7942
- FormsModule,
7943
- LuxModule] }); }
8681
+ FormsModule] }); }
7944
8682
  }
7945
8683
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DagaModule, decorators: [{
7946
8684
  type: NgModule,
@@ -7958,17 +8696,224 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
7958
8696
  TextMapEditorComponent,
7959
8697
  PropertyEditorComponent,
7960
8698
  CommonModule,
7961
- FormsModule,
7962
- LuxModule
8699
+ FormsModule
7963
8700
  ],
7964
8701
  exports: [DiagramComponent, DiagramEditorComponent],
7965
8702
  providers: [DagaConfigurationService, CanvasProviderService]
7966
8703
  }]
7967
8704
  }] });
7968
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
+
7969
8914
  /**
7970
8915
  * Generated bundle index. Do not edit.
7971
8916
  */
7972
8917
 
7973
- 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 };
7974
8919
  //# sourceMappingURL=metadev-daga.mjs.map