@metadev/daga 1.4.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/Changelog.md +16 -1
  2. package/assets/icon/property/add.svg +23 -3
  3. package/assets/icon/property/gear.svg +3 -0
  4. package/assets/icon/property/move.svg +35 -0
  5. package/assets/styles/_property-editor.scss +161 -101
  6. package/assets/styles/daga.scss +1 -1
  7. package/fesm2022/metadev-daga.mjs +1276 -651
  8. package/fesm2022/metadev-daga.mjs.map +1 -1
  9. package/index.d.ts +4 -4
  10. package/lib/diagram-editor/diagram/collab/collab-action.d.ts +144 -22
  11. package/lib/diagram-editor/diagram/collab/primitives.d.ts +9 -2
  12. package/lib/diagram-editor/diagram/converters/daga-exporter.d.ts +1 -1
  13. package/lib/diagram-editor/diagram/converters/daga-model.d.ts +57 -3
  14. package/lib/diagram-editor/diagram/diagram-action.d.ts +23 -51
  15. package/lib/diagram-editor/diagram/diagram-connection.d.ts +20 -3
  16. package/lib/diagram-editor/diagram/diagram-element.d.ts +24 -2
  17. package/lib/diagram-editor/diagram/diagram-field.d.ts +9 -2
  18. package/lib/diagram-editor/diagram/diagram-node.d.ts +25 -6
  19. package/lib/diagram-editor/diagram/diagram-port.d.ts +3 -2
  20. package/lib/diagram-editor/diagram/diagram-property.d.ts +60 -8
  21. package/lib/diagram-editor/diagram/diagram-section.d.ts +18 -2
  22. package/lib/property-editor/object-editor/object-editor.component.d.ts +2 -3
  23. package/lib/property-editor/option-list-editor/option-list-editor.component.d.ts +5 -1
  24. package/lib/property-editor/property-editor.component.d.ts +2 -0
  25. package/lib/property-editor/property-settings/property-settings.component.d.ts +35 -0
  26. package/lib/property-editor/text-list-editor/text-list-editor.component.d.ts +3 -1
  27. package/package.json +5 -5
@@ -190,10 +190,10 @@ class CollapseButtonComponent {
190
190
  }
191
191
  }
192
192
  }
193
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CollapseButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
194
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: CollapseButtonComponent, isStandalone: true, selector: "daga-collapse-button", inputs: { collapsableSelector: "collapsableSelector", collapsableAdditionalSelector: "collapsableAdditionalSelector", collapsed: "collapsed", disabled: "disabled", direction: "direction", rule: "rule", collapsedValue: "collapsedValue", visibleValue: "visibleValue" }, ngImport: i0, template: "<button class=\"collapse-button {{ direction }}\" (click)=\"toggleCollapse()\">\n <div [class]=\"getClass()\"></div>\n</button>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
193
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: CollapseButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
194
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", type: CollapseButtonComponent, isStandalone: true, selector: "daga-collapse-button", inputs: { collapsableSelector: "collapsableSelector", collapsableAdditionalSelector: "collapsableAdditionalSelector", collapsed: "collapsed", disabled: "disabled", direction: "direction", rule: "rule", collapsedValue: "collapsedValue", visibleValue: "visibleValue" }, ngImport: i0, template: "<button class=\"collapse-button {{ direction }}\" (click)=\"toggleCollapse()\">\n <div [class]=\"getClass()\"></div>\n</button>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
195
195
  }
196
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CollapseButtonComponent, decorators: [{
196
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: CollapseButtonComponent, decorators: [{
197
197
  type: Component,
198
198
  args: [{ standalone: true, selector: 'daga-collapse-button', imports: [CommonModule], template: "<button class=\"collapse-button {{ direction }}\" (click)=\"toggleCollapse()\">\n <div [class]=\"getClass()\"></div>\n</button>\n" }]
199
199
  }], propDecorators: { collapsableSelector: [{
@@ -214,42 +214,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
214
214
  type: Input
215
215
  }] } });
216
216
 
217
- /**
218
- * Removes the given element from the array if it exists within the array.
219
- * @private
220
- * @param arr An array.
221
- * @param obj An element to remove from the array.
222
- * @returns The given array without the given element removed if it was found; otherwise the array without modifications.
223
- */
224
- const removeIfExists = (arr, obj) => {
225
- const index = arr.indexOf(obj);
226
- if (index >= 0) {
227
- arr.splice(index, 1);
228
- }
229
- return arr;
230
- };
231
- /**
232
- * Adds the given element to the array if it doesn't exist within the array.
233
- * @private
234
- * @param arr An array.
235
- * @param obj An element to add to the array.
236
- * @returns The given array with the given element added it was not found; otherwise the array without modifications.
237
- */
238
- const addIfNotExists = (arr, obj) => {
239
- if (!arr.includes(obj)) {
240
- arr.push(obj);
241
- }
242
- return arr;
243
- };
244
-
245
217
  /**
246
218
  * Returns whether the incoming timestamp wins over the existing timestamp.
247
219
  *
248
- * In the DiagramModel, timestamps that have never been set are left undefined;
220
+ * In the DiagramModel, timestamps that have never been set are left null;
249
221
  * `timestampWins` treats that as an "initial timestamp" that loses to all LogicalTimestamps.
250
222
  */
251
223
  function timestampWins(incoming, existing) {
252
- if (existing === undefined)
224
+ if (!existing)
253
225
  return true;
254
226
  if (incoming[0] > existing[0])
255
227
  return true;
@@ -268,16 +240,16 @@ function timestampWins(incoming, existing) {
268
240
  * @private
269
241
  */
270
242
  class AddNodeCollabAction {
271
- constructor(canvas, id, nodeTypeId, coords, label, values) {
243
+ constructor(canvas, id, typeId, coords, label, values) {
272
244
  this.canvas = canvas;
273
245
  this.id = id;
274
- this.nodeTypeId = nodeTypeId;
246
+ this.typeId = typeId;
275
247
  this.coords = coords;
276
248
  this.label = label;
277
249
  this.values = values;
278
250
  }
279
251
  do() {
280
- const node = this.canvas.model.nodes.new(this.nodeTypeId, this.coords, this.id);
252
+ const node = this.canvas.model.nodes.new(this.typeId, this.coords, this.id);
281
253
  if (this.values !== undefined) {
282
254
  node.valueSet.setValues(structuredClone({
283
255
  ...node.valueSet.getValues(),
@@ -290,24 +262,24 @@ class AddNodeCollabAction {
290
262
  }
291
263
  serialize() {
292
264
  return {
293
- type: 'add',
265
+ type: 'addNode',
294
266
  id: this.id,
295
- nodeTypeId: this.nodeTypeId,
267
+ typeId: this.typeId,
296
268
  coords: this.coords,
297
269
  label: this.label,
298
270
  values: this.values
299
271
  };
300
272
  }
301
273
  static deserialize(canvas, serialized) {
302
- return new AddNodeCollabAction(canvas, serialized.id, serialized.nodeTypeId, serialized.coords, serialized.label, serialized.values);
274
+ return new AddNodeCollabAction(canvas, serialized.id, serialized.typeId, serialized.coords, serialized.label, serialized.values);
303
275
  }
304
276
  }
305
277
  /**
306
- * Collaborative action which consists of moving a node.
307
- * @see MoveNodeAction
278
+ * Collaborative action which consists of changing a node's geometry: moving, stretching, or stretching sections.
279
+ * @see SetGeometryAction
308
280
  * @private
309
281
  */
310
- class MoveNodeCollabAction {
282
+ class SetGeometryCollabAction {
311
283
  constructor(canvas, nodeId, to, timestamp) {
312
284
  this.canvas = canvas;
313
285
  this.nodeId = nodeId;
@@ -316,23 +288,197 @@ class MoveNodeCollabAction {
316
288
  }
317
289
  do() {
318
290
  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;
291
+ if (node && timestampWins(this.timestamp, node.geometryTimestamp)) {
292
+ node.setGeometry(this.to);
293
+ // Re-fit the labels, in case their text has changed since `this.to` was measured.
294
+ if (node.label?.fit) {
295
+ this.canvas.fitFieldRootInView(node.label.id);
296
+ }
297
+ for (const section of node.sections) {
298
+ if (section.label?.fit) {
299
+ this.canvas.fitFieldRootInView(section.label.id);
300
+ }
301
+ }
302
+ node.geometryTimestamp = this.timestamp;
324
303
  }
325
304
  }
326
305
  serialize() {
327
306
  return {
328
- type: 'move',
307
+ type: 'setGeometry',
329
308
  nodeId: this.nodeId,
330
309
  to: this.to,
331
310
  timestamp: this.timestamp
332
311
  };
333
312
  }
334
313
  static deserialize(canvas, serialized) {
335
- return new MoveNodeCollabAction(canvas, serialized.nodeId, serialized.to, serialized.timestamp);
314
+ return new SetGeometryCollabAction(canvas, serialized.nodeId, serialized.to, serialized.timestamp);
315
+ }
316
+ }
317
+ /**
318
+ * Collaborative action which consists of adding a connection.
319
+ * @see AddConnectionAction
320
+ * @private
321
+ */
322
+ class AddConnectionCollabAction {
323
+ constructor(canvas, id, typeId, startId, endId) {
324
+ this.canvas = canvas;
325
+ this.id = id;
326
+ this.typeId = typeId;
327
+ this.startId = startId;
328
+ this.endId = endId;
329
+ }
330
+ do() {
331
+ const start = this.canvas.model.ports.get(this.startId);
332
+ const end = this.canvas.model.ports.get(this.endId);
333
+ if (start && end) {
334
+ this.canvas.model.connections.new(this.typeId, start, end, this.id);
335
+ }
336
+ }
337
+ serialize() {
338
+ return {
339
+ type: 'addConnection',
340
+ id: this.id,
341
+ typeId: this.typeId,
342
+ startId: this.startId,
343
+ endId: this.endId
344
+ };
345
+ }
346
+ static deserialize(canvas, serialized) {
347
+ return new AddConnectionCollabAction(canvas, serialized.id, serialized.typeId, serialized.startId, serialized.endId);
348
+ }
349
+ }
350
+ /**
351
+ * Collaborative action which consists of editing the text of a field.
352
+ * @see EditFieldAction
353
+ * @private
354
+ */
355
+ class EditFieldCollabAction {
356
+ constructor(canvas, fieldId, to, timestamp) {
357
+ this.canvas = canvas;
358
+ this.fieldId = fieldId;
359
+ this.to = to;
360
+ this.timestamp = timestamp;
361
+ }
362
+ do() {
363
+ const field = this.canvas.model.fields.get(this.fieldId);
364
+ if (field && timestampWins(this.timestamp, field.textTimestamp)) {
365
+ field.text = this.to;
366
+ field.textTimestamp = this.timestamp;
367
+ }
368
+ }
369
+ serialize() {
370
+ return {
371
+ type: 'editField',
372
+ fieldId: this.fieldId,
373
+ to: this.to,
374
+ timestamp: this.timestamp
375
+ };
376
+ }
377
+ static deserialize(canvas, serialized) {
378
+ return new EditFieldCollabAction(canvas, serialized.fieldId, serialized.to, serialized.timestamp);
379
+ }
380
+ }
381
+ /**
382
+ * Collaborative action which consists of editing the values of a value set.
383
+ * @see UpdateValuesAction
384
+ * @private
385
+ */
386
+ class UpdateValuesCollabAction {
387
+ constructor(canvas, id, to, timestamp) {
388
+ this.canvas = canvas;
389
+ this.id = id;
390
+ this.to = to;
391
+ this.timestamp = timestamp;
392
+ }
393
+ getValueSet() {
394
+ if (this.id === undefined) {
395
+ return this.canvas.model.valueSet;
396
+ }
397
+ else {
398
+ return (this.canvas.model.nodes.get(this.id) ||
399
+ this.canvas.model.connections.get(this.id))?.valueSet;
400
+ }
401
+ }
402
+ do() {
403
+ this.getValueSet()?.overwriteValuesLww(this.to, this.timestamp);
404
+ }
405
+ serialize() {
406
+ return {
407
+ type: 'updateValues',
408
+ id: this.id,
409
+ to: this.to,
410
+ timestamp: this.timestamp
411
+ };
412
+ }
413
+ static deserialize(canvas, serialized) {
414
+ return new UpdateValuesCollabAction(canvas, serialized.id, serialized.to, serialized.timestamp);
415
+ }
416
+ }
417
+ /**
418
+ * Collaborative action which consists of removing or un-removing elements.
419
+ *
420
+ * Specifically, the action sets the `selfRemoved` field for elements that are
421
+ * explicitly removed/unremoved. This causes the `removed` fields for those elements
422
+ * and their transitive dependents to update automatically.
423
+ * For example, self-removing a node also removes all of its sections/connections/etc.
424
+ *
425
+ * @see RemoveAction
426
+ * @private
427
+ */
428
+ class SetSelfRemovedCollabAction {
429
+ constructor(canvas, nodeIds, sectionIds, portIds, connectionIds, fieldIds, selfRemoved, timestamp) {
430
+ this.canvas = canvas;
431
+ this.nodeIds = nodeIds;
432
+ this.sectionIds = sectionIds;
433
+ this.portIds = portIds;
434
+ this.connectionIds = connectionIds;
435
+ this.fieldIds = fieldIds;
436
+ this.selfRemoved = selfRemoved;
437
+ this.timestamp = timestamp;
438
+ }
439
+ doOne(elt) {
440
+ if (elt && timestampWins(this.timestamp, elt.selfRemovedTimestamp)) {
441
+ elt.selfRemoved = this.selfRemoved;
442
+ elt.selfRemovedTimestamp = this.timestamp;
443
+ }
444
+ }
445
+ do() {
446
+ for (const nodeId of this.nodeIds) {
447
+ this.doOne(this.canvas.model.nodes.get(nodeId));
448
+ }
449
+ for (const sectionId of this.sectionIds) {
450
+ this.doOne(this.canvas.model.sections.get(sectionId));
451
+ }
452
+ for (const portId of this.portIds) {
453
+ this.doOne(this.canvas.model.ports.get(portId));
454
+ }
455
+ for (const connectionId of this.connectionIds) {
456
+ this.doOne(this.canvas.model.connections.get(connectionId));
457
+ }
458
+ for (const fieldId of this.fieldIds) {
459
+ this.doOne(this.canvas.model.fields.get(fieldId));
460
+ }
461
+ // update view
462
+ this.canvas.updateNodesInView(...this.nodeIds);
463
+ this.canvas.updateSectionsInView(...this.sectionIds);
464
+ this.canvas.updatePortsInView(...this.portIds);
465
+ this.canvas.updateConnectionsInView(...this.connectionIds);
466
+ this.canvas.updateFieldsInView(...this.fieldIds);
467
+ }
468
+ serialize() {
469
+ return {
470
+ type: 'setSelfRemoved',
471
+ nodeIds: this.nodeIds,
472
+ sectionIds: this.sectionIds,
473
+ portIds: this.portIds,
474
+ connectionIds: this.connectionIds,
475
+ fieldIds: this.fieldIds,
476
+ removed: this.selfRemoved,
477
+ timestamp: this.timestamp
478
+ };
479
+ }
480
+ static deserialize(canvas, serialized) {
481
+ return new SetSelfRemovedCollabAction(canvas, serialized.nodeIds, serialized.sectionIds, serialized.portIds, serialized.connectionIds, serialized.fieldIds, serialized.removed, serialized.timestamp);
336
482
  }
337
483
  }
338
484
 
@@ -415,7 +561,7 @@ var DiagramActions;
415
561
  DiagramActions["EditField"] = "edit-field";
416
562
  /**
417
563
  * Action that corresponds to moving a node.
418
- * @see MoveNodeAction
564
+ * @see SetGeometryAction
419
565
  * @public
420
566
  */
421
567
  DiagramActions["MoveNode"] = "move-node";
@@ -427,13 +573,13 @@ var DiagramActions;
427
573
  DiagramActions["Remove"] = "remove";
428
574
  /**
429
575
  * Action that corresponds to altering a node's dimensions.
430
- * @see StretchNodeAction
576
+ * @see SetGeometryActionAction
431
577
  * @public
432
578
  */
433
579
  DiagramActions["StretchNode"] = "stretch-node";
434
580
  /**
435
581
  * Action that corresponds to altering a sections's dimensions.
436
- * @see StretchSectionAction
582
+ * @see SetGeometryAction
437
583
  * @public
438
584
  */
439
585
  DiagramActions["StretchSection"] = "stretch-section";
@@ -468,141 +614,47 @@ class AddNodeAction {
468
614
  this.canvas.collabEngine.doCollaboratively(collabAction);
469
615
  }
470
616
  undo() {
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();
495
- }
496
- }
497
- redo() {
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();
522
- }
523
- }
524
- }
525
- /**
526
- * Action which consists of moving a node.
527
- * @see DiagramNode
528
- * @private
529
- */
530
- class MoveNodeAction {
531
- constructor(canvas, nodeId, from, to) {
532
- this.canvas = canvas;
533
- this.nodeId = nodeId;
534
- this.from = from;
535
- this.to = to;
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
- }
541
- undo() {
542
- const collabAction = new MoveNodeCollabAction(this.canvas, this.nodeId, this.from, this.canvas.collabEngine.freshTimestamp());
617
+ const collabAction = new SetSelfRemovedCollabAction(this.canvas, [this.id], [], [], [], [], true, this.canvas.collabEngine.freshTimestamp());
543
618
  this.canvas.collabEngine.doCollaboratively(collabAction);
544
619
  }
545
620
  redo() {
546
- this.do();
621
+ const collabAction = new SetSelfRemovedCollabAction(this.canvas, [this.id], [], [], [], [], false, this.canvas.collabEngine.freshTimestamp());
622
+ this.canvas.collabEngine.doCollaboratively(collabAction);
547
623
  }
548
624
  }
549
625
  /**
550
- * Action which consists of changing the dimensions of a node in a given direction.
626
+ * Action which consists of changing a node's geometry: moving, stretching, or stretching sections.
551
627
  * @see DiagramNode
552
628
  * @private
553
629
  */
554
- class StretchNodeAction {
555
- constructor(canvas, nodeId, direction, from, to) {
630
+ class SetGeometryAction {
631
+ constructor(canvas, intent, nodeId, from, to) {
556
632
  this.canvas = canvas;
633
+ this.intent = intent;
557
634
  this.nodeId = nodeId;
558
- this.direction = direction;
559
635
  this.from = from;
560
636
  this.to = to;
561
637
  }
562
638
  do() {
563
- this.canvas.model.nodes
564
- .get(this.nodeId)
565
- ?.stretch(this.direction, this.to - this.from);
639
+ const collabAction = new SetGeometryCollabAction(this.canvas, this.nodeId, this.to, this.canvas.collabEngine.freshTimestamp());
640
+ this.canvas.collabEngine.doCollaboratively(collabAction);
566
641
  }
567
642
  undo() {
568
- this.canvas.model.nodes
569
- .get(this.nodeId)
570
- ?.stretch(this.direction, this.from - this.to);
571
- }
572
- redo() {
573
- this.do();
574
- }
575
- }
576
- /**
577
- * Action which consists of changing the dimensions of a section in a given direction.
578
- * @see DiagramSection
579
- * @private
580
- */
581
- class StretchSectionAction {
582
- constructor(canvas, sectionId, direction, from, to) {
583
- this.canvas = canvas;
584
- this.sectionId = sectionId;
585
- this.direction = direction;
586
- this.from = from;
587
- this.to = to;
588
- }
589
- do() {
590
- const section = this.canvas.model.sections.get(this.sectionId);
591
- const node = section?.node;
643
+ const node = this.canvas.model.nodes.get(this.nodeId);
592
644
  if (node) {
593
- node.stretchSections(this.direction, this.to - this.from, section.indexXInNode, section.indexYInNode);
645
+ this.to = node.getGeometry();
646
+ const collabAction = new SetGeometryCollabAction(this.canvas, this.nodeId, this.from, this.canvas.collabEngine.freshTimestamp());
647
+ this.canvas.collabEngine.doCollaboratively(collabAction);
594
648
  }
595
649
  }
596
- undo() {
597
- const section = this.canvas.model.sections.get(this.sectionId);
598
- const node = section?.node;
650
+ redo() {
651
+ const node = this.canvas.model.nodes.get(this.nodeId);
599
652
  if (node) {
600
- node.stretchSections(this.direction, this.from - this.to, section.indexXInNode, section.indexYInNode);
653
+ this.from = node.getGeometry();
654
+ const collabAction = new SetGeometryCollabAction(this.canvas, this.nodeId, this.to, this.canvas.collabEngine.freshTimestamp());
655
+ this.canvas.collabEngine.doCollaboratively(collabAction);
601
656
  }
602
657
  }
603
- redo() {
604
- this.do();
605
- }
606
658
  }
607
659
  /**
608
660
  * Action which consists of adding a connection.
@@ -610,39 +662,24 @@ class StretchSectionAction {
610
662
  * @private
611
663
  */
612
664
  class AddConnectionAction {
613
- constructor(canvas, type, startId, endId, id) {
665
+ constructor(canvas, type, startId, endId) {
614
666
  this.canvas = canvas;
615
667
  this.type = type;
616
668
  this.startId = startId;
617
669
  this.endId = endId;
618
- this.id = id;
670
+ this.id = this.canvas.collabEngine.freshId();
619
671
  }
620
672
  do() {
621
- const start = this.canvas.model.ports.get(this.startId);
622
- const end = this.canvas.model.ports.get(this.endId);
623
- if (start && end) {
624
- const connection = this.canvas.model.connections.new(this.type, start, end, this.id);
625
- // reset our id in case it was automatically generated
626
- this.id = connection.id;
627
- }
673
+ const collabAction = new AddConnectionCollabAction(this.canvas, this.id, this.type.id, this.startId, this.endId);
674
+ this.canvas.collabEngine.doCollaboratively(collabAction);
628
675
  }
629
676
  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
- }
677
+ const collabAction = new SetSelfRemovedCollabAction(this.canvas, [], [], [], [this.id], [], true, this.canvas.collabEngine.freshTimestamp());
678
+ this.canvas.collabEngine.doCollaboratively(collabAction);
637
679
  }
638
680
  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
- }
681
+ const collabAction = new SetSelfRemovedCollabAction(this.canvas, [], [], [], [this.id], [], false, this.canvas.collabEngine.freshTimestamp());
682
+ this.canvas.collabEngine.doCollaboratively(collabAction);
646
683
  }
647
684
  }
648
685
  /**
@@ -658,19 +695,24 @@ class EditFieldAction {
658
695
  this.to = to;
659
696
  }
660
697
  do() {
661
- const field = this.canvas.model.fields.get(this.fieldId);
662
- if (field) {
663
- field.text = this.to;
664
- }
698
+ const collabAction = new EditFieldCollabAction(this.canvas, this.fieldId, this.to, this.canvas.collabEngine.freshTimestamp());
699
+ this.canvas.collabEngine.doCollaboratively(collabAction);
665
700
  }
666
701
  undo() {
667
702
  const field = this.canvas.model.fields.get(this.fieldId);
668
703
  if (field) {
669
- field.text = this.from;
704
+ this.to = field.text;
705
+ const collabAction = new EditFieldCollabAction(this.canvas, this.fieldId, this.from, this.canvas.collabEngine.freshTimestamp());
706
+ this.canvas.collabEngine.doCollaboratively(collabAction);
670
707
  }
671
708
  }
672
709
  redo() {
673
- this.do();
710
+ const field = this.canvas.model.fields.get(this.fieldId);
711
+ if (field) {
712
+ this.from = field.text;
713
+ const collabAction = new EditFieldCollabAction(this.canvas, this.fieldId, this.to, this.canvas.collabEngine.freshTimestamp());
714
+ this.canvas.collabEngine.doCollaboratively(collabAction);
715
+ }
674
716
  }
675
717
  }
676
718
  /**
@@ -679,180 +721,63 @@ class EditFieldAction {
679
721
  * @private
680
722
  */
681
723
  class UpdateValuesAction {
682
- constructor(model, id, from, to) {
683
- this.model = model;
724
+ constructor(canvas, id, from, to) {
725
+ this.canvas = canvas;
684
726
  this.id = id;
685
727
  this.from = from;
686
728
  this.to = to;
687
729
  }
688
- getValueSet() {
689
- if (this.id === undefined) {
690
- return this.model.valueSet;
691
- }
692
- else {
693
- return (this.model.nodes.get(this.id) || this.model.connections.get(this.id))?.valueSet;
694
- }
695
- }
696
730
  do() {
697
- this.getValueSet()?.overwriteValues(this.to);
731
+ const collabAction = new UpdateValuesCollabAction(this.canvas, this.id, this.to, this.canvas.collabEngine.freshTimestamp());
732
+ this.canvas.collabEngine.doCollaboratively(collabAction);
698
733
  }
699
734
  undo() {
700
- this.getValueSet()?.overwriteValues(this.from);
735
+ const collabAction = new UpdateValuesCollabAction(this.canvas, this.id, this.from, this.canvas.collabEngine.freshTimestamp());
736
+ const valueSet = collabAction.getValueSet();
737
+ if (valueSet) {
738
+ this.to = valueSet.getValuesForKeys(this.to);
739
+ this.canvas.collabEngine.doCollaboratively(collabAction);
740
+ }
701
741
  }
702
742
  redo() {
703
- this.do();
743
+ const collabAction = new UpdateValuesCollabAction(this.canvas, this.id, this.to, this.canvas.collabEngine.freshTimestamp());
744
+ const valueSet = collabAction.getValueSet();
745
+ if (valueSet) {
746
+ this.from = valueSet.getValuesForKeys(this.from);
747
+ this.canvas.collabEngine.doCollaboratively(collabAction);
748
+ }
704
749
  }
705
750
  }
706
751
  /**
707
752
  * Action which consists of removing elements from a diagram.
753
+ *
754
+ * You should only include an element in this action if the element itself is explicitly removed.
755
+ * Elements removed as a consequence of a removing another element
756
+ * (e.g., removing a node removes all of its sections)
757
+ * will be handled automatically, in a way that converges in the case
758
+ * of concurrent collaborative actions.
759
+ *
708
760
  * @private
709
761
  */
710
762
  class RemoveAction {
711
- constructor(model, nodeIds, sectionIds, portIds, connectionIds, fieldIds) {
712
- this.model = model;
763
+ constructor(canvas, nodeIds, sectionIds, portIds, connectionIds, fieldIds) {
764
+ this.canvas = canvas;
713
765
  this.nodeIds = nodeIds;
714
766
  this.sectionIds = sectionIds;
715
767
  this.portIds = portIds;
716
- this.connectionIds = connectionIds;
717
- this.fieldIds = fieldIds;
718
- }
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);
726
- }
727
- for (const port of node.ports) {
728
- addIfNotExists(this.portIds, port.id);
729
- }
730
- if (node.label) {
731
- addIfNotExists(this.fieldIds, node.label.id);
732
- }
733
- }
734
- }
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);
741
- }
742
- if (section.label) {
743
- addIfNotExists(this.fieldIds, section.label.id);
744
- }
745
- }
746
- }
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);
753
- }
754
- for (const connection of port.incomingConnections) {
755
- addIfNotExists(this.connectionIds, connection.id);
756
- }
757
- if (port.label) {
758
- addIfNotExists(this.fieldIds, port.label.id);
759
- }
760
- }
761
- }
762
- for (const connectionId of this.connectionIds) {
763
- const connection = this.model.connections.get(connectionId);
764
- if (connection) {
765
- connection.removed = true;
766
- }
767
- }
768
- for (const fieldId of this.fieldIds) {
769
- const field = this.model.fields.get(fieldId);
770
- if (field) {
771
- field.removed = true;
772
- }
773
- }
774
- // update view
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);
768
+ this.connectionIds = connectionIds;
769
+ this.fieldIds = fieldIds;
770
+ }
771
+ do() {
772
+ const collabAction = new SetSelfRemovedCollabAction(this.canvas, this.nodeIds, this.sectionIds, this.portIds, this.connectionIds, this.fieldIds, true, this.canvas.collabEngine.freshTimestamp());
773
+ this.canvas.collabEngine.doCollaboratively(collabAction);
780
774
  }
781
775
  undo() {
782
- for (const nodeId of this.nodeIds) {
783
- const node = this.model.nodes.get(nodeId);
784
- if (node) {
785
- node.removed = false;
786
- }
787
- }
788
- for (const sectionId of this.sectionIds) {
789
- const section = this.model.sections.get(sectionId);
790
- if (section) {
791
- section.removed = false;
792
- }
793
- }
794
- for (const portId of this.portIds) {
795
- const port = this.model.ports.get(portId);
796
- if (port) {
797
- port.removed = false;
798
- }
799
- }
800
- for (const connectionId of this.connectionIds) {
801
- const connection = this.model.connections.get(connectionId);
802
- if (connection) {
803
- connection.removed = false;
804
- }
805
- }
806
- for (const fieldId of this.fieldIds) {
807
- const field = this.model.fields.get(fieldId);
808
- if (field) {
809
- field.removed = false;
810
- }
811
- }
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);
776
+ const collabAction = new SetSelfRemovedCollabAction(this.canvas, this.nodeIds, this.sectionIds, this.portIds, this.connectionIds, this.fieldIds, false, this.canvas.collabEngine.freshTimestamp());
777
+ this.canvas.collabEngine.doCollaboratively(collabAction);
818
778
  }
819
779
  redo() {
820
- for (const nodeId of this.nodeIds) {
821
- const node = this.model.nodes.get(nodeId);
822
- if (node) {
823
- node.removed = true;
824
- }
825
- }
826
- for (const sectionId of this.sectionIds) {
827
- const section = this.model.sections.get(sectionId);
828
- if (section) {
829
- section.removed = true;
830
- }
831
- }
832
- for (const portId of this.portIds) {
833
- const port = this.model.ports.get(portId);
834
- if (port) {
835
- port.removed = true;
836
- }
837
- }
838
- for (const connectionId of this.connectionIds) {
839
- const connection = this.model.connections.get(connectionId);
840
- if (connection) {
841
- connection.removed = true;
842
- }
843
- }
844
- for (const fieldId of this.fieldIds) {
845
- const field = this.model.fields.get(fieldId);
846
- if (field) {
847
- field.removed = true;
848
- }
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);
780
+ this.do();
856
781
  }
857
782
  }
858
783
 
@@ -996,6 +921,34 @@ class Grid {
996
921
  }
997
922
  }
998
923
 
924
+ /**
925
+ * Removes the given element from the array if it exists within the array.
926
+ * @private
927
+ * @param arr An array.
928
+ * @param obj An element to remove from the array.
929
+ * @returns The given array without the given element removed if it was found; otherwise the array without modifications.
930
+ */
931
+ const removeIfExists = (arr, obj) => {
932
+ const index = arr.indexOf(obj);
933
+ if (index >= 0) {
934
+ arr.splice(index, 1);
935
+ }
936
+ return arr;
937
+ };
938
+ /**
939
+ * Adds the given element to the array if it doesn't exist within the array.
940
+ * @private
941
+ * @param arr An array.
942
+ * @param obj An element to add to the array.
943
+ * @returns The given array with the given element added it was not found; otherwise the array without modifications.
944
+ */
945
+ const addIfNotExists = (arr, obj) => {
946
+ if (!arr.includes(obj)) {
947
+ arr.push(obj);
948
+ }
949
+ return arr;
950
+ };
951
+
999
952
  /**
1000
953
  * A layout which places adjacent nodes close by, placing nodes in order according to depth first search.
1001
954
  * @public
@@ -1389,7 +1342,7 @@ class ForceLayout {
1389
1342
  const coolingFactor = 0.99;
1390
1343
  const minimumTemperature = 1;
1391
1344
  const attractionFactor = 0.1;
1392
- const repulsionFactor = 200000;
1345
+ const repulsionFactor = 200_000;
1393
1346
  const gapRepulsionFactor = 0.5;
1394
1347
  const shiftNodeIfOverlap = 1;
1395
1348
  const gravityFactor = 0.002;
@@ -2380,10 +2333,22 @@ class DiagramElement {
2380
2333
  */
2381
2334
  this.selected = false;
2382
2335
  /**
2383
- * Whether this diagram element has been removed.
2336
+ * Whether this diagram element has itself been explicitly removed.
2337
+ *
2338
+ * Override the `removed` getter so that it returns true if and only if:
2339
+ * - `selfRemoved` is true, or
2340
+ * - `removed` is true for any of this element's dependencies.
2341
+ *
2342
+ * For example, a DiagramConnection is removed if either of its ports are removed,
2343
+ * even if the connection's own `selfRemoved` field is false.
2344
+ * @private
2345
+ */
2346
+ this.selfRemoved = false;
2347
+ /**
2348
+ * Collaborative timestamp for selfRemoved.
2384
2349
  * @private
2385
2350
  */
2386
- this.removed = false;
2351
+ this.selfRemovedTimestamp = null;
2387
2352
  this.model = model;
2388
2353
  this._id = id;
2389
2354
  }
@@ -2568,6 +2533,11 @@ var Type;
2568
2533
  * @see Type.Option
2569
2534
  */
2570
2535
  Type["OptionList"] = "option-list";
2536
+ /**
2537
+ * A type whose value must be a list of values picked from a set of options without repeated values.
2538
+ * @see Type.Option
2539
+ */
2540
+ Type["OptionSet"] = "option-set";
2571
2541
  /**
2572
2542
  * A type whose value must be a string without newline characters.
2573
2543
  */
@@ -2580,6 +2550,10 @@ var Type;
2580
2550
  * A type whose value must be a list of strings.
2581
2551
  */
2582
2552
  Type["TextList"] = "text-list";
2553
+ /**
2554
+ * A type whose value must be a list of strings without repeated values.
2555
+ */
2556
+ Type["TextSet"] = "text-set";
2583
2557
  /**
2584
2558
  * A type whose value must be a map with string keys and values.
2585
2559
  */
@@ -2625,6 +2599,13 @@ class ValueSet {
2625
2599
  this.hiddenProperties = [];
2626
2600
  this.values = {};
2627
2601
  this.valueSets = {};
2602
+ /**
2603
+ * Collaborative timestamps for all keys in this.values that have ever been set,
2604
+ * even if since they've since been set to the default value.
2605
+ *
2606
+ * Object values (in this.valueSets) store their own timestamps separately.
2607
+ */
2608
+ this.ownTimestamps = {};
2628
2609
  this.propertySet = propertySet;
2629
2610
  this.rootElement = rootElement;
2630
2611
  this.resetValues();
@@ -2723,6 +2704,45 @@ class ValueSet {
2723
2704
  }
2724
2705
  return result;
2725
2706
  }
2707
+ /**
2708
+ * Returns the values for all keys present in the given object, including keys in sub-objects.
2709
+ *
2710
+ * @private
2711
+ * @param values An object containing all values for keys present in the given object.
2712
+ */
2713
+ getValuesForKeys(keys) {
2714
+ const result = {};
2715
+ for (const key in keys) {
2716
+ const property = this.propertySet.getProperty(key);
2717
+ if (property.type === Type.Object) {
2718
+ result[key] = this.valueSets[key].getValuesForKeys(keys[key]);
2719
+ }
2720
+ else {
2721
+ result[key] = this.getValue(key);
2722
+ }
2723
+ }
2724
+ return result;
2725
+ }
2726
+ /**
2727
+ * Obtain all CollabTimestamps in the set, including those corresponding to nested keys.
2728
+ * @private
2729
+ * @returns An object containing all the CollabTimestamps in the set.
2730
+ */
2731
+ getTimestamps() {
2732
+ const result = {};
2733
+ for (const key in this.propertySet.propertyMap) {
2734
+ const property = this.propertySet.getProperty(key);
2735
+ if (property.type === Type.Object) {
2736
+ result[key] = this.valueSets[key].getTimestamps();
2737
+ }
2738
+ else {
2739
+ const timestamp = this.ownTimestamps[key];
2740
+ if (timestamp)
2741
+ result[key] = timestamp;
2742
+ }
2743
+ }
2744
+ return result;
2745
+ }
2726
2746
  /**
2727
2747
  * Check if the value under the key is not empty.
2728
2748
  * @private
@@ -2737,7 +2757,7 @@ class ValueSet {
2737
2757
  * Check if the value under the key is not empty or the default value.
2738
2758
  * @private
2739
2759
  * @param key A key.
2740
- * @returns `true` if the value under the key is empty or the default value, `false` otherwise.
2760
+ * @returns `true` if the value under the key is not empty or the default value, `false` otherwise.
2741
2761
  */
2742
2762
  hasSetValue(key) {
2743
2763
  const value = this.getValue(key);
@@ -2774,12 +2794,14 @@ class ValueSet {
2774
2794
  }
2775
2795
  else {
2776
2796
  this.values[key] = value;
2797
+ if (!equals(value, property.defaultValue)) {
2798
+ this.displayProperty(property);
2799
+ }
2777
2800
  }
2778
2801
  const rootAttribute = property.rootAttribute;
2779
2802
  if (rootAttribute !== undefined && rootAttribute !== null) {
2780
2803
  this.setRootElementValue(rootAttribute, value);
2781
2804
  }
2782
- this.displayProperty(property);
2783
2805
  }
2784
2806
  }
2785
2807
  /**
@@ -2799,6 +2821,23 @@ class ValueSet {
2799
2821
  }
2800
2822
  }
2801
2823
  }
2824
+ /**
2825
+ * Reset all timestamps and then set them to the given timestamps.
2826
+ * @private
2827
+ * @param values An object containing all the CollabTimestamps in the set.
2828
+ */
2829
+ setTimestamps(timestamps) {
2830
+ this.ownTimestamps = {};
2831
+ for (const key in timestamps) {
2832
+ const property = this.propertySet.getProperty(key);
2833
+ if (property.type === Type.Object) {
2834
+ this.valueSets[key].setTimestamps(timestamps[key]);
2835
+ }
2836
+ else {
2837
+ this.ownTimestamps[key] = timestamps[key];
2838
+ }
2839
+ }
2840
+ }
2802
2841
  /**
2803
2842
  * Writes the given values over the value set's existing values without resetting the existing values.
2804
2843
  * @private
@@ -2815,6 +2854,25 @@ class ValueSet {
2815
2854
  }
2816
2855
  }
2817
2856
  }
2857
+ /**
2858
+ * Variant of overwriteValues that applies last-writer-wins to each key, updating timestamps if appropriate.
2859
+ * @private
2860
+ * @param values An object containing all values to set the values to.
2861
+ */
2862
+ overwriteValuesLww(values, timestamp) {
2863
+ for (const key in values) {
2864
+ const property = this.propertySet.getProperty(key);
2865
+ if (property.type === Type.Object) {
2866
+ this.valueSets[key].overwriteValuesLww(values[key], timestamp);
2867
+ }
2868
+ else {
2869
+ if (timestampWins(timestamp, this.ownTimestamps[key])) {
2870
+ this.setValue(key, values[key]);
2871
+ this.ownTimestamps[key] = timestamp;
2872
+ }
2873
+ }
2874
+ }
2875
+ }
2818
2876
  /**
2819
2877
  * Set all the values of this set to the defaults.
2820
2878
  * 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.
@@ -2823,6 +2881,7 @@ class ValueSet {
2823
2881
  resetValues() {
2824
2882
  this.displayedProperties = [];
2825
2883
  this.hiddenProperties = [];
2884
+ this.ownTimestamps = {};
2826
2885
  for (const key in this.propertySet.propertyMap) {
2827
2886
  const property = this.propertySet.getProperty(key);
2828
2887
  const rootAttribute = property.rootAttribute;
@@ -2915,10 +2974,10 @@ const equals = (a, b) => {
2915
2974
  return a === b || JSON.stringify(a) === JSON.stringify(b);
2916
2975
  };
2917
2976
  /**
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.
2977
+ * Calculate the differences between the given objects and return two objects, each containing the keys for which the values are different in each argument, containing the different values.
2919
2978
  * @param a A dictionary.
2920
2979
  * @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.
2980
+ * @returns A tuple of two objects with each containing the keys that have a different value in the corresponding argument compared to the other argument.
2922
2981
  */
2923
2982
  const diff = (a, b) => {
2924
2983
  const aDiff = {};
@@ -2934,7 +2993,13 @@ const diff = (a, b) => {
2934
2993
  }
2935
2994
  for (const key of allKeys) {
2936
2995
  if (isObject(a[key]) && isObject(b[key])) {
2937
- [aDiff[key], bDiff[key]] = diff(a[key], b[key]);
2996
+ const diffAB = diff(a[key], b[key]);
2997
+ // only add the key if differences are detected
2998
+ if (Object.keys(diffAB[0]).length > 0 &&
2999
+ Object.keys(diffAB[1]).length > 0) {
3000
+ aDiff[key] = diffAB[0];
3001
+ bDiff[key] = diffAB[1];
3002
+ }
2938
3003
  }
2939
3004
  else {
2940
3005
  if (!equals(a[key], b[key])) {
@@ -2945,9 +3010,8 @@ const diff = (a, b) => {
2945
3010
  }
2946
3011
  return [aDiff, bDiff];
2947
3012
  };
2948
- const isObject = (x) => typeof x === 'object' && !!x;
3013
+ const isObject = (x) => x !== undefined && x !== null && x.constructor === Object;
2949
3014
 
2950
- let idTicker$4 = 0;
2951
3015
  /**
2952
3016
  * Default values of the parameters of a diagram connection.
2953
3017
  * @see DiagramConnection
@@ -2967,6 +3031,23 @@ const DIAGRAM_CONNECTION_TYPE_DEFAULTS = {
2967
3031
  selectedColor: '#000000',
2968
3032
  properties: []
2969
3033
  };
3034
+ // TODO: Should maybe get these variables from the diagram configuration rather than have them hardcoded
3035
+ /**
3036
+ * Whether a connection can have the same port as both its start port and its end port.
3037
+ */
3038
+ const CAN_A_CONNECTION_END_ON_THE_SAME_PORT_IT_STARTS = false;
3039
+ /**
3040
+ * Whether a connection can have the same ports as another connection.
3041
+ */
3042
+ const CAN_A_CONNECTION_HAVE_THE_SAME_PORTS_AS_ANOTHER = false;
3043
+ /**
3044
+ * Whether a port can have multiple connections.
3045
+ */
3046
+ const CAN_CONNECTIONS_SHARE_PORTS = true;
3047
+ /**
3048
+ * Whether tightening connections is allowed.
3049
+ */
3050
+ const CAN_TIGHTEN_CONNECTIONS = true;
2970
3051
  /**
2971
3052
  * A connection type, which holds properties that connections of this type share in common.
2972
3053
  * @see ConnectionTypeConfig
@@ -3016,16 +3097,10 @@ class DiagramConnection extends DiagramElement {
3016
3097
  this.middleLabel = name;
3017
3098
  }
3018
3099
  constructor(model, type, start, end, id) {
3019
- let uniqueId;
3020
- if (id !== undefined && model.connections.get(id) === undefined) {
3021
- uniqueId = id;
3022
- }
3023
- else {
3024
- do {
3025
- uniqueId = `diagram-connection-${++idTicker$4}`;
3026
- } while (model.connections.get(uniqueId) !== undefined);
3100
+ if (model.connections.get(id) !== undefined) {
3101
+ throw new Error(`DiagramConnection with id "${id}" already exists`);
3027
3102
  }
3028
- super(model, uniqueId);
3103
+ super(model, id);
3029
3104
  /**
3030
3105
  * Coordinates of the start point of this connection.
3031
3106
  * @public
@@ -3064,6 +3139,11 @@ class DiagramConnection extends DiagramElement {
3064
3139
  this.setStart(start);
3065
3140
  this.setEnd(end);
3066
3141
  }
3142
+ get removed() {
3143
+ return (this.selfRemoved ||
3144
+ (this.start !== undefined && this.start.removed) ||
3145
+ (this.end !== undefined && this.end.removed));
3146
+ }
3067
3147
  updateInView() {
3068
3148
  this.model.canvas?.updateConnectionsInView(this.id);
3069
3149
  }
@@ -3121,20 +3201,104 @@ class DiagramConnection extends DiagramElement {
3121
3201
  * @public
3122
3202
  */
3123
3203
  tighten() {
3124
- if (this.start?.rootElement && this.end?.rootElement) {
3125
- const tighterStartPort =
3126
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
3127
- this.start.rootElement.getClosestPortToPoint(this.end.coords);
3128
- if (tighterStartPort && tighterStartPort !== this.start) {
3129
- this.setStart(tighterStartPort);
3130
- }
3131
- const tighterEndPort =
3132
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
3133
- this.end.rootElement.getClosestPortToPoint(this.start.coords);
3134
- if (tighterEndPort && tighterEndPort !== this.end) {
3135
- this.setEnd(tighterEndPort);
3204
+ if (!CAN_TIGHTEN_CONNECTIONS) {
3205
+ return;
3206
+ }
3207
+ if (this.start?.rootElement && this.end) {
3208
+ const alternativeStartPortsSortedByDistanceAscending = this.start.rootElement.ports
3209
+ .map((p) => [
3210
+ p,
3211
+ p.distanceTo(this.end.coords)
3212
+ ])
3213
+ .sort((a, b) => a[1] - b[1])
3214
+ .map((a) => a[0]);
3215
+ checkAlternativeStartPorts: for (const alternativeStartPort of alternativeStartPortsSortedByDistanceAscending) {
3216
+ if (!CAN_A_CONNECTION_END_ON_THE_SAME_PORT_IT_STARTS &&
3217
+ alternativeStartPort === this.end) {
3218
+ // alternative start port not valid, it is the same as the end port
3219
+ continue checkAlternativeStartPorts;
3220
+ }
3221
+ if (!CAN_CONNECTIONS_SHARE_PORTS &&
3222
+ ((alternativeStartPort.incomingConnections.length === 1 &&
3223
+ alternativeStartPort.incomingConnections[0] !== this) ||
3224
+ alternativeStartPort.incomingConnections.length > 1 ||
3225
+ (alternativeStartPort.outgoingConnections.length === 1 &&
3226
+ alternativeStartPort.outgoingConnections[0] !== this) ||
3227
+ alternativeStartPort.outgoingConnections.length > 1)) {
3228
+ // alternative start port not valid, it already has other connections
3229
+ continue checkAlternativeStartPorts;
3230
+ }
3231
+ if (!CAN_A_CONNECTION_HAVE_THE_SAME_PORTS_AS_ANOTHER) {
3232
+ for (const connection of alternativeStartPort.outgoingConnections) {
3233
+ if (connection !== this && connection.end === this.end) {
3234
+ // alternative start port not valid, there is a connection whose start and end matches the alternative start port and this connection's end
3235
+ continue checkAlternativeStartPorts;
3236
+ }
3237
+ }
3238
+ for (const connection of alternativeStartPort.incomingConnections) {
3239
+ if (connection !== this && connection.start === this.end) {
3240
+ // alternative start port not valid, there is a connection whose start and end matches the alternative start port and this connection's end
3241
+ continue checkAlternativeStartPorts;
3242
+ }
3243
+ }
3244
+ }
3245
+ // found a valid start port
3246
+ if (alternativeStartPort === this.start) {
3247
+ break checkAlternativeStartPorts;
3248
+ }
3249
+ else {
3250
+ this.setStart(alternativeStartPort);
3251
+ break checkAlternativeStartPorts;
3252
+ }
3253
+ }
3254
+ }
3255
+ if (this.end?.rootElement && this.start) {
3256
+ const alternativeEndPortsSortedByDistanceAscending = this.end.rootElement.ports
3257
+ .map((p) => [
3258
+ p,
3259
+ p.distanceTo(this.start.coords)
3260
+ ])
3261
+ .sort((a, b) => a[1] - b[1])
3262
+ .map((a) => a[0]);
3263
+ checkAlternativeEndPorts: for (const alternativeEndPort of alternativeEndPortsSortedByDistanceAscending) {
3264
+ if (!CAN_A_CONNECTION_END_ON_THE_SAME_PORT_IT_STARTS &&
3265
+ alternativeEndPort === this.start) {
3266
+ // alternative start port not valid, it is the same as the end port
3267
+ continue checkAlternativeEndPorts;
3268
+ }
3269
+ if (!CAN_CONNECTIONS_SHARE_PORTS &&
3270
+ ((alternativeEndPort.outgoingConnections.length === 1 &&
3271
+ alternativeEndPort.outgoingConnections[0] !== this) ||
3272
+ alternativeEndPort.outgoingConnections.length > 1 ||
3273
+ (alternativeEndPort.incomingConnections.length === 1 &&
3274
+ alternativeEndPort.incomingConnections[0] !== this) ||
3275
+ alternativeEndPort.incomingConnections.length > 1)) {
3276
+ // alternative start port not valid, it already has other connections
3277
+ continue checkAlternativeEndPorts;
3278
+ }
3279
+ if (!CAN_A_CONNECTION_HAVE_THE_SAME_PORTS_AS_ANOTHER) {
3280
+ for (const connection of alternativeEndPort.incomingConnections) {
3281
+ if (connection !== this && connection.start === this.start) {
3282
+ // alternative start port not valid, there is a connection whose start and end matches the alternative end port and this connection's start
3283
+ continue checkAlternativeEndPorts;
3284
+ }
3285
+ }
3286
+ for (const connection of alternativeEndPort.outgoingConnections) {
3287
+ if (connection !== this && connection.end === this.start) {
3288
+ // alternative start port not valid, there is a connection whose start and end matches the alternative end port and this connection's start
3289
+ continue checkAlternativeEndPorts;
3290
+ }
3291
+ }
3292
+ }
3293
+ // found a valid start port
3294
+ if (alternativeEndPort === this.end) {
3295
+ break checkAlternativeEndPorts;
3296
+ }
3297
+ else {
3298
+ this.setEnd(alternativeEndPort);
3299
+ break checkAlternativeEndPorts;
3300
+ }
3136
3301
  }
3137
- this.updateInView();
3138
3302
  }
3139
3303
  }
3140
3304
  getPriority() {
@@ -3176,7 +3340,7 @@ class DiagramConnectionSet extends DiagramEntitySet {
3176
3340
  * @param type The type of the connection given as either the type itself or the id of the type.
3177
3341
  * @param start The start port of the connection.
3178
3342
  * @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.
3343
+ * @param id The id of the connection.
3180
3344
  * @returns The instanced connection.
3181
3345
  */
3182
3346
  new(type, start, end, id) {
@@ -3211,7 +3375,6 @@ class DiagramConnectionSet extends DiagramEntitySet {
3211
3375
  }
3212
3376
  }
3213
3377
 
3214
- let idTicker$3 = 0;
3215
3378
  /**
3216
3379
  * Default values of the look of a diagram section.
3217
3380
  * @see DIAGRAM_SECTION_DEFAULTS
@@ -3281,16 +3444,10 @@ class DiagramSection extends DiagramElement {
3281
3444
  }
3282
3445
  }
3283
3446
  constructor(model, node, indexXInNode, indexYInNode, coords, width, height, id) {
3284
- let uniqueId;
3285
- if (id !== undefined && model.sections.get(id) === undefined) {
3286
- uniqueId = id;
3447
+ if (model.sections.get(id) !== undefined) {
3448
+ throw new Error(`DiagramSection with id "${id}" already exists`);
3287
3449
  }
3288
- else {
3289
- do {
3290
- uniqueId = `diagram-section-${++idTicker$3}`;
3291
- } while (model.sections.get(uniqueId) !== undefined);
3292
- }
3293
- super(model, uniqueId);
3450
+ super(model, id);
3294
3451
  /**
3295
3452
  * Ports of this section.
3296
3453
  * @public
@@ -3303,6 +3460,9 @@ class DiagramSection extends DiagramElement {
3303
3460
  this.width = width;
3304
3461
  this.height = height;
3305
3462
  }
3463
+ get removed() {
3464
+ return this.selfRemoved || (this.node !== undefined && this.node.removed);
3465
+ }
3306
3466
  updateInView() {
3307
3467
  this.model.canvas?.updateSectionsInView(this.id);
3308
3468
  }
@@ -3390,25 +3550,11 @@ class DiagramSection extends DiagramElement {
3390
3550
  * @param coords A point in the diagram.
3391
3551
  */
3392
3552
  move(coords) {
3393
- const coordDifferences = [
3394
- coords[0] - this.coords[0],
3395
- coords[1] - this.coords[1]
3396
- ];
3397
- this.coords = coords;
3398
- for (const port of this.ports) {
3399
- port.move([
3400
- port.coords[0] + coordDifferences[0],
3401
- port.coords[1] + coordDifferences[1]
3402
- ]);
3403
- }
3404
- if (this.label) {
3405
- this.label.move([
3406
- this.label.coords[0] + coordDifferences[0],
3407
- this.label.coords[1] + coordDifferences[1]
3408
- ]);
3409
- }
3410
- this.getConnections().forEach((c) => c.tighten());
3411
- this.updateInView();
3553
+ this.setGeometry({
3554
+ coords,
3555
+ width: this.width,
3556
+ height: this.height
3557
+ });
3412
3558
  }
3413
3559
  /**
3414
3560
  * Change the dimensions of this section in the given direction by the given amount.
@@ -3451,12 +3597,40 @@ class DiagramSection extends DiagramElement {
3451
3597
  }
3452
3598
  break;
3453
3599
  }
3454
- this.coords = [newCoordsX[0], newCoordsY[0]];
3455
- this.width = newCoordsX[1] - newCoordsX[0];
3456
- this.height = newCoordsY[1] - newCoordsY[0];
3600
+ this.setGeometry({
3601
+ coords: [newCoordsX[0], newCoordsY[0]],
3602
+ width: newCoordsX[1] - newCoordsX[0],
3603
+ height: newCoordsY[1] - newCoordsY[0]
3604
+ });
3605
+ }
3606
+ /**
3607
+ * Returns the current values of all geometric properties (coordinates and dimensions) of this section.
3608
+ * @public
3609
+ */
3610
+ getGeometry() {
3611
+ return {
3612
+ coords: [...this.coords],
3613
+ width: this.width,
3614
+ height: this.height
3615
+ };
3616
+ }
3617
+ /**
3618
+ * Sets all geometric properties (coordinates and dimensions) of this section.
3619
+ * @public
3620
+ */
3621
+ setGeometry(geometry) {
3622
+ const oldCoordsX = [this.coords[0], this.coords[0] + this.width];
3623
+ const oldCoordsY = [this.coords[1], this.coords[1] + this.height];
3624
+ this.coords = [...geometry.coords];
3625
+ this.width = geometry.width;
3626
+ this.height = geometry.height;
3627
+ const newCoordsX = [this.coords[0], this.coords[0] + this.width];
3628
+ const newCoordsY = [this.coords[1], this.coords[1] + this.height];
3629
+ // Move ports to match the new coords.
3457
3630
  for (const port of this.ports) {
3458
3631
  port.move(translatePoint(port.coords, oldCoordsX, oldCoordsY, newCoordsX, newCoordsY));
3459
3632
  }
3633
+ // Set label's dimensions as a function of ours.
3460
3634
  if (this.label) {
3461
3635
  this.label.coords = [
3462
3636
  this.coords[0] + (this.getConfig()?.label?.margin || 0),
@@ -3468,6 +3642,8 @@ class DiagramSection extends DiagramElement {
3468
3642
  this.height - (this.getConfig()?.label?.margin || 0) * 2);
3469
3643
  this.label.updateInView();
3470
3644
  }
3645
+ // Update canvas.
3646
+ this.getConnections().forEach((c) => c.tighten());
3471
3647
  this.updateInView();
3472
3648
  }
3473
3649
  }
@@ -3499,7 +3675,7 @@ class DiagramSectionSet extends DiagramEntitySet {
3499
3675
  const port = this.model.ports.new(section, [
3500
3676
  section.coords[0] + (portConfig?.coords?.[0] || 0),
3501
3677
  section.coords[1] + (portConfig?.coords?.[1] || 0)
3502
- ], portConfig?.direction);
3678
+ ], portConfig?.direction, `${section.id}_${i}`);
3503
3679
  if (portConfig.label) {
3504
3680
  const labelConfiguration = {
3505
3681
  ...DIAGRAM_FIELD_DEFAULTS,
@@ -3570,7 +3746,6 @@ class DiagramSectionSet extends DiagramEntitySet {
3570
3746
  }
3571
3747
  }
3572
3748
 
3573
- let idTicker$2 = 0;
3574
3749
  /**
3575
3750
  * Default values of the look of a diagram node.
3576
3751
  * @see DIAGRAM_NODE_TYPE_DEFAULTS
@@ -3654,16 +3829,10 @@ class DiagramNode extends DiagramElement {
3654
3829
  }
3655
3830
  }
3656
3831
  constructor(model, type, coords = [0, 0], id) {
3657
- let uniqueId;
3658
- if (id !== undefined && model.nodes.get(id) === undefined) {
3659
- uniqueId = id;
3660
- }
3661
- else {
3662
- do {
3663
- uniqueId = `diagram-node-${++idTicker$2}`;
3664
- } while (model.nodes.get(uniqueId) !== undefined);
3832
+ if (model.nodes.get(id) !== undefined) {
3833
+ throw new Error(`DiagramNode with id "${id}" already exists`);
3665
3834
  }
3666
- super(model, uniqueId);
3835
+ super(model, id);
3667
3836
  /**
3668
3837
  * Sections of this node.
3669
3838
  * @public
@@ -3674,6 +3843,11 @@ class DiagramNode extends DiagramElement {
3674
3843
  * @public
3675
3844
  */
3676
3845
  this.ports = [];
3846
+ /**
3847
+ * Collaborative timestamp for SetGeometryCollabActions.
3848
+ * @public
3849
+ */
3850
+ this.geometryTimestamp = null;
3677
3851
  this.type = type;
3678
3852
  this.valueSet = new ValueSet(type.propertySet, this);
3679
3853
  this.originalData = {};
@@ -3681,6 +3855,9 @@ class DiagramNode extends DiagramElement {
3681
3855
  this.width = type.defaultWidth;
3682
3856
  this.height = type.defaultHeight;
3683
3857
  }
3858
+ get removed() {
3859
+ return this.selfRemoved;
3860
+ }
3684
3861
  updateInView() {
3685
3862
  this.model.canvas?.updateNodesInView(this.id);
3686
3863
  }
@@ -3784,27 +3961,19 @@ class DiagramNode extends DiagramElement {
3784
3961
  coords[0] - this.coords[0],
3785
3962
  coords[1] - this.coords[1]
3786
3963
  ];
3787
- this.coords = coords;
3788
3964
  for (const section of this.sections) {
3789
3965
  section.move([
3790
3966
  section.coords[0] + coordDifferences[0],
3791
3967
  section.coords[1] + coordDifferences[1]
3792
3968
  ]);
3793
3969
  }
3794
- for (const port of this.ports) {
3795
- port.move([
3796
- port.coords[0] + coordDifferences[0],
3797
- port.coords[1] + coordDifferences[1]
3798
- ]);
3799
- }
3800
- if (this.label) {
3801
- this.label.move([
3802
- this.label.coords[0] + coordDifferences[0],
3803
- this.label.coords[1] + coordDifferences[1]
3804
- ]);
3805
- }
3806
- this.getConnections().forEach((c) => c.tighten());
3807
- this.updateInView();
3970
+ this.setGeometry({
3971
+ coords,
3972
+ width: this.width,
3973
+ height: this.height,
3974
+ // We already moved the sections above - skip changing them in setDimensions.
3975
+ sections: {}
3976
+ });
3808
3977
  }
3809
3978
  /**
3810
3979
  * Change the dimensions of this node in the given direction by the given amount without changing the dimensions of the sections of this node.
@@ -3847,23 +4016,13 @@ class DiagramNode extends DiagramElement {
3847
4016
  }
3848
4017
  break;
3849
4018
  }
3850
- this.coords = [newCoordsX[0], newCoordsY[0]];
3851
- this.width = newCoordsX[1] - newCoordsX[0];
3852
- this.height = newCoordsY[1] - newCoordsY[0];
3853
- for (const port of this.ports) {
3854
- port.move(translatePoint(port.coords, oldCoordsX, oldCoordsY, newCoordsX, newCoordsY));
3855
- }
3856
- if (this.label) {
3857
- this.label.coords = [
3858
- this.coords[0] + (this.type.label?.margin || 0),
3859
- this.coords[1] + (this.type.label?.margin || 0)
3860
- ];
3861
- (this.label.width = this.width - (this.type.label?.margin || 0) * 2),
3862
- (this.label.height = this.height - (this.type.label?.margin || 0) * 2);
3863
- this.label.updateInView();
3864
- }
3865
- // we ignore this.sections, the stretching of a node with sections is handled in the stretchSections method
3866
- this.updateInView();
4019
+ this.setGeometry({
4020
+ coords: [newCoordsX[0], newCoordsY[0]],
4021
+ width: newCoordsX[1] - newCoordsX[0],
4022
+ height: newCoordsY[1] - newCoordsY[0],
4023
+ // we ignore this.sections, the stretching of a node with sections is handled in the stretchSections method
4024
+ sections: {}
4025
+ });
3867
4026
  }
3868
4027
  /**
3869
4028
  * 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.
@@ -3951,6 +4110,59 @@ class DiagramNode extends DiagramElement {
3951
4110
  }
3952
4111
  this.stretch(direction, distance);
3953
4112
  }
4113
+ /**
4114
+ * Returns the current values of all geometric properties (coordinates and dimensions) of this node and its sections.
4115
+ * @public
4116
+ */
4117
+ getGeometry() {
4118
+ const sections = {};
4119
+ for (const section of this.sections) {
4120
+ sections[section.id] = section.getGeometry();
4121
+ }
4122
+ return {
4123
+ coords: [...this.coords],
4124
+ width: this.width,
4125
+ height: this.height,
4126
+ sections
4127
+ };
4128
+ }
4129
+ /**
4130
+ * Sets all geometric properties (coordinates and dimensions) of this node and its sections.
4131
+ * @public
4132
+ */
4133
+ setGeometry(geometry) {
4134
+ const oldCoordsX = [this.coords[0], this.coords[0] + this.width];
4135
+ const oldCoordsY = [this.coords[1], this.coords[1] + this.height];
4136
+ this.coords = [...geometry.coords];
4137
+ this.width = geometry.width;
4138
+ this.height = geometry.height;
4139
+ const newCoordsX = [this.coords[0], this.coords[0] + this.width];
4140
+ const newCoordsY = [this.coords[1], this.coords[1] + this.height];
4141
+ // Update sections, if supplied.
4142
+ for (const section of this.sections) {
4143
+ const sectionGeometry = geometry.sections[section.id];
4144
+ if (sectionGeometry) {
4145
+ section.setGeometry(sectionGeometry);
4146
+ }
4147
+ }
4148
+ // Move ports to match the new coords.
4149
+ for (const port of this.ports) {
4150
+ port.move(translatePoint(port.coords, oldCoordsX, oldCoordsY, newCoordsX, newCoordsY));
4151
+ }
4152
+ // Set label's dimensions as a function of ours.
4153
+ if (this.label) {
4154
+ this.label.coords = [
4155
+ this.coords[0] + (this.type.label?.margin || 0),
4156
+ this.coords[1] + (this.type.label?.margin || 0)
4157
+ ];
4158
+ (this.label.width = this.width - (this.type.label?.margin || 0) * 2),
4159
+ (this.label.height = this.height - (this.type.label?.margin || 0) * 2);
4160
+ this.label.updateInView();
4161
+ }
4162
+ // Update canvas.
4163
+ this.getConnections().forEach((c) => c.tighten());
4164
+ this.updateInView();
4165
+ }
3954
4166
  }
3955
4167
  class DiagramNodeSet extends DiagramEntitySet {
3956
4168
  /**
@@ -3971,7 +4183,7 @@ class DiagramNodeSet extends DiagramEntitySet {
3971
4183
  * @public
3972
4184
  * @param type The type of the node given as either the type itself or the id of the type.
3973
4185
  * @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.
4186
+ * @param id The id of the node.
3975
4187
  * @returns The instanced node.
3976
4188
  */
3977
4189
  new(type, coords, id) {
@@ -3998,7 +4210,7 @@ class DiagramNodeSet extends DiagramEntitySet {
3998
4210
  for (let i = 0; i < nodeType.sectionGrid.sections[j].length; ++i) {
3999
4211
  this.model.sections.new(node, i, j, [widthAccumulator, heightAccumulator], nodeType.sectionGrid.defaultWidths?.[i] ||
4000
4212
  DIAGRAM_SECTION_DEFAULT_WIDTH, nodeType.sectionGrid.defaultHeights?.[j] ||
4001
- DIAGRAM_SECTION_DEFAULT_HEIGHT);
4213
+ DIAGRAM_SECTION_DEFAULT_HEIGHT, `${node.id}_${j}_${i}`);
4002
4214
  widthAccumulator +=
4003
4215
  (nodeType.sectionGrid.defaultWidths?.[i] ||
4004
4216
  DIAGRAM_SECTION_DEFAULT_WIDTH) +
@@ -4017,7 +4229,7 @@ class DiagramNodeSet extends DiagramEntitySet {
4017
4229
  const port = this.model.ports.new(node, [
4018
4230
  node.coords[0] + portConfig.coords[0],
4019
4231
  node.coords[1] + portConfig.coords[1]
4020
- ], portConfig.direction);
4232
+ ], portConfig.direction, `${node.id}_${i}`);
4021
4233
  if (portConfig.label) {
4022
4234
  const labelConfiguration = {
4023
4235
  ...DIAGRAM_FIELD_DEFAULTS,
@@ -4089,7 +4301,6 @@ class DiagramNodeSet extends DiagramEntitySet {
4089
4301
  }
4090
4302
  }
4091
4303
 
4092
- let idTicker$1 = 0;
4093
4304
  /**
4094
4305
  * Default values of the parameters of a diagram port.
4095
4306
  * @see DiagramPort
@@ -4109,16 +4320,10 @@ const DIAGRAM_PORT_DEFAULTS = {
4109
4320
  */
4110
4321
  class DiagramPort extends DiagramElement {
4111
4322
  constructor(model, rootElement, coords, direction, id) {
4112
- let uniqueId;
4113
- if (id !== undefined && model.ports.get(id) === undefined) {
4114
- uniqueId = id;
4115
- }
4116
- else {
4117
- do {
4118
- uniqueId = `diagram-port-${++idTicker$1}`;
4119
- } while (model.ports.get(uniqueId) !== undefined);
4323
+ if (model.ports.get(id) !== undefined) {
4324
+ throw new Error(`DiagramPort with id "${id}" already exists`);
4120
4325
  }
4121
- super(model, uniqueId);
4326
+ super(model, id);
4122
4327
  /**
4123
4328
  * Connections that start at this port.
4124
4329
  * @public
@@ -4133,6 +4338,10 @@ class DiagramPort extends DiagramElement {
4133
4338
  this.coords = coords;
4134
4339
  this.direction = direction;
4135
4340
  }
4341
+ get removed() {
4342
+ return (this.selfRemoved ||
4343
+ (this.rootElement !== undefined && this.rootElement.removed));
4344
+ }
4136
4345
  updateInView() {
4137
4346
  this.model.canvas?.updatePortsInView(this.id);
4138
4347
  }
@@ -4248,7 +4457,6 @@ class DiagramPortSet extends DiagramEntitySet {
4248
4457
  }
4249
4458
  }
4250
4459
 
4251
- let idTicker = 0;
4252
4460
  /**
4253
4461
  * Default values of the parameters of a diagram field.
4254
4462
  * @see DiagramField
@@ -4291,17 +4499,17 @@ class DiagramField extends DiagramElement {
4291
4499
  this.model.canvas?.fitFieldRootInView(this.id);
4292
4500
  }
4293
4501
  }
4294
- constructor(model, rootElement, coords, width, height, fontSize, fontFamily, color, selectedColor, horizontalAlign, verticalAlign, text, editable, fit, id) {
4295
- let uniqueId;
4296
- if (id !== undefined && model.fields.get(id) === undefined) {
4297
- uniqueId = id;
4298
- }
4299
- else {
4300
- do {
4301
- uniqueId = `diagram-field-${++idTicker}`;
4302
- } while (model.fields.get(uniqueId) !== undefined);
4502
+ constructor(model, rootElement, coords, width, height, fontSize, fontFamily, color, selectedColor, horizontalAlign, verticalAlign, text, editable, fit) {
4503
+ const id = `${rootElement?.id}_field`;
4504
+ if (model.fields.get(id) !== undefined) {
4505
+ throw new Error('DiagramField for rootElement already exists');
4303
4506
  }
4304
- super(model, uniqueId);
4507
+ super(model, id);
4508
+ /**
4509
+ * Collaborative timestamp for text.
4510
+ * @public
4511
+ */
4512
+ this.textTimestamp = null;
4305
4513
  this.rootElement = rootElement;
4306
4514
  this.coords = coords;
4307
4515
  this.width = width;
@@ -4322,6 +4530,10 @@ class DiagramField extends DiagramElement {
4322
4530
  ?.selectCanvasView()
4323
4531
  ?.select(`foreignObject#${this.id}`);
4324
4532
  }
4533
+ get removed() {
4534
+ return (this.selfRemoved ||
4535
+ (this.rootElement !== undefined && this.rootElement.removed));
4536
+ }
4325
4537
  updateInView() {
4326
4538
  this.model.canvas?.updateFieldsInView(this.id);
4327
4539
  }
@@ -4351,8 +4563,8 @@ class DiagramFieldSet extends DiagramEntitySet {
4351
4563
  * 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
4564
  * @private
4353
4565
  */
4354
- new(rootElement, coords, fontSize, fontFamily, color, selectedColor, width, height, horizontalAlign, verticalAlign, text, editable, fit, id) {
4355
- const field = new DiagramField(this.model, rootElement, coords, width, height, fontSize, fontFamily, color, selectedColor, horizontalAlign, verticalAlign, text, editable, fit, id);
4566
+ new(rootElement, coords, fontSize, fontFamily, color, selectedColor, width, height, horizontalAlign, verticalAlign, text, editable, fit) {
4567
+ const field = new DiagramField(this.model, rootElement, coords, width, height, fontSize, fontFamily, color, selectedColor, horizontalAlign, verticalAlign, text, editable, fit);
4356
4568
  super.add(field);
4357
4569
  field.updateInView();
4358
4570
  // add this field to its root element
@@ -4474,7 +4686,10 @@ class CollabEngine {
4474
4686
  */
4475
4687
  freshId() {
4476
4688
  // 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();
4689
+ // Since IDs are often used in CSS query selectors, ensure that they start with
4690
+ // a letter instead of a number.
4691
+ // See https://stackoverflow.com/a/34777644/16782898
4692
+ return 'id' + v4();
4478
4693
  }
4479
4694
  /**
4480
4695
  * Performs the action - immediately locally, and eventually for remote collaborators.
@@ -4502,13 +4717,33 @@ class CollabEngine {
4502
4717
  }
4503
4718
  // Perform CollabAction.
4504
4719
  switch (message.type) {
4505
- case 'add': {
4720
+ case 'addNode': {
4506
4721
  const action = AddNodeCollabAction.deserialize(this.canvas, message);
4507
4722
  action.do();
4508
4723
  break;
4509
4724
  }
4510
- case 'move': {
4511
- const action = MoveNodeCollabAction.deserialize(this.canvas, message);
4725
+ case 'setGeometry': {
4726
+ const action = SetGeometryCollabAction.deserialize(this.canvas, message);
4727
+ action.do();
4728
+ break;
4729
+ }
4730
+ case 'addConnection': {
4731
+ const action = AddConnectionCollabAction.deserialize(this.canvas, message);
4732
+ action.do();
4733
+ break;
4734
+ }
4735
+ case 'editField': {
4736
+ const action = EditFieldCollabAction.deserialize(this.canvas, message);
4737
+ action.do();
4738
+ break;
4739
+ }
4740
+ case 'updateValues': {
4741
+ const action = UpdateValuesCollabAction.deserialize(this.canvas, message);
4742
+ action.do();
4743
+ break;
4744
+ }
4745
+ case 'setSelfRemoved': {
4746
+ const action = SetSelfRemovedCollabAction.deserialize(this.canvas, message);
4512
4747
  action.do();
4513
4748
  break;
4514
4749
  }
@@ -4543,6 +4778,7 @@ const DIAGRAM_PROPERTIES_TEXT = 'Diagram properties';
4543
4778
  */
4544
4779
  const ACTION_QUEUE_SIZE = 25;
4545
4780
  const UNFINISHED_CONNECTION_ID = 'diagram-connection-unfinished';
4781
+ const MAX_DISTANCE_TO_PORT = 100;
4546
4782
  class DiagramCanvas {
4547
4783
  static { this.canvasCount = 0; }
4548
4784
  constructor(parentComponent, config) {
@@ -4663,7 +4899,7 @@ class DiagramCanvas {
4663
4899
  fieldIdsToBeDeleted.push(userSelectionElement.id);
4664
4900
  }
4665
4901
  }
4666
- const removeAction = new RemoveAction(this.model, nodeIdsToBeDeleted, sectionIdsToBeDeleted, portIdsToBeDeleted, connectionIdsToBeDeleted, fieldIdsToBeDeleted);
4902
+ const removeAction = new RemoveAction(this, nodeIdsToBeDeleted, sectionIdsToBeDeleted, portIdsToBeDeleted, connectionIdsToBeDeleted, fieldIdsToBeDeleted);
4667
4903
  removeAction.do();
4668
4904
  this.actionQueue.add(removeAction);
4669
4905
  }
@@ -4902,7 +5138,7 @@ class DiagramCanvas {
4902
5138
  !d.removed) {
4903
5139
  cursorStyle(CursorStyle.Grabbing);
4904
5140
  this.draggingFrom = [event.x, event.y];
4905
- this.currentAction = new MoveNodeAction(this, d.id, d.coords, [0, 0]);
5141
+ this.currentAction = new SetGeometryAction(this, DiagramActions.MoveNode, d.id, d.getGeometry(), d.getGeometry());
4906
5142
  }
4907
5143
  else {
4908
5144
  cursorStyle(CursorStyle.NotAllowed);
@@ -4924,7 +5160,8 @@ class DiagramCanvas {
4924
5160
  // prevent drag behavior if mouse hasn't moved
4925
5161
  if ((this.draggingFrom[0] !== event.x ||
4926
5162
  this.draggingFrom[1] !== event.y) &&
4927
- this.currentAction instanceof MoveNodeAction) {
5163
+ this.currentAction instanceof SetGeometryAction &&
5164
+ this.currentAction.intent === DiagramActions.MoveNode) {
4928
5165
  let newNodeCoords = [
4929
5166
  event.x - d.width / 2,
4930
5167
  event.y - d.height / 2
@@ -4932,7 +5169,8 @@ class DiagramCanvas {
4932
5169
  if (this.snapToGrid) {
4933
5170
  newNodeCoords = this.getClosestGridPoint(newNodeCoords);
4934
5171
  }
4935
- this.currentAction.to = newNodeCoords;
5172
+ d.move(newNodeCoords);
5173
+ this.currentAction.to = d.getGeometry();
4936
5174
  this.currentAction.do();
4937
5175
  this.actionQueue.add(this.currentAction);
4938
5176
  this.currentAction = undefined;
@@ -5017,7 +5255,7 @@ class DiagramCanvas {
5017
5255
  d.type.resizableX &&
5018
5256
  !d.removed) {
5019
5257
  cursorStyle(CursorStyle.EWResize);
5020
- this.currentAction = new StretchNodeAction(this, d.id, Side.Left, d.width, d.width);
5258
+ this.currentAction = new SetGeometryAction(this, DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
5021
5259
  }
5022
5260
  else {
5023
5261
  cursorStyle(CursorStyle.NotAllowed);
@@ -5035,13 +5273,15 @@ class DiagramCanvas {
5035
5273
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
5036
5274
  d.type.resizableX &&
5037
5275
  !d.removed &&
5038
- this.currentAction instanceof StretchNodeAction) {
5276
+ this.currentAction instanceof SetGeometryAction &&
5277
+ this.currentAction.intent === DiagramActions.StretchNode) {
5039
5278
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5040
5279
  if (this.snapToGrid) {
5041
5280
  pointerCoords = this.getClosestGridPoint(pointerCoords);
5042
5281
  }
5043
5282
  d.stretch(Side.Left, d.coords[0] - pointerCoords[0]);
5044
- this.currentAction.to = d.width;
5283
+ this.currentAction.to = d.getGeometry();
5284
+ this.currentAction.do();
5045
5285
  this.actionQueue.add(this.currentAction);
5046
5286
  this.currentAction = undefined;
5047
5287
  }
@@ -5074,7 +5314,7 @@ class DiagramCanvas {
5074
5314
  d.type.resizableY &&
5075
5315
  !d.removed) {
5076
5316
  cursorStyle(CursorStyle.NSResize);
5077
- this.currentAction = new StretchNodeAction(this, d.id, Side.Top, d.height, d.height);
5317
+ this.currentAction = new SetGeometryAction(this, DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
5078
5318
  }
5079
5319
  else {
5080
5320
  cursorStyle(CursorStyle.NotAllowed);
@@ -5092,13 +5332,15 @@ class DiagramCanvas {
5092
5332
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
5093
5333
  d.type.resizableY &&
5094
5334
  !d.removed &&
5095
- this.currentAction instanceof StretchNodeAction) {
5335
+ this.currentAction instanceof SetGeometryAction &&
5336
+ this.currentAction.intent === DiagramActions.StretchNode) {
5096
5337
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5097
5338
  if (this.snapToGrid) {
5098
5339
  pointerCoords = this.getClosestGridPoint(pointerCoords);
5099
5340
  }
5100
5341
  d.stretch(Side.Top, d.coords[1] - pointerCoords[1]);
5101
- this.currentAction.to = d.height;
5342
+ this.currentAction.to = d.getGeometry();
5343
+ this.currentAction.do();
5102
5344
  this.actionQueue.add(this.currentAction);
5103
5345
  this.currentAction = undefined;
5104
5346
  }
@@ -5131,7 +5373,7 @@ class DiagramCanvas {
5131
5373
  d.type.resizableX &&
5132
5374
  !d.removed) {
5133
5375
  cursorStyle(CursorStyle.EWResize);
5134
- this.currentAction = new StretchNodeAction(this, d.id, Side.Right, d.width, d.width);
5376
+ this.currentAction = new SetGeometryAction(this, DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
5135
5377
  }
5136
5378
  else {
5137
5379
  cursorStyle(CursorStyle.NotAllowed);
@@ -5149,13 +5391,15 @@ class DiagramCanvas {
5149
5391
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
5150
5392
  d.type.resizableX &&
5151
5393
  !d.removed &&
5152
- this.currentAction instanceof StretchNodeAction) {
5394
+ this.currentAction instanceof SetGeometryAction &&
5395
+ this.currentAction.intent === DiagramActions.StretchNode) {
5153
5396
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5154
5397
  if (this.snapToGrid) {
5155
5398
  pointerCoords = this.getClosestGridPoint(pointerCoords);
5156
5399
  }
5157
5400
  d.stretch(Side.Right, pointerCoords[0] - (d.coords[0] + d.width));
5158
- this.currentAction.to = d.width;
5401
+ this.currentAction.to = d.getGeometry();
5402
+ this.currentAction.do();
5159
5403
  this.actionQueue.add(this.currentAction);
5160
5404
  this.currentAction = undefined;
5161
5405
  }
@@ -5188,7 +5432,7 @@ class DiagramCanvas {
5188
5432
  d.type.resizableY &&
5189
5433
  !d.removed) {
5190
5434
  cursorStyle(CursorStyle.NSResize);
5191
- this.currentAction = new StretchNodeAction(this, d.id, Side.Bottom, d.height, d.height);
5435
+ this.currentAction = new SetGeometryAction(this, DiagramActions.StretchNode, d.id, d.getGeometry(), d.getGeometry());
5192
5436
  }
5193
5437
  else {
5194
5438
  cursorStyle(CursorStyle.NotAllowed);
@@ -5206,13 +5450,15 @@ class DiagramCanvas {
5206
5450
  if (this.canUserPerformAction(DiagramActions.StretchNode) &&
5207
5451
  d.type.resizableY &&
5208
5452
  !d.removed &&
5209
- this.currentAction instanceof StretchNodeAction) {
5453
+ this.currentAction instanceof SetGeometryAction &&
5454
+ this.currentAction.intent === DiagramActions.StretchNode) {
5210
5455
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5211
5456
  if (this.snapToGrid) {
5212
5457
  pointerCoords = this.getClosestGridPoint(pointerCoords);
5213
5458
  }
5214
5459
  d.stretch(Side.Bottom, pointerCoords[1] - (d.coords[1] + d.height));
5215
- this.currentAction.to = d.height;
5460
+ this.currentAction.to = d.getGeometry();
5461
+ this.currentAction.do();
5216
5462
  this.actionQueue.add(this.currentAction);
5217
5463
  this.currentAction = undefined;
5218
5464
  }
@@ -5417,7 +5663,7 @@ class DiagramCanvas {
5417
5663
  const node = d.node;
5418
5664
  cursorStyle(CursorStyle.Grabbing);
5419
5665
  this.draggingFrom = [event.x, event.y];
5420
- this.currentAction = new MoveNodeAction(this, node.id, node.coords, [0, 0]);
5666
+ this.currentAction = new SetGeometryAction(this, DiagramActions.MoveNode, node.id, node.getGeometry(), node.getGeometry());
5421
5667
  }
5422
5668
  else {
5423
5669
  cursorStyle(CursorStyle.NotAllowed);
@@ -5441,7 +5687,8 @@ class DiagramCanvas {
5441
5687
  // prevent drag behavior if mouse hasn't moved
5442
5688
  if ((this.draggingFrom[0] !== event.x ||
5443
5689
  this.draggingFrom[1] !== event.y) &&
5444
- this.currentAction instanceof MoveNodeAction) {
5690
+ this.currentAction instanceof SetGeometryAction &&
5691
+ this.currentAction.intent === DiagramActions.MoveNode) {
5445
5692
  let newNodeCoords = [
5446
5693
  event.x - node.width / 2,
5447
5694
  event.y - node.height / 2
@@ -5449,7 +5696,8 @@ class DiagramCanvas {
5449
5696
  if (this.snapToGrid) {
5450
5697
  newNodeCoords = this.getClosestGridPoint(newNodeCoords);
5451
5698
  }
5452
- this.currentAction.to = newNodeCoords;
5699
+ node.move(newNodeCoords);
5700
+ this.currentAction.to = node.getGeometry();
5453
5701
  this.currentAction.do();
5454
5702
  this.actionQueue.add(this.currentAction);
5455
5703
  this.currentAction = undefined;
@@ -5534,7 +5782,7 @@ class DiagramCanvas {
5534
5782
  d.node?.type?.resizableX &&
5535
5783
  !d.removed) {
5536
5784
  cursorStyle(CursorStyle.EWResize);
5537
- this.currentAction = new StretchSectionAction(this, d.id, Side.Left, d.width, d.width);
5785
+ this.currentAction = new SetGeometryAction(this, DiagramActions.StretchSection, d.node.id, d.node.getGeometry(), d.node.getGeometry());
5538
5786
  }
5539
5787
  else {
5540
5788
  cursorStyle(CursorStyle.NotAllowed);
@@ -5552,13 +5800,15 @@ class DiagramCanvas {
5552
5800
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5553
5801
  d.node?.type?.resizableX &&
5554
5802
  !d.removed &&
5555
- this.currentAction instanceof StretchSectionAction) {
5803
+ this.currentAction instanceof SetGeometryAction &&
5804
+ this.currentAction.intent === DiagramActions.StretchSection) {
5556
5805
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5557
5806
  if (this.snapToGrid) {
5558
5807
  pointerCoords = this.getClosestGridPoint(pointerCoords);
5559
5808
  }
5560
5809
  d.node.stretchSections(Side.Left, d.coords[0] - pointerCoords[0], d.indexXInNode, d.indexYInNode);
5561
- this.currentAction.to = d.width;
5810
+ this.currentAction.to = d.node.getGeometry();
5811
+ this.currentAction.do();
5562
5812
  this.actionQueue.add(this.currentAction);
5563
5813
  this.currentAction = undefined;
5564
5814
  }
@@ -5591,7 +5841,7 @@ class DiagramCanvas {
5591
5841
  d.node?.type?.resizableY &&
5592
5842
  !d.removed) {
5593
5843
  cursorStyle(CursorStyle.NSResize);
5594
- this.currentAction = new StretchSectionAction(this, d.id, Side.Top, d.height, d.height);
5844
+ this.currentAction = new SetGeometryAction(this, DiagramActions.StretchSection, d.node.id, d.node.getGeometry(), d.node.getGeometry());
5595
5845
  }
5596
5846
  else {
5597
5847
  cursorStyle(CursorStyle.NotAllowed);
@@ -5609,13 +5859,15 @@ class DiagramCanvas {
5609
5859
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5610
5860
  d.node?.type?.resizableY &&
5611
5861
  !d.removed &&
5612
- this.currentAction instanceof StretchSectionAction) {
5862
+ this.currentAction instanceof SetGeometryAction &&
5863
+ this.currentAction.intent === DiagramActions.StretchSection) {
5613
5864
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5614
5865
  if (this.snapToGrid) {
5615
5866
  pointerCoords = this.getClosestGridPoint(pointerCoords);
5616
5867
  }
5617
5868
  d.node.stretchSections(Side.Top, d.coords[1] - pointerCoords[1], d.indexXInNode, d.indexYInNode);
5618
- this.currentAction.to = d.height;
5869
+ this.currentAction.to = d.node.getGeometry();
5870
+ this.currentAction.do();
5619
5871
  this.actionQueue.add(this.currentAction);
5620
5872
  this.currentAction = undefined;
5621
5873
  }
@@ -5648,7 +5900,7 @@ class DiagramCanvas {
5648
5900
  d.node?.type?.resizableX &&
5649
5901
  !d.removed) {
5650
5902
  cursorStyle(CursorStyle.EWResize);
5651
- this.currentAction = new StretchSectionAction(this, d.id, Side.Right, d.width, d.width);
5903
+ this.currentAction = new SetGeometryAction(this, DiagramActions.StretchSection, d.node.id, d.node.getGeometry(), d.node.getGeometry());
5652
5904
  }
5653
5905
  else {
5654
5906
  cursorStyle(CursorStyle.NotAllowed);
@@ -5666,13 +5918,15 @@ class DiagramCanvas {
5666
5918
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5667
5919
  d.node?.type?.resizableX &&
5668
5920
  !d.removed &&
5669
- this.currentAction instanceof StretchSectionAction) {
5921
+ this.currentAction instanceof SetGeometryAction &&
5922
+ this.currentAction.intent === DiagramActions.StretchSection) {
5670
5923
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5671
5924
  if (this.snapToGrid) {
5672
5925
  pointerCoords = this.getClosestGridPoint(pointerCoords);
5673
5926
  }
5674
5927
  d.node.stretchSections(Side.Right, pointerCoords[0] - (d.coords[0] + d.width), d.indexXInNode, d.indexYInNode);
5675
- this.currentAction.to = d.width;
5928
+ this.currentAction.to = d.node.getGeometry();
5929
+ this.currentAction.do();
5676
5930
  this.actionQueue.add(this.currentAction);
5677
5931
  this.currentAction = undefined;
5678
5932
  }
@@ -5705,7 +5959,7 @@ class DiagramCanvas {
5705
5959
  d.node?.type?.resizableY &&
5706
5960
  !d.removed) {
5707
5961
  cursorStyle(CursorStyle.NSResize);
5708
- this.currentAction = new StretchSectionAction(this, d.id, Side.Bottom, d.height, d.height);
5962
+ this.currentAction = new SetGeometryAction(this, DiagramActions.StretchSection, d.node.id, d.node.getGeometry(), d.node.getGeometry());
5709
5963
  }
5710
5964
  else {
5711
5965
  cursorStyle(CursorStyle.NotAllowed);
@@ -5723,13 +5977,15 @@ class DiagramCanvas {
5723
5977
  if (this.canUserPerformAction(DiagramActions.StretchSection) &&
5724
5978
  d.node?.type?.resizableY &&
5725
5979
  !d.removed &&
5726
- this.currentAction instanceof StretchSectionAction) {
5980
+ this.currentAction instanceof SetGeometryAction &&
5981
+ this.currentAction.intent === DiagramActions.StretchSection) {
5727
5982
  let pointerCoords = this.getPointerLocationRelativeToCanvas(event);
5728
5983
  if (this.snapToGrid) {
5729
5984
  pointerCoords = this.getClosestGridPoint(pointerCoords);
5730
5985
  }
5731
5986
  d.node.stretchSections(Side.Bottom, pointerCoords[1] - (d.coords[1] + d.height), d.indexXInNode, d.indexYInNode);
5732
- this.currentAction.to = d.height;
5987
+ this.currentAction.to = d.node.getGeometry();
5988
+ this.currentAction.do();
5733
5989
  this.actionQueue.add(this.currentAction);
5734
5990
  this.currentAction = undefined;
5735
5991
  }
@@ -5933,7 +6189,10 @@ class DiagramCanvas {
5933
6189
  exitSelection.remove();
5934
6190
  enterSelection
5935
6191
  .on(Events.MouseOver, (event, d) => {
5936
- this.highlight(d);
6192
+ if (!this.unfinishedConnection) {
6193
+ // if there is an unfinished connection, the port will be highlighted automatically if the pointer is close
6194
+ this.highlight(d);
6195
+ }
5937
6196
  if (this.unfinishedConnection) {
5938
6197
  const canConnectionFinishAtThisPort = (this.unfinishedConnection.type.canStartFromType(this.unfinishedConnection.start?.getNode()?.type?.id || '') &&
5939
6198
  this.unfinishedConnection.type.canFinishOnType(d.getNode()?.type?.id || '')) ||
@@ -5998,6 +6257,26 @@ class DiagramCanvas {
5998
6257
  this.unfinishedConnection.endCoords = endCoords;
5999
6258
  }
6000
6259
  this.updateConnectionsInView(UNFINISHED_CONNECTION_ID);
6260
+ // highlight closest target port
6261
+ if (this.model.ports.length > 0) {
6262
+ const pointerCoords = this.getPointerLocationRelativeToCanvas(event);
6263
+ let minDistanceFound = Number.POSITIVE_INFINITY;
6264
+ let closestPortFound = undefined;
6265
+ for (const port of this.model.ports) {
6266
+ const distance = port.distanceTo(pointerCoords);
6267
+ if (distance < minDistanceFound) {
6268
+ minDistanceFound = distance;
6269
+ closestPortFound = port;
6270
+ }
6271
+ }
6272
+ if (closestPortFound &&
6273
+ minDistanceFound < MAX_DISTANCE_TO_PORT) {
6274
+ this.highlight(closestPortFound);
6275
+ }
6276
+ else {
6277
+ this.unhighlight();
6278
+ }
6279
+ }
6001
6280
  }
6002
6281
  }
6003
6282
  })
@@ -6186,6 +6465,7 @@ class DiagramCanvas {
6186
6465
  d.text = text;
6187
6466
  if (this.currentAction instanceof EditFieldAction) {
6188
6467
  this.currentAction.to = text;
6468
+ this.currentAction.do();
6189
6469
  this.actionQueue.add(this.currentAction);
6190
6470
  this.currentAction = undefined;
6191
6471
  }
@@ -6204,7 +6484,7 @@ class DiagramCanvas {
6204
6484
  : d.rootElement.node;
6205
6485
  cursorStyle(CursorStyle.Grabbing);
6206
6486
  this.draggingFrom = [event.x, event.y];
6207
- this.currentAction = new MoveNodeAction(this, node.id, node.coords, [0, 0]);
6487
+ this.currentAction = new SetGeometryAction(this, DiagramActions.MoveNode, node.id, node.getGeometry(), node.getGeometry());
6208
6488
  }
6209
6489
  }
6210
6490
  else {
@@ -6238,7 +6518,8 @@ class DiagramCanvas {
6238
6518
  // prevent drag behavior if mouse hasn't moved
6239
6519
  if ((this.draggingFrom[0] !== event.x ||
6240
6520
  this.draggingFrom[1] !== event.y) &&
6241
- this.currentAction instanceof MoveNodeAction) {
6521
+ this.currentAction instanceof SetGeometryAction &&
6522
+ this.currentAction.intent === DiagramActions.MoveNode) {
6242
6523
  let newNodeCoords = [
6243
6524
  event.x - node.width / 2,
6244
6525
  event.y - node.height / 2
@@ -6246,7 +6527,8 @@ class DiagramCanvas {
6246
6527
  if (this.snapToGrid) {
6247
6528
  newNodeCoords = this.getClosestGridPoint(newNodeCoords);
6248
6529
  }
6249
- this.currentAction.to = newNodeCoords;
6530
+ node.move(newNodeCoords);
6531
+ this.currentAction.to = node.getGeometry();
6250
6532
  this.currentAction.do();
6251
6533
  this.actionQueue.add(this.currentAction);
6252
6534
  this.currentAction = undefined;
@@ -6636,6 +6918,7 @@ class DiagramCanvas {
6636
6918
  }
6637
6919
  }
6638
6920
  finishConnection(port) {
6921
+ this.unhighlight();
6639
6922
  if (this.unfinishedConnection !== undefined) {
6640
6923
  if (this.unfinishedConnection.start !== port) {
6641
6924
  if (this.unfinishedConnection.type.canStartFromType(this.unfinishedConnection?.start?.getNode()?.type?.id || '') &&
@@ -6767,7 +7050,8 @@ class DiagramCanvas {
6767
7050
  const from = this.propertyEditorValues;
6768
7051
  const to = structuredClone(this.propertyEditorSelection?.valueSet.getValues());
6769
7052
  const [fromDiff, toDiff] = diff(from, to);
6770
- const currentAction = new UpdateValuesAction(this.model, previousSelectionId, fromDiff, toDiff);
7053
+ const currentAction = new UpdateValuesAction(this, previousSelectionId, fromDiff, toDiff);
7054
+ currentAction.do();
6771
7055
  this.actionQueue.add(currentAction);
6772
7056
  this.propertyEditorValues = undefined;
6773
7057
  }
@@ -7006,10 +7290,10 @@ class CanvasProviderService {
7006
7290
  getCanvas() {
7007
7291
  return this._canvas;
7008
7292
  }
7009
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CanvasProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
7010
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CanvasProviderService }); }
7293
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: CanvasProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
7294
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: CanvasProviderService }); }
7011
7295
  }
7012
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CanvasProviderService, decorators: [{
7296
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: CanvasProviderService, decorators: [{
7013
7297
  type: Injectable
7014
7298
  }] });
7015
7299
 
@@ -7130,10 +7414,10 @@ class DiagramButtonsComponent {
7130
7414
  redo() {
7131
7415
  this.canvas.actionQueue.redo();
7132
7416
  }
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 }); }
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"] }] }); }
7417
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DiagramButtonsComponent, deps: [{ token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
7418
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", 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"] }] }); }
7135
7419
  }
7136
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DiagramButtonsComponent, decorators: [{
7420
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DiagramButtonsComponent, decorators: [{
7137
7421
  type: Component,
7138
7422
  args: [{ standalone: true, selector: 'daga-diagram-buttons', imports: [CommonModule], 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" }]
7139
7423
  }], ctorParameters: () => [{ type: CanvasProviderService }], propDecorators: { collapsableButtons: [{
@@ -7198,10 +7482,10 @@ class ErrorsComponent {
7198
7482
  }
7199
7483
  // TODO: IF ERROR IS IN AN ELEMENT BUT NOT IN A SPECIFIC PROPERTY, WE COULD HIGHLIGHT THE ELEMENT
7200
7484
  }
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 }); }
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"] }] }); }
7485
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: ErrorsComponent, deps: [{ token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
7486
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", 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"] }] }); }
7203
7487
  }
7204
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: ErrorsComponent, decorators: [{
7488
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: ErrorsComponent, decorators: [{
7205
7489
  type: Component,
7206
7490
  args: [{ standalone: true, selector: 'daga-errors', imports: [CommonModule, CollapseButtonComponent], 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" }]
7207
7491
  }], ctorParameters: () => [{ type: CanvasProviderService }], propDecorators: { errorsContainer: [{
@@ -7349,7 +7633,7 @@ class PaletteComponent {
7349
7633
  })
7350
7634
  .on(DragEvents.Start, (event) => {
7351
7635
  if (this.canvas.canUserPerformAction(DiagramActions.AddNode)) {
7352
- d3.select('body').style('cursor', 'grabbing');
7636
+ cursorStyle(CursorStyle.Grabbing);
7353
7637
  const pointerCoords = this.canvas.getPointerLocationRelativeToScreen(event);
7354
7638
  if (pointerCoords.length < 2 ||
7355
7639
  isNaN(pointerCoords[0]) ||
@@ -7371,7 +7655,7 @@ class PaletteComponent {
7371
7655
  .on(DragEvents.End, (event) => {
7372
7656
  if (this.canvas.canUserPerformAction(DiagramActions.AddNode)) {
7373
7657
  // take node back to its original position
7374
- d3.select('body').style('cursor', 'auto');
7658
+ cursorStyle(CursorStyle.Auto);
7375
7659
  thisComponent
7376
7660
  .style('position', 'relative')
7377
7661
  .style('left', 0)
@@ -7587,10 +7871,10 @@ class PaletteComponent {
7587
7871
  .text(templateConfig.label);
7588
7872
  }
7589
7873
  }
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 }); }
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"] }] }); }
7874
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: PaletteComponent, deps: [{ token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
7875
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", 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"] }] }); }
7592
7876
  }
7593
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: PaletteComponent, decorators: [{
7877
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: PaletteComponent, decorators: [{
7594
7878
  type: Component,
7595
7879
  args: [{ standalone: true, selector: 'daga-palette', imports: [CommonModule, CollapseButtonComponent], 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" }]
7596
7880
  }], ctorParameters: () => [{ type: CanvasProviderService }], propDecorators: { panel: [{
@@ -7610,6 +7894,214 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
7610
7894
  type: Input
7611
7895
  }] } });
7612
7896
 
7897
+ /**
7898
+ * Editor of the settings of a property editor, such as what properties are displayed and hidden and in what order.
7899
+ * @see PropertyEditor
7900
+ * @see ValueSet
7901
+ * @private
7902
+ */
7903
+ class PropertySettingsComponent {
7904
+ get canvas() {
7905
+ return this.canvasProvider.getCanvas();
7906
+ }
7907
+ get valueSet() {
7908
+ return this._valueSet;
7909
+ }
7910
+ set valueSet(valueSet) {
7911
+ if (this._valueSet !== valueSet) {
7912
+ this._valueSet = valueSet;
7913
+ // force value change detection
7914
+ this.cdr.detectChanges();
7915
+ }
7916
+ }
7917
+ constructor(element, cdr, canvasProvider) {
7918
+ this.element = element;
7919
+ this.cdr = cdr;
7920
+ this.canvasProvider = canvasProvider;
7921
+ /** How many property-settings elements are parents of this property-settings element. @private */
7922
+ this.depth = 0;
7923
+ // Attributes for the template
7924
+ this.Type = Type;
7925
+ this.getStyleClassName = getStyleClassName;
7926
+ this.asString = asString;
7927
+ }
7928
+ ngAfterViewInit() {
7929
+ this.addListeners();
7930
+ }
7931
+ addListeners() {
7932
+ for (const property of this.valueSet?.displayedProperties || []) {
7933
+ let propertyElementWidth = 0;
7934
+ let propertyElementHeight = 0;
7935
+ let closestDropbarIndex = 0;
7936
+ const propertyElement = d3
7937
+ .select(this.element.nativeElement)
7938
+ .select(`.property.${getStyleClassName(property.name)}.depth-${this.depth}`);
7939
+ propertyElement.select('button.move-button').call(d3
7940
+ .drag()
7941
+ .on(DragEvents.Start, (event) => {
7942
+ cursorStyle(CursorStyle.Grabbing);
7943
+ const pointerCoords = this.canvas.getPointerLocationRelativeToScreen(event);
7944
+ if (pointerCoords.length < 2 ||
7945
+ isNaN(pointerCoords[0]) ||
7946
+ isNaN(pointerCoords[1])) {
7947
+ return;
7948
+ }
7949
+ const boundingRect = propertyElement
7950
+ .node()
7951
+ ?.getBoundingClientRect();
7952
+ propertyElementWidth = boundingRect?.width || 0;
7953
+ propertyElementHeight = boundingRect?.height || 0;
7954
+ propertyElement
7955
+ .style('position', 'fixed')
7956
+ .style('left', `${pointerCoords[0] - propertyElementWidth / 2}px`)
7957
+ .style('top', `${pointerCoords[1] - propertyElementHeight / 2}px`)
7958
+ .style('width', `${propertyElementWidth}px`)
7959
+ .style('height', `${propertyElementHeight}px`)
7960
+ .style('z-index', 1);
7961
+ })
7962
+ .on(DragEvents.Drag, (event) => {
7963
+ cursorStyle(CursorStyle.Grabbing);
7964
+ const pointerCoords = this.canvas.getPointerLocationRelativeToScreen(event);
7965
+ if (pointerCoords.length < 2 ||
7966
+ isNaN(pointerCoords[0]) ||
7967
+ isNaN(pointerCoords[1])) {
7968
+ return;
7969
+ }
7970
+ propertyElement
7971
+ .style('position', 'fixed')
7972
+ .style('left', `${pointerCoords[0] - propertyElementWidth / 2}px`)
7973
+ .style('top', `${pointerCoords[1] - propertyElementHeight / 2}px`)
7974
+ .style('width', `${propertyElementWidth}px`)
7975
+ .style('height', `${propertyElementHeight}px`)
7976
+ .style('z-index', 1);
7977
+ d3.select(this.element.nativeElement)
7978
+ .select(`.dropbar.index-${closestDropbarIndex}.depth-${this.depth}`)
7979
+ .style('visibility', 'hidden')
7980
+ .style('height', 0);
7981
+ closestDropbarIndex = this.getClosestDropbarIndex(pointerCoords);
7982
+ d3.select(this.element.nativeElement)
7983
+ .select(`.dropbar.index-${closestDropbarIndex}.depth-${this.depth}`)
7984
+ .style('visibility', 'visible')
7985
+ .style('height', '0.25rem');
7986
+ })
7987
+ .on(DragEvents.End, (event) => {
7988
+ cursorStyle(CursorStyle.Auto);
7989
+ propertyElement
7990
+ .style('position', 'relative')
7991
+ .style('left', 0)
7992
+ .style('top', 0)
7993
+ .style('z-index', 0)
7994
+ .style('width', 'unset')
7995
+ .style('height', 'unset');
7996
+ d3.select(this.element.nativeElement)
7997
+ .select(`.dropbar.index-${closestDropbarIndex}.depth-${this.depth}`)
7998
+ .style('visibility', 'hidden')
7999
+ .style('height', 0);
8000
+ const pointerCoords = this.canvas.getPointerLocationRelativeToScreen(event);
8001
+ if (pointerCoords.length < 2 ||
8002
+ isNaN(pointerCoords[0]) ||
8003
+ isNaN(pointerCoords[1])) {
8004
+ return;
8005
+ }
8006
+ closestDropbarIndex = this.getClosestDropbarIndex(pointerCoords);
8007
+ this.valueSet?.displayedProperties?.splice(this.valueSet.displayedProperties.indexOf(property), 1);
8008
+ this.valueSet?.displayedProperties?.splice(closestDropbarIndex, 0, property);
8009
+ }));
8010
+ }
8011
+ }
8012
+ getClosestDropbarIndex(pointerCoords) {
8013
+ const propertyList = this.valueSet?.propertySet.propertyList || [];
8014
+ // note that this is <= and not < as we are counting the index after the last element as well
8015
+ const coordsList = [];
8016
+ for (let i = 0; i <= propertyList.length; ++i) {
8017
+ const boundingRect = d3
8018
+ .select(this.element.nativeElement)
8019
+ .select(`.dropbar.index-${i}.depth-${this.depth}`)
8020
+ .node()
8021
+ ?.getBoundingClientRect();
8022
+ if (boundingRect) {
8023
+ coordsList.push([
8024
+ boundingRect.x + boundingRect.width / 2,
8025
+ boundingRect.y + boundingRect.height / 2
8026
+ ]);
8027
+ }
8028
+ }
8029
+ if (coordsList.length > 0) {
8030
+ const distanceToDropbars = coordsList.map((p) => distanceBetweenPoints(pointerCoords, p));
8031
+ const indexOfClosestDropbar = distanceToDropbars.indexOf(Math.min(...distanceToDropbars));
8032
+ return indexOfClosestDropbar;
8033
+ }
8034
+ return 0;
8035
+ }
8036
+ displayProperty(property) {
8037
+ if (this.valueSet === undefined) {
8038
+ return;
8039
+ }
8040
+ let selectedProperty;
8041
+ if (property instanceof Property) {
8042
+ selectedProperty = property;
8043
+ }
8044
+ else if (property instanceof Event) {
8045
+ selectedProperty = this.valueSet?.propertySet.getProperty(property.target?.value || '');
8046
+ }
8047
+ else {
8048
+ selectedProperty = this.valueSet?.propertySet.getProperty(property || '');
8049
+ }
8050
+ if (selectedProperty) {
8051
+ this.valueSet.displayProperty(selectedProperty);
8052
+ }
8053
+ this.cdr.detectChanges();
8054
+ this.addListeners();
8055
+ }
8056
+ hideProperty(property) {
8057
+ if (this.valueSet === undefined) {
8058
+ return;
8059
+ }
8060
+ let selectedProperty;
8061
+ if (property instanceof Property) {
8062
+ selectedProperty = property;
8063
+ }
8064
+ else if (property instanceof Event) {
8065
+ selectedProperty = this.valueSet?.propertySet.getProperty(property.target?.value || '');
8066
+ }
8067
+ else {
8068
+ selectedProperty = this.valueSet?.propertySet.getProperty(property || '');
8069
+ }
8070
+ if (selectedProperty) {
8071
+ this.valueSet.hideProperty(selectedProperty);
8072
+ }
8073
+ this.cdr.detectChanges();
8074
+ this.addListeners();
8075
+ }
8076
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: PropertySettingsComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
8077
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", type: PropertySettingsComponent, isStandalone: true, selector: "daga-property-settings", inputs: { valueSet: "valueSet", depth: "depth" }, ngImport: i0, template: "<div class=\"dropbar\" [class]=\"'index-' + 0 + ' depth-' + depth\"></div>\r\n<div\r\n *ngFor=\"let property of valueSet?.displayedProperties || []; index as i\"\r\n class=\"property-and-dropbar\"\r\n [class]=\"getStyleClassName(property.name) + ' depth-' + depth\"\r\n>\r\n <div\r\n class=\"property\"\r\n [class]=\"getStyleClassName(property.name) + ' depth-' + depth\"\r\n >\r\n <div class=\"property-name\">\r\n <span>{{ property.name }}</span>\r\n <div class=\"buttons\">\r\n <button class=\"property-button move-button\">\r\n <div class=\"icon move-icon\"></div>\r\n </button>\r\n <button\r\n class=\"property-button hide-button\"\r\n (click)=\"hideProperty(property.name)\"\r\n >\r\n <div class=\"icon hide-icon\"></div>\r\n </button>\r\n </div>\r\n </div>\r\n <div *ngIf=\"property.type !== Type.Object\" class=\"property-value\">\r\n {{ asString(valueSet?.getValue(property.name)) }}\r\n </div>\r\n <div *ngIf=\"property.type === Type.Object\">\r\n <daga-property-settings\r\n [valueSet]=\"valueSet?.getSubValueSet(property.name)\"\r\n [depth]=\"depth + 1\"\r\n ></daga-property-settings>\r\n </div>\r\n </div>\r\n <div class=\"dropbar\" [class]=\"'index-' + (i + 1) + ' depth-' + depth\"></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: PropertySettingsComponent, selector: "daga-property-settings", inputs: ["valueSet", "depth"] }, { 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"] }] }); }
8078
+ }
8079
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: PropertySettingsComponent, decorators: [{
8080
+ type: Component,
8081
+ args: [{ standalone: true, selector: 'daga-property-settings', imports: [CommonModule, FormsModule], template: "<div class=\"dropbar\" [class]=\"'index-' + 0 + ' depth-' + depth\"></div>\r\n<div\r\n *ngFor=\"let property of valueSet?.displayedProperties || []; index as i\"\r\n class=\"property-and-dropbar\"\r\n [class]=\"getStyleClassName(property.name) + ' depth-' + depth\"\r\n>\r\n <div\r\n class=\"property\"\r\n [class]=\"getStyleClassName(property.name) + ' depth-' + depth\"\r\n >\r\n <div class=\"property-name\">\r\n <span>{{ property.name }}</span>\r\n <div class=\"buttons\">\r\n <button class=\"property-button move-button\">\r\n <div class=\"icon move-icon\"></div>\r\n </button>\r\n <button\r\n class=\"property-button hide-button\"\r\n (click)=\"hideProperty(property.name)\"\r\n >\r\n <div class=\"icon hide-icon\"></div>\r\n </button>\r\n </div>\r\n </div>\r\n <div *ngIf=\"property.type !== Type.Object\" class=\"property-value\">\r\n {{ asString(valueSet?.getValue(property.name)) }}\r\n </div>\r\n <div *ngIf=\"property.type === Type.Object\">\r\n <daga-property-settings\r\n [valueSet]=\"valueSet?.getSubValueSet(property.name)\"\r\n [depth]=\"depth + 1\"\r\n ></daga-property-settings>\r\n </div>\r\n </div>\r\n <div class=\"dropbar\" [class]=\"'index-' + (i + 1) + ' depth-' + depth\"></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" }]
8082
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: CanvasProviderService }], propDecorators: { valueSet: [{
8083
+ type: Input
8084
+ }], depth: [{
8085
+ type: Input
8086
+ }] } });
8087
+ const asString = (value) => {
8088
+ if (Array.isArray(value)) {
8089
+ return value.join(', ');
8090
+ }
8091
+ if (isObject(value)) {
8092
+ return Object.entries(value)
8093
+ .map((pair) => pair.map((p) => JSON.stringify(p)).join(': '))
8094
+ .join(', ');
8095
+ }
8096
+ if (value instanceof Date) {
8097
+ return value.toLocaleString();
8098
+ }
8099
+ if (value === undefined || value === null) {
8100
+ return '';
8101
+ }
8102
+ return '' + value;
8103
+ };
8104
+
7613
8105
  const LOST_FOCUS_TIME_WINDOW_MS = 200;
7614
8106
  /**
7615
8107
  * Combobox with autocomplete.
@@ -7619,7 +8111,7 @@ const LOST_FOCUS_TIME_WINDOW_MS = 200;
7619
8111
  */
7620
8112
  class AutocompleteComponent {
7621
8113
  set value(value) {
7622
- if (value === this._value) {
8114
+ if (equals(value, this._value)) {
7623
8115
  return;
7624
8116
  }
7625
8117
  this._value = value;
@@ -7637,11 +8129,11 @@ class AutocompleteComponent {
7637
8129
  }
7638
8130
  getLabelOfValue(value) {
7639
8131
  for (const option of this.options) {
7640
- if (option.key === value) {
8132
+ if (equals(option.key, value)) {
7641
8133
  return option.label;
7642
8134
  }
7643
8135
  }
7644
- return `${value}`;
8136
+ return asString(value);
7645
8137
  }
7646
8138
  onKeyup(event) {
7647
8139
  if (!this.disabled) {
@@ -7771,12 +8263,12 @@ class AutocompleteComponent {
7771
8263
  this.valueChange.emit(option.key);
7772
8264
  }
7773
8265
  }
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"] }] }); }
8266
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: AutocompleteComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
8267
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", 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 *ngIf=\"(currentOptions?.length || 0) === 0\"\r\n class=\"autocomplete-option no-options\"\r\n >\r\n (No options)\r\n </li>\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
8268
  }
7777
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: AutocompleteComponent, decorators: [{
8269
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: AutocompleteComponent, decorators: [{
7778
8270
  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" }]
8271
+ 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 *ngIf=\"(currentOptions?.length || 0) === 0\"\r\n class=\"autocomplete-option no-options\"\r\n >\r\n (No options)\r\n </li>\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
8272
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { mainElement: [{
7781
8273
  type: ViewChild,
7782
8274
  args: ['main', { static: true }]
@@ -7817,6 +8309,7 @@ class OptionListEditorComponent {
7817
8309
  if (initialAndEmpty) {
7818
8310
  this.valueChange.emit(this._value);
7819
8311
  }
8312
+ this.updateOptionsNotPresentInValue();
7820
8313
  }
7821
8314
  get value() {
7822
8315
  return this._value;
@@ -7827,6 +8320,8 @@ class OptionListEditorComponent {
7827
8320
  this.labelsOfValue = [];
7828
8321
  this.options = [];
7829
8322
  this.valueInput = undefined;
8323
+ this.allowRepeats = true;
8324
+ this.optionsNotPresentInValue = [];
7830
8325
  this.disabled = false;
7831
8326
  this.valueChange = new EventEmitter();
7832
8327
  }
@@ -7838,43 +8333,69 @@ class OptionListEditorComponent {
7838
8333
  }
7839
8334
  return `${value}`;
7840
8335
  }
8336
+ hasValue(value) {
8337
+ for (const item of this._value || []) {
8338
+ if (item === value) {
8339
+ return true;
8340
+ }
8341
+ }
8342
+ return false;
8343
+ }
7841
8344
  removeFromValue(index) {
7842
8345
  if (this._value.length > index) {
7843
8346
  this._value.splice(index, 1);
7844
8347
  this.labelsOfValue.splice(index, 1);
7845
8348
  this.valueChange.emit(this._value);
8349
+ this.updateOptionsNotPresentInValue();
8350
+ this.cdr.detectChanges();
7846
8351
  }
7847
- this.cdr.detectChanges();
7848
8352
  }
7849
8353
  addToValue() {
7850
- if (this.valueInput !== undefined) {
8354
+ if (this.valueInput !== undefined &&
8355
+ (this.allowRepeats ? true : !this.hasValue(this.valueInput))) {
7851
8356
  this._value.push(this.valueInput);
7852
8357
  this.labelsOfValue.push(this.getLabelOfValue(this.valueInput));
7853
8358
  this.valueChange.emit(this._value);
7854
8359
  this.clearInput();
8360
+ this.updateOptionsNotPresentInValue();
7855
8361
  this.cdr.detectChanges();
7856
8362
  }
7857
8363
  }
7858
8364
  clearInput() {
7859
8365
  this.valueInput = '';
7860
8366
  }
8367
+ updateOptionsNotPresentInValue() {
8368
+ if (!this.allowRepeats) {
8369
+ const optionsNotPresentInValue = [];
8370
+ for (const option of this.options) {
8371
+ if (!this.hasValue(option.key)) {
8372
+ optionsNotPresentInValue.push(option);
8373
+ }
8374
+ }
8375
+ this.optionsNotPresentInValue = optionsNotPresentInValue;
8376
+ }
8377
+ }
7861
8378
  onKeyUp(event) {
7862
8379
  if (event.key === 'Enter') {
7863
8380
  this.addToValue();
7864
8381
  }
7865
8382
  }
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 }] }); }
8383
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: OptionListEditorComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
8384
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", type: OptionListEditorComponent, isStandalone: true, selector: "daga-option-list-editor", inputs: { value: "value", options: "options", valueInput: "valueInput", allowRepeats: "allowRepeats", optionsNotPresentInValue: "optionsNotPresentInValue", 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]=\"allowRepeats ? options || [] : optionsNotPresentInValue || []\"\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
8385
  }
7869
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: OptionListEditorComponent, decorators: [{
8386
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: OptionListEditorComponent, decorators: [{
7870
8387
  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" }]
8388
+ 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]=\"allowRepeats ? options || [] : optionsNotPresentInValue || []\"\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
8389
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { value: [{
7873
8390
  type: Input
7874
8391
  }], options: [{
7875
8392
  type: Input
7876
8393
  }], valueInput: [{
7877
8394
  type: Input
8395
+ }], allowRepeats: [{
8396
+ type: Input
8397
+ }], optionsNotPresentInValue: [{
8398
+ type: Input
7878
8399
  }], disabled: [{
7879
8400
  type: Input
7880
8401
  }], valueChange: [{
@@ -7906,29 +8427,41 @@ class TextListEditorComponent {
7906
8427
  this.cdr = cdr;
7907
8428
  this._value = [];
7908
8429
  this.valueInput = '';
8430
+ this.allowRepeats = true;
7909
8431
  this.disabled = false;
7910
8432
  this.valueChange = new EventEmitter();
7911
8433
  }
7912
8434
  getValueFromEvent(event) {
7913
8435
  return event.target.value;
7914
8436
  }
8437
+ hasValue(value) {
8438
+ for (const item of this._value || []) {
8439
+ if (item === value) {
8440
+ return true;
8441
+ }
8442
+ }
8443
+ return false;
8444
+ }
7915
8445
  removeFromValue(index) {
7916
8446
  if (this._value.length > index) {
7917
8447
  this._value.splice(index, 1);
7918
8448
  this.valueChange.emit(this._value);
8449
+ this.cdr.detectChanges();
7919
8450
  }
7920
- this.cdr.detectChanges();
7921
8451
  }
7922
8452
  editFromValue(item, index) {
7923
- if (this._value.length > index) {
8453
+ item = item.trim();
8454
+ if (this._value.length > index && item !== '') {
7924
8455
  this._value.splice(index, 1, item);
7925
8456
  this.valueChange.emit(this._value);
8457
+ this.cdr.detectChanges();
7926
8458
  }
7927
- this.cdr.detectChanges();
7928
8459
  }
7929
8460
  addToValue() {
7930
- if (this.valueInput !== '') {
7931
- this._value.push(this.valueInput);
8461
+ const valueInput = this.valueInput.trim();
8462
+ if (valueInput !== '' &&
8463
+ (this.allowRepeats ? true : !this.hasValue(valueInput))) {
8464
+ this._value.push(valueInput);
7932
8465
  this.valueChange.emit(this._value);
7933
8466
  this.clearInput();
7934
8467
  this.cdr.detectChanges();
@@ -7942,16 +8475,18 @@ class TextListEditorComponent {
7942
8475
  this.addToValue();
7943
8476
  }
7944
8477
  }
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 }); }
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"] }] }); }
8478
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: TextListEditorComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
8479
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", type: TextListEditorComponent, isStandalone: true, selector: "daga-text-list-editor", inputs: { value: "value", valueInput: "valueInput", allowRepeats: "allowRepeats", 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"] }] }); }
7947
8480
  }
7948
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TextListEditorComponent, decorators: [{
8481
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: TextListEditorComponent, decorators: [{
7949
8482
  type: Component,
7950
8483
  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" }]
7951
8484
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { value: [{
7952
8485
  type: Input
7953
8486
  }], valueInput: [{
7954
8487
  type: Input
8488
+ }], allowRepeats: [{
8489
+ type: Input
7955
8490
  }], disabled: [{
7956
8491
  type: Input
7957
8492
  }], valueChange: [{
@@ -7995,19 +8530,25 @@ class TextMapEditorComponent {
7995
8530
  this.cdr.detectChanges();
7996
8531
  }
7997
8532
  editKey(oldKey, newKey) {
7998
- if (oldKey !== newKey) {
8533
+ newKey = newKey.trim();
8534
+ if (oldKey !== newKey && newKey !== '') {
7999
8535
  this._value[newKey] = this._value[oldKey];
8000
8536
  delete this._value[oldKey];
8001
8537
  this.cdr.detectChanges();
8002
8538
  }
8003
8539
  }
8004
8540
  editValue(key, value) {
8005
- this._value[key] = value;
8006
- this.cdr.detectChanges();
8541
+ value = value.trim();
8542
+ if (value !== '') {
8543
+ this._value[key] = value;
8544
+ this.cdr.detectChanges();
8545
+ }
8007
8546
  }
8008
8547
  addToValue() {
8009
- if (this.keyInput !== '' && this.valueInput !== '') {
8010
- this._value[this.keyInput] = this.valueInput;
8548
+ const keyInput = this.keyInput.trim();
8549
+ const valueInput = this.valueInput.trim();
8550
+ if (keyInput !== '' && valueInput !== '') {
8551
+ this._value[keyInput] = valueInput;
8011
8552
  this.valueChange.emit(this._value);
8012
8553
  this.clearKeyInput();
8013
8554
  this.clearValueInput();
@@ -8025,10 +8566,10 @@ class TextMapEditorComponent {
8025
8566
  this.addToValue();
8026
8567
  }
8027
8568
  }
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 }); }
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"] }] }); }
8569
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: TextMapEditorComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
8570
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", 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"] }] }); }
8030
8571
  }
8031
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TextMapEditorComponent, decorators: [{
8572
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: TextMapEditorComponent, decorators: [{
8032
8573
  type: Component,
8033
8574
  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" }]
8034
8575
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { value: [{
@@ -8061,55 +8602,20 @@ class ObjectEditorComponent {
8061
8602
  return this._valueSet;
8062
8603
  }
8063
8604
  set valueSet(valueSet) {
8064
- this._valueSet = valueSet;
8065
- // force value change detection
8066
- this.cdr.detectChanges();
8605
+ if (this._valueSet !== valueSet) {
8606
+ this._valueSet = valueSet;
8607
+ // force value change detection
8608
+ this.cdr.detectChanges();
8609
+ }
8067
8610
  }
8068
8611
  constructor(cdr, canvasProvider) {
8069
8612
  this.cdr = cdr;
8070
8613
  this.canvasProvider = canvasProvider;
8071
- /** How many object-editors are parents of this object-editor. @private */
8614
+ /** How many object-editor elements are parents of this object-editor element. @private */
8072
8615
  this.depth = 0;
8073
8616
  // Attributes for the template
8074
8617
  this.Type = Type;
8075
- }
8076
- displayProperty(property) {
8077
- if (this.valueSet === undefined) {
8078
- return;
8079
- }
8080
- let selectedProperty;
8081
- if (property instanceof Property) {
8082
- selectedProperty = property;
8083
- }
8084
- else if (property instanceof Event) {
8085
- selectedProperty = this.valueSet?.propertySet.getProperty(property.target?.value || '');
8086
- }
8087
- else {
8088
- selectedProperty = this.valueSet?.propertySet.getProperty(property || '');
8089
- }
8090
- if (selectedProperty) {
8091
- this.valueSet.displayProperty(selectedProperty);
8092
- }
8093
- this.cdr.detectChanges();
8094
- }
8095
- hideProperty(property) {
8096
- if (this.valueSet === undefined) {
8097
- return;
8098
- }
8099
- let selectedProperty;
8100
- if (property instanceof Property) {
8101
- selectedProperty = property;
8102
- }
8103
- else if (property instanceof Event) {
8104
- selectedProperty = this.valueSet?.propertySet.getProperty(property.target?.value || '');
8105
- }
8106
- else {
8107
- selectedProperty = this.valueSet?.propertySet.getProperty(property || '');
8108
- }
8109
- if (selectedProperty) {
8110
- this.valueSet.hideProperty(selectedProperty);
8111
- }
8112
- this.cdr.detectChanges();
8618
+ this.getStyleClassName = getStyleClassName;
8113
8619
  }
8114
8620
  setValue(property, value) {
8115
8621
  if (this.valueSet === undefined) {
@@ -8139,10 +8645,10 @@ class ObjectEditorComponent {
8139
8645
  // with no timezone specified, the local time is assumed
8140
8646
  return new Date(string);
8141
8647
  }
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 }); }
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"] }] }); }
8648
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: ObjectEditorComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
8649
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", 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]=\"getStyleClassName(property.name) + ' depth-' + depth\"\r\n>\r\n <p class=\"property-name\">\r\n {{ property.name }}\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=\"\r\n property.type === Type.OptionList || property.type === Type.OptionSet\r\n \"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [allowRepeats]=\"property.type === Type.OptionList\"\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 || property.type === Type.TextSet\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [allowRepeats]=\"property.type === Type.TextList\"\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", 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.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", "allowRepeats", "optionsNotPresentInValue", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: TextListEditorComponent, selector: "daga-text-list-editor", inputs: ["value", "valueInput", "allowRepeats", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: TextMapEditorComponent, selector: "daga-text-map-editor", inputs: ["value", "keyInput", "valueInput", "disabled"], outputs: ["valueChange"] }] }); }
8144
8650
  }
8145
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: ObjectEditorComponent, decorators: [{
8651
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: ObjectEditorComponent, decorators: [{
8146
8652
  type: Component,
8147
8653
  args: [{ standalone: true, selector: 'daga-object-editor', imports: [
8148
8654
  AutocompleteComponent,
@@ -8151,7 +8657,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
8151
8657
  OptionListEditorComponent,
8152
8658
  TextListEditorComponent,
8153
8659
  TextMapEditorComponent
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" }]
8660
+ ], template: "<div\r\n *ngFor=\"let property of valueSet?.displayedProperties || []\"\r\n class=\"property\"\r\n [class]=\"getStyleClassName(property.name) + ' depth-' + depth\"\r\n>\r\n <p class=\"property-name\">\r\n {{ property.name }}\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=\"\r\n property.type === Type.OptionList || property.type === Type.OptionSet\r\n \"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [allowRepeats]=\"property.type === Type.OptionList\"\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 || property.type === Type.TextSet\"\r\n [disabled]=\"!property.editable || !userCanEdit\"\r\n [allowRepeats]=\"property.type === Type.TextList\"\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" }]
8155
8661
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: CanvasProviderService }], propDecorators: { valueSet: [{
8156
8662
  type: Input
8157
8663
  }], depth: [{
@@ -8168,9 +8674,12 @@ class PropertyEditorComponent {
8168
8674
  return this._valueSet;
8169
8675
  }
8170
8676
  set valueSet(valueSet) {
8171
- this._valueSet = valueSet;
8172
- // force value change detection
8173
- this.cdr.detectChanges();
8677
+ if (this._valueSet !== valueSet) {
8678
+ this._valueSet = valueSet;
8679
+ this.settings = undefined;
8680
+ // force value change detection
8681
+ this.cdr.detectChanges();
8682
+ }
8174
8683
  }
8175
8684
  constructor(cdr) {
8176
8685
  this.cdr = cdr;
@@ -8194,7 +8703,7 @@ class PropertyEditorComponent {
8194
8703
  let selector = '';
8195
8704
  let depth = 0;
8196
8705
  for (const propertyName of propertyNames) {
8197
- selector += ` .property.${propertyName}.depth-${depth}`;
8706
+ selector += ` .property.${getStyleClassName(propertyName)}.depth-${depth}`;
8198
8707
  ++depth;
8199
8708
  }
8200
8709
  if (this.valueSet) {
@@ -8209,12 +8718,17 @@ class PropertyEditorComponent {
8209
8718
  });
8210
8719
  }
8211
8720
  }
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 }); }
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"] }] }); }
8721
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: PropertyEditorComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
8722
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", 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\">\r\n {{ title }}\r\n <button class=\"property-button\" (click)=\"settings = !settings\">\r\n <div\r\n class=\"icon settings-icon\"\r\n [class]=\"\r\n settings === undefined ? '' : settings ? 'unrotate' : 'rotate'\r\n \"\r\n ></div>\r\n </button>\r\n </p>\r\n <daga-object-editor *ngIf=\"!settings\" [valueSet]=\"valueSet\" />\r\n <daga-property-settings *ngIf=\"settings\" [valueSet]=\"valueSet\" />\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"] }, { kind: "component", type: PropertySettingsComponent, selector: "daga-property-settings", inputs: ["valueSet", "depth"] }] }); }
8214
8723
  }
8215
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: PropertyEditorComponent, decorators: [{
8724
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: PropertyEditorComponent, decorators: [{
8216
8725
  type: Component,
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" }]
8726
+ args: [{ standalone: true, selector: 'daga-property-editor', imports: [
8727
+ CommonModule,
8728
+ CollapseButtonComponent,
8729
+ ObjectEditorComponent,
8730
+ PropertySettingsComponent
8731
+ ], 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\">\r\n {{ title }}\r\n <button class=\"property-button\" (click)=\"settings = !settings\">\r\n <div\r\n class=\"icon settings-icon\"\r\n [class]=\"\r\n settings === undefined ? '' : settings ? 'unrotate' : 'rotate'\r\n \"\r\n ></div>\r\n </button>\r\n </p>\r\n <daga-object-editor *ngIf=\"!settings\" [valueSet]=\"valueSet\" />\r\n <daga-property-settings *ngIf=\"settings\" [valueSet]=\"valueSet\" />\r\n </div>\r\n</div>\r\n" }]
8218
8732
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { panel: [{
8219
8733
  type: ViewChild,
8220
8734
  args: ['panel']
@@ -8227,6 +8741,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
8227
8741
  }], valueSet: [{
8228
8742
  type: Input
8229
8743
  }] } });
8744
+ const getStyleClassName = (s) => {
8745
+ return 'property-name-' + s.replace(/\s/g, '');
8746
+ };
8230
8747
 
8231
8748
  /**
8232
8749
  * A provider for the {@link DiagramConfig} associated with a {@link DiagramComponent} context.
@@ -8249,10 +8766,10 @@ class DagaConfigurationService {
8249
8766
  getConfig() {
8250
8767
  return this._config;
8251
8768
  }
8252
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DagaConfigurationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
8253
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DagaConfigurationService }); }
8769
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DagaConfigurationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
8770
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DagaConfigurationService }); }
8254
8771
  }
8255
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DagaConfigurationService, decorators: [{
8772
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DagaConfigurationService, decorators: [{
8256
8773
  type: Injectable
8257
8774
  }] });
8258
8775
 
@@ -8279,10 +8796,10 @@ class DiagramEditorComponent {
8279
8796
  ngAfterViewInit() {
8280
8797
  this.canvasProvider.initCanvasView(this.appendTo.nativeElement);
8281
8798
  }
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 }); }
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" }] }); }
8799
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DiagramEditorComponent, deps: [{ token: DagaConfigurationService }, { token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
8800
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", 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" }] }); }
8284
8801
  }
8285
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DiagramEditorComponent, decorators: [{
8802
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DiagramEditorComponent, decorators: [{
8286
8803
  type: Component,
8287
8804
  args: [{ standalone: true, selector: 'daga-diagram-editor', imports: [
8288
8805
  CommonModule,
@@ -8312,7 +8829,7 @@ const DAGA_FILE_VERSION = 1;
8312
8829
  * @public
8313
8830
  */
8314
8831
  class DagaExporter {
8315
- export(model) {
8832
+ export(model, includeCollabMeta = false) {
8316
8833
  const result = {
8317
8834
  name: model.name,
8318
8835
  type: model.type,
@@ -8322,7 +8839,14 @@ class DagaExporter {
8322
8839
  nodes: [],
8323
8840
  connections: [],
8324
8841
  data: model.valueSet.getValues(),
8325
- logicalClock: model.logicalClock
8842
+ ...(includeCollabMeta
8843
+ ? {
8844
+ collabMeta: {
8845
+ logicalClock: model.logicalClock,
8846
+ dataTimestamps: model.valueSet.getTimestamps()
8847
+ }
8848
+ }
8849
+ : {})
8326
8850
  };
8327
8851
  if (model.id) {
8328
8852
  result.id = model.id;
@@ -8330,7 +8854,9 @@ class DagaExporter {
8330
8854
  if (model.description) {
8331
8855
  result.description = model.description;
8332
8856
  }
8333
- for (const node of model.nodes.filter((e) => !e.removed)) {
8857
+ for (const node of model.nodes) {
8858
+ if (!includeCollabMeta && node.removed)
8859
+ continue;
8334
8860
  const sections = [];
8335
8861
  for (const section of node.sections) {
8336
8862
  const ports = [];
@@ -8339,7 +8865,17 @@ class DagaExporter {
8339
8865
  id: port.id,
8340
8866
  coords: roundPoint(port.coords),
8341
8867
  direction: port.direction,
8342
- label: port.label?.text || ''
8868
+ label: port.label?.text || '',
8869
+ ...(includeCollabMeta
8870
+ ? {
8871
+ collabMeta: {
8872
+ removed: port.removed,
8873
+ selfRemoved: port.selfRemoved,
8874
+ selfRemovedTimestamp: port.selfRemovedTimestamp,
8875
+ ...exportLabelCollabMeta(port)
8876
+ }
8877
+ }
8878
+ : {})
8343
8879
  });
8344
8880
  }
8345
8881
  sections.push({
@@ -8350,7 +8886,17 @@ class DagaExporter {
8350
8886
  indexYInNode: section.indexYInNode,
8351
8887
  coords: roundPoint(section.coords),
8352
8888
  width: section.width,
8353
- height: section.height
8889
+ height: section.height,
8890
+ ...(includeCollabMeta
8891
+ ? {
8892
+ collabMeta: {
8893
+ removed: section.removed,
8894
+ selfRemoved: section.selfRemoved,
8895
+ selfRemovedTimestamp: section.selfRemovedTimestamp,
8896
+ ...exportLabelCollabMeta(section)
8897
+ }
8898
+ }
8899
+ : {})
8354
8900
  });
8355
8901
  }
8356
8902
  const ports = [];
@@ -8359,10 +8905,20 @@ class DagaExporter {
8359
8905
  id: port.id,
8360
8906
  coords: roundPoint(port.coords),
8361
8907
  direction: port.direction,
8362
- label: port.label?.text || ''
8908
+ label: port.label?.text || '',
8909
+ ...(includeCollabMeta
8910
+ ? {
8911
+ collabMeta: {
8912
+ removed: port.removed,
8913
+ selfRemoved: port.selfRemoved,
8914
+ selfRemovedTimestamp: port.selfRemovedTimestamp,
8915
+ ...exportLabelCollabMeta(port)
8916
+ }
8917
+ }
8918
+ : {})
8363
8919
  });
8364
8920
  }
8365
- const newNode = {
8921
+ result.nodes.push({
8366
8922
  id: node.id,
8367
8923
  type: node.type.id,
8368
8924
  sections,
@@ -8371,14 +8927,24 @@ class DagaExporter {
8371
8927
  coords: roundPoint(node.coords),
8372
8928
  width: node.width,
8373
8929
  height: node.height,
8374
- data: node.valueSet.getValues()
8375
- };
8376
- if (node.coordsTimestamp) {
8377
- newNode.coordsTimestamp = node.coordsTimestamp;
8378
- }
8379
- result.nodes.push(newNode);
8930
+ data: node.valueSet.getValues(),
8931
+ ...(includeCollabMeta
8932
+ ? {
8933
+ collabMeta: {
8934
+ removed: node.removed,
8935
+ selfRemoved: node.selfRemoved,
8936
+ selfRemovedTimestamp: node.selfRemovedTimestamp,
8937
+ geometryTimestamp: node.geometryTimestamp,
8938
+ dataTimestamps: node.valueSet.getTimestamps(),
8939
+ ...exportLabelCollabMeta(node)
8940
+ }
8941
+ }
8942
+ : {})
8943
+ });
8380
8944
  }
8381
- for (const connection of model.connections.filter((e) => !e.removed)) {
8945
+ for (const connection of model.connections) {
8946
+ if (!includeCollabMeta && connection.removed)
8947
+ continue;
8382
8948
  result.connections.push({
8383
8949
  id: connection.id,
8384
8950
  type: connection.type.id,
@@ -8388,12 +8954,36 @@ class DagaExporter {
8388
8954
  middleLabel: connection.middleLabel,
8389
8955
  endLabel: connection.endLabel,
8390
8956
  points: connection.points,
8391
- data: connection.valueSet.getValues()
8957
+ data: connection.valueSet.getValues(),
8958
+ ...(includeCollabMeta
8959
+ ? {
8960
+ collabMeta: {
8961
+ removed: connection.removed,
8962
+ selfRemoved: connection.selfRemoved,
8963
+ selfRemovedTimestamp: connection.selfRemovedTimestamp,
8964
+ dataTimestamps: connection.valueSet.getTimestamps()
8965
+ }
8966
+ }
8967
+ : {})
8392
8968
  });
8393
8969
  }
8394
8970
  return result;
8395
8971
  }
8396
8972
  }
8973
+ function exportLabelCollabMeta({ label }) {
8974
+ if (label) {
8975
+ return {
8976
+ label: {
8977
+ removed: label.removed,
8978
+ selfRemoved: label.selfRemoved,
8979
+ selfRemovedTimestamp: label.selfRemovedTimestamp,
8980
+ textTimestamp: label.textTimestamp
8981
+ }
8982
+ };
8983
+ }
8984
+ else
8985
+ return {};
8986
+ }
8397
8987
 
8398
8988
  /**
8399
8989
  * Importer which imports a diagram from its daga model representation.
@@ -8411,9 +9001,6 @@ class DagaImporter {
8411
9001
  model.type = data.type;
8412
9002
  model.createdAt = new Date(data.createdAt);
8413
9003
  model.updatedAt = new Date(data.updatedAt);
8414
- if (data.logicalClock) {
8415
- model.logicalClock = data.logicalClock;
8416
- }
8417
9004
  for (const node of data.nodes || []) {
8418
9005
  const newNodeType = model.nodes.types.get(node.type);
8419
9006
  if (newNodeType) {
@@ -8421,9 +9008,6 @@ class DagaImporter {
8421
9008
  model.nodes.add(newNode);
8422
9009
  newNode.width = node.width;
8423
9010
  newNode.height = node.height;
8424
- if (node.coordsTimestamp) {
8425
- newNode.coordsTimestamp = node.coordsTimestamp;
8426
- }
8427
9011
  if (node.label) {
8428
9012
  // add node label
8429
9013
  if (newNodeType.label) {
@@ -8504,6 +9088,18 @@ class DagaImporter {
8504
9088
  }
8505
9089
  ++portCounter;
8506
9090
  }
9091
+ if (port.collabMeta) {
9092
+ newPort.selfRemoved = port.collabMeta.selfRemoved;
9093
+ newPort.selfRemovedTimestamp =
9094
+ port.collabMeta.selfRemovedTimestamp;
9095
+ importLabelCollabMeta(newPort.label, port.collabMeta.label);
9096
+ }
9097
+ }
9098
+ if (section.collabMeta) {
9099
+ newSection.selfRemoved = section.collabMeta.selfRemoved;
9100
+ newSection.selfRemovedTimestamp =
9101
+ section.collabMeta.selfRemovedTimestamp;
9102
+ importLabelCollabMeta(newSection.label, section.collabMeta.label);
8507
9103
  }
8508
9104
  }
8509
9105
  let portCounter = 0;
@@ -8550,10 +9146,22 @@ class DagaImporter {
8550
9146
  }
8551
9147
  ++portCounter;
8552
9148
  }
9149
+ if (port.collabMeta) {
9150
+ newPort.selfRemoved = port.collabMeta.selfRemoved;
9151
+ newPort.selfRemovedTimestamp = port.collabMeta.selfRemovedTimestamp;
9152
+ importLabelCollabMeta(newPort.label, port.collabMeta.label);
9153
+ }
8553
9154
  }
8554
9155
  if (node.data) {
8555
9156
  newNode.valueSet.setValues(node.data);
8556
9157
  }
9158
+ if (node.collabMeta) {
9159
+ newNode.selfRemoved = node.collabMeta.selfRemoved;
9160
+ newNode.selfRemovedTimestamp = node.collabMeta.selfRemovedTimestamp;
9161
+ newNode.geometryTimestamp = node.collabMeta.geometryTimestamp;
9162
+ importLabelCollabMeta(newNode.label, node.collabMeta.label);
9163
+ newNode.valueSet.setTimestamps(node.collabMeta.dataTimestamps);
9164
+ }
8557
9165
  }
8558
9166
  }
8559
9167
  for (const connection of data.connections || []) {
@@ -8568,15 +9176,32 @@ class DagaImporter {
8568
9176
  if (connection.data) {
8569
9177
  newConnection.valueSet.setValues(connection.data);
8570
9178
  }
9179
+ if (connection.collabMeta) {
9180
+ newConnection.selfRemoved = connection.collabMeta.selfRemoved;
9181
+ newConnection.selfRemovedTimestamp =
9182
+ connection.collabMeta.selfRemovedTimestamp;
9183
+ newConnection.valueSet.setTimestamps(connection.collabMeta.dataTimestamps);
9184
+ }
8571
9185
  }
8572
9186
  }
8573
9187
  if (data.data) {
8574
9188
  model.valueSet.setValues(data.data);
8575
9189
  }
9190
+ if (data.collabMeta) {
9191
+ model.logicalClock = data.collabMeta.logicalClock;
9192
+ model.valueSet.setTimestamps(data.collabMeta.dataTimestamps);
9193
+ }
8576
9194
  model.canvas?.updateModelInView();
8577
9195
  return model;
8578
9196
  }
8579
9197
  }
9198
+ function importLabelCollabMeta(newLabel, labelCollabMeta) {
9199
+ if (newLabel && labelCollabMeta) {
9200
+ newLabel.selfRemoved = labelCollabMeta.selfRemoved;
9201
+ newLabel.selfRemovedTimestamp = labelCollabMeta.selfRemovedTimestamp;
9202
+ newLabel.textTimestamp = labelCollabMeta.textTimestamp;
9203
+ }
9204
+ }
8580
9205
 
8581
9206
  /**
8582
9207
  * The context of a diagram. Every separate diagram must be contained within its own {@link DiagramComponent} and every {@link DiagramComponent} must contain its own {@link DiagramEditorComponent}.
@@ -8635,10 +9260,10 @@ class DiagramComponent {
8635
9260
  this.modelChange.emit(exportedModel);
8636
9261
  });
8637
9262
  }
8638
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DiagramComponent, deps: [{ token: DagaConfigurationService }, { token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
8639
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.1.2", type: DiagramComponent, isStandalone: true, selector: "daga-diagram", inputs: { config: "config", model: "model" }, outputs: { modelChange: "modelChange" }, providers: [CanvasProviderService, DagaConfigurationService], ngImport: i0, template: `<ng-content />`, isInline: true }); }
9263
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DiagramComponent, deps: [{ token: DagaConfigurationService }, { token: CanvasProviderService }], target: i0.ɵɵFactoryTarget.Component }); }
9264
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", type: DiagramComponent, isStandalone: true, selector: "daga-diagram", inputs: { config: "config", model: "model" }, outputs: { modelChange: "modelChange" }, providers: [CanvasProviderService, DagaConfigurationService], ngImport: i0, template: `<ng-content />`, isInline: true }); }
8640
9265
  }
8641
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DiagramComponent, decorators: [{
9266
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DiagramComponent, decorators: [{
8642
9267
  type: Component,
8643
9268
  args: [{
8644
9269
  standalone: true,
@@ -8655,8 +9280,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
8655
9280
  }] } });
8656
9281
 
8657
9282
  class DagaModule {
8658
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DagaModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
8659
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.1.2", ngImport: i0, type: DagaModule, imports: [CollapseButtonComponent,
9283
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DagaModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
9284
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.1", ngImport: i0, type: DagaModule, imports: [CollapseButtonComponent,
8660
9285
  DiagramButtonsComponent,
8661
9286
  DiagramComponent,
8662
9287
  DiagramEditorComponent,
@@ -8668,7 +9293,7 @@ class DagaModule {
8668
9293
  PropertyEditorComponent,
8669
9294
  CommonModule,
8670
9295
  FormsModule], exports: [DiagramComponent, DiagramEditorComponent] }); }
8671
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DagaModule, providers: [DagaConfigurationService, CanvasProviderService], imports: [CollapseButtonComponent,
9296
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DagaModule, providers: [DagaConfigurationService, CanvasProviderService], imports: [CollapseButtonComponent,
8672
9297
  DiagramButtonsComponent,
8673
9298
  DiagramEditorComponent,
8674
9299
  ErrorsComponent,
@@ -8680,7 +9305,7 @@ class DagaModule {
8680
9305
  CommonModule,
8681
9306
  FormsModule] }); }
8682
9307
  }
8683
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: DagaModule, decorators: [{
9308
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: DagaModule, decorators: [{
8684
9309
  type: NgModule,
8685
9310
  args: [{
8686
9311
  declarations: [],
@@ -8825,7 +9450,7 @@ class CollabClient {
8825
9450
  }
8826
9451
  canvas.collabEngine.isInRoom = true;
8827
9452
  const refId = v4();
8828
- const initialModel = new DagaExporter().export(canvas.model);
9453
+ const initialModel = new DagaExporter().export(canvas.model, true);
8829
9454
  this.send({
8830
9455
  type: 'CREA',
8831
9456
  clientId: this.clientId,
@@ -8915,5 +9540,5 @@ function now() {
8915
9540
  * Generated bundle index. Do not edit.
8916
9541
  */
8917
9542
 
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 };
9543
+ 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, ObjectEditorComponent, PaletteComponent, PriorityLayout, Property, PropertyEditorComponent, PropertySet, RemoveAction, SetGeometryAction, Side, TextListEditorComponent, TextMapEditorComponent, Type, UpdateValuesAction, ValueSet, VerticalAlign, VerticalLayout, layouts };
8919
9544
  //# sourceMappingURL=metadev-daga.mjs.map