@yuuvis/client-framework 2.4.0 → 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/fesm2022/yuuvis-client-framework-forms.mjs +124 -109
  2. package/fesm2022/yuuvis-client-framework-forms.mjs.map +1 -1
  3. package/fesm2022/yuuvis-client-framework-list.mjs +38 -6
  4. package/fesm2022/yuuvis-client-framework-list.mjs.map +1 -1
  5. package/fesm2022/yuuvis-client-framework-metadata-form-defaults.mjs +1 -1
  6. package/fesm2022/yuuvis-client-framework-metadata-form-defaults.mjs.map +1 -1
  7. package/fesm2022/yuuvis-client-framework-object-relationship.mjs +72 -50
  8. package/fesm2022/yuuvis-client-framework-object-relationship.mjs.map +1 -1
  9. package/fesm2022/yuuvis-client-framework-renderer.mjs +81 -6
  10. package/fesm2022/yuuvis-client-framework-renderer.mjs.map +1 -1
  11. package/fesm2022/yuuvis-client-framework-tile-list.mjs +137 -177
  12. package/fesm2022/yuuvis-client-framework-tile-list.mjs.map +1 -1
  13. package/forms/lib/elements/data-grid/data-grid/data-grid.component.d.ts +2 -2
  14. package/forms/lib/elements/organization/organization.component.d.ts +6 -11
  15. package/forms/lib/elements/organization-set/organization-set.component.d.ts +3 -5
  16. package/lib/assets/i18n/de.json +2 -0
  17. package/lib/assets/i18n/en.json +2 -0
  18. package/list/index.d.ts +1 -0
  19. package/list/lib/list-tile/list-tile.component.d.ts +14 -0
  20. package/list/lib/list.component.d.ts +1 -0
  21. package/list/lib/list.module.d.ts +2 -1
  22. package/object-relationship/lib/actions/add-relationship/add-relationship.component.d.ts +3 -1
  23. package/object-relationship/lib/object-relationship.component.d.ts +2 -0
  24. package/object-relationship/lib/object-relationship.const.d.ts +4 -0
  25. package/package.json +12 -12
  26. package/renderer/lib/property-renderer/organization.renderer.d.ts +4 -1
  27. package/tile-list/index.d.ts +1 -1
  28. package/tile-list/lib/tile-actions-menu/tile-actions-menu.component.d.ts +11 -0
  29. package/tile-list/lib/tile-list/tile-list.component.d.ts +18 -24
  30. package/tile-list/lib/tile/tile.component.d.ts +0 -22
@@ -32,20 +32,25 @@ import { SnackBarService } from '@yuuvis/client-framework';
32
32
  const ROOT_NODE_SETTINGS = {
33
33
  size: 30,
34
34
  borderWidth: 4,
35
- borderWidthSelected: 6,
35
+ borderWidthSelected: 6
36
36
  };
37
37
  const ROOT_NODE_FONT_SETTINGS = {
38
- size: 14,
38
+ size: 14
39
39
  };
40
40
  const NODE_SETTINGS = {
41
- borderWidthSelected: 2,
41
+ borderWidthSelected: 2
42
42
  };
43
43
  const NODE_FONT_SETTINGS = {
44
- size: 12,
44
+ size: 12
45
45
  };
46
46
  const EDGE_SETTINGS = {};
47
47
  const EDGE_FONT_SETTINGS = {
48
- size: 10,
48
+ size: 10
49
+ };
50
+ // add relationship dialog settings
51
+ const ADD_RELATIONSHIP_DIALOG_OPTIONS = {
52
+ width: '500px',
53
+ maxWidth: '90vw'
49
54
  };
50
55
 
51
56
  class ObjectRelationshipService {
@@ -86,6 +91,7 @@ class ObjectRelationshipService {
86
91
  this.busy.set(true);
87
92
  // search for relations that have this object as source or target
88
93
  const query = `SELECT * FROM ${SystemType.RELATIONSHIP} WHERE ${RelationshipTypeField.SOURCE_ID} = '${originId}' OR ${RelationshipTypeField.TARGET_ID} = '${originId}'`;
94
+ // TODO: What about multi-page results?
89
95
  return this.#search
90
96
  .searchCmis(query)
91
97
  .pipe(switchMap((resRelations) => {
@@ -100,7 +106,9 @@ class ObjectRelationshipService {
100
106
  const ids = Object.keys(o)
101
107
  .map((id) => `'${id}'`)
102
108
  .join(',');
103
- const q = `SELECT * FROM system:object WHERE ${BaseObjectTypeField.OBJECT_ID} IN (${ids})`;
109
+ // having no relations would end up in an empty IN () statement which is invalid
110
+ // but as we need to fetch the origin objects anyway we can add it manually
111
+ const q = `SELECT * FROM system:object WHERE ${BaseObjectTypeField.OBJECT_ID} IN (${ids.length > 0 ? ids : `'${originId}'`})`;
104
112
  return this.#search.searchCmis(q).pipe(map((resObjects) => ({
105
113
  relations: resRelations,
106
114
  objects: resObjects
@@ -131,6 +139,7 @@ class ObjectRelationshipService {
131
139
  this.busy.set(false);
132
140
  if (success) {
133
141
  this.#eventService.trigger(YuvEventType.RELATIONSHIP_DELETED, { relationId });
142
+ // TODO: trigger the event will reload the resource anyway (see constructor)
134
143
  if (reloadResource) {
135
144
  this.#relationsResource.reload();
136
145
  }
@@ -170,7 +179,6 @@ class NodeSummaryComponent {
170
179
  #system;
171
180
  #router;
172
181
  openObject(link) {
173
- console.log('Navigating to link:', link);
174
182
  this.#router.navigateByUrl(link);
175
183
  }
176
184
  #propertyToRendererInput(propertyName, object) {
@@ -300,7 +308,7 @@ class RelationshipTargetSearchComponent {
300
308
  useExisting: forwardRef(() => RelationshipTargetSearchComponent),
301
309
  multi: true
302
310
  }
303
- ], ngImport: i0, template: "<mat-form-field>\n <mat-label>{{ 'yuv.object-relationship.add-relationship.target-search.label' | translate }}</mat-label>\n\n <yuv-autocomplete\n [required]=\"true\"\n [busy]=\"busy()\"\n [formControl]=\"acFormControl\"\n [autocompleteValues]=\"autocompleteRes\"\n [forceSelection]=\"true\"\n (autocompleteFnc)=\"autocompleteFn($event)\"\n [multiple]=\"true\"\n [maxItems]=\"1\"\n >\n <!-- template for chip -->\n <ng-template #chipTemplate let-item>\n <div class=\"label\">{{ item.label }}</div>\n <!-- <div class=\"meta\">{{ item.value[metaFieldProperty] }}</div> -->\n </ng-template>\n </yuv-autocomplete>\n</mat-form-field>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: YuvAutocompleteModule }, { kind: "component", type: i1$1.AutocompleteComponent, selector: "yuv-autocomplete", inputs: ["ariaLabel", "busy", "multiple", "distinctValues", "addOnBlur", "minLength", "maxItems", "forceSelection", "autocompleteValues"], outputs: ["autocompleteFnc", "acBlur"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }] }); }
311
+ ], ngImport: i0, template: "<mat-form-field>\n <mat-label>{{ 'yuv.object-relationship.add-relationship.target-search.label' | translate }}</mat-label>\n\n <yuv-autocomplete\n [required]=\"true\"\n [busy]=\"busy()\"\n [formControl]=\"acFormControl\"\n [autocompleteValues]=\"autocompleteRes\"\n [forceSelection]=\"true\"\n (autocompleteFnc)=\"autocompleteFn($event)\"\n [multiple]=\"true\"\n [maxItems]=\"1\"\n >\n <!-- template for chip -->\n <ng-template #chipTemplate let-item>\n <div class=\"label\">{{ item.label }}</div>\n <!-- <div class=\"meta\">{{ item.value[metaFieldProperty] }}</div> -->\n </ng-template>\n </yuv-autocomplete>\n</mat-form-field>\n", styles: [":host mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: YuvAutocompleteModule }, { kind: "component", type: i1$1.AutocompleteComponent, selector: "yuv-autocomplete", inputs: ["ariaLabel", "busy", "multiple", "distinctValues", "addOnBlur", "minLength", "maxItems", "forceSelection", "autocompleteValues"], outputs: ["autocompleteFnc", "acBlur"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }] }); }
304
312
  }
305
313
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: RelationshipTargetSearchComponent, decorators: [{
306
314
  type: Component,
@@ -310,7 +318,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
310
318
  useExisting: forwardRef(() => RelationshipTargetSearchComponent),
311
319
  multi: true
312
320
  }
313
- ], template: "<mat-form-field>\n <mat-label>{{ 'yuv.object-relationship.add-relationship.target-search.label' | translate }}</mat-label>\n\n <yuv-autocomplete\n [required]=\"true\"\n [busy]=\"busy()\"\n [formControl]=\"acFormControl\"\n [autocompleteValues]=\"autocompleteRes\"\n [forceSelection]=\"true\"\n (autocompleteFnc)=\"autocompleteFn($event)\"\n [multiple]=\"true\"\n [maxItems]=\"1\"\n >\n <!-- template for chip -->\n <ng-template #chipTemplate let-item>\n <div class=\"label\">{{ item.label }}</div>\n <!-- <div class=\"meta\">{{ item.value[metaFieldProperty] }}</div> -->\n </ng-template>\n </yuv-autocomplete>\n</mat-form-field>\n" }]
321
+ ], template: "<mat-form-field>\n <mat-label>{{ 'yuv.object-relationship.add-relationship.target-search.label' | translate }}</mat-label>\n\n <yuv-autocomplete\n [required]=\"true\"\n [busy]=\"busy()\"\n [formControl]=\"acFormControl\"\n [autocompleteValues]=\"autocompleteRes\"\n [forceSelection]=\"true\"\n (autocompleteFnc)=\"autocompleteFn($event)\"\n [multiple]=\"true\"\n [maxItems]=\"1\"\n >\n <!-- template for chip -->\n <ng-template #chipTemplate let-item>\n <div class=\"label\">{{ item.label }}</div>\n <!-- <div class=\"meta\">{{ item.value[metaFieldProperty] }}</div> -->\n </ng-template>\n </yuv-autocomplete>\n</mat-form-field>\n", styles: [":host mat-form-field{width:100%}\n"] }]
314
322
  }], ctorParameters: () => [] });
315
323
 
316
324
  class AddRelationshipComponent {
@@ -320,6 +328,7 @@ class AddRelationshipComponent {
320
328
  this.#dialogRef = inject((MatDialogRef));
321
329
  this.#dmsService = inject(DmsService);
322
330
  this.#eventService = inject(EventService);
331
+ this.translate = inject(TranslateService);
323
332
  this.object = input(this.#dialogData.object);
324
333
  this.config = input(this.#dialogData.config);
325
334
  this.supportedRelationships = computed(() => {
@@ -358,6 +367,7 @@ class AddRelationshipComponent {
358
367
  targetObject: new FormControl(null, { nonNullable: true, validators: [Validators.required] })
359
368
  }, { validators: this.#relationMatchesTargetObjectValidator });
360
369
  this.busy = signal(false);
370
+ this.error = signal(null);
361
371
  }
362
372
  #system;
363
373
  #dialogData;
@@ -369,10 +379,16 @@ class AddRelationshipComponent {
369
379
  return o1?.id === o2?.id;
370
380
  }
371
381
  submit() {
382
+ this.error.set(null);
383
+ this.busy.set(true);
372
384
  this.#addRelationship(this.form.value.relation.id, this.form.value.targetObject).subscribe({
373
- next: () => this.close(),
385
+ next: () => {
386
+ this.busy.set(false);
387
+ this.close();
388
+ },
374
389
  error: () => {
375
- // TODO: Handle error appropriately
390
+ this.error.set(this.translate.instant('yuv.object-relationship.add-relation.error-message'));
391
+ this.busy.set(false);
376
392
  }
377
393
  });
378
394
  }
@@ -389,7 +405,7 @@ class AddRelationshipComponent {
389
405
  this.#dialogRef.close();
390
406
  }
391
407
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AddRelationshipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
392
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: AddRelationshipComponent, isStandalone: true, selector: "yuv-add-relationship", inputs: { object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<yuv-dialog class=\"not-separated\" [headertitel]=\"'yuv.object-relationship.add-relationship.headline' | translate\">\n <main [yuvBusyOverlay]=\"busy()\">\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n @let sn = sourceNode();\n @if (sn) {\n <div class=\"source\">\n {{ sn.title }}\n @if (sn.description) {\n <em>{{ sn.description }}</em>\n }\n </div>\n <div class=\"line\"></div>\n }\n <mat-form-field>\n <mat-label>{{ 'cm.app.cases.action.add-link.search.relation.label' | translate }}</mat-label>\n <mat-select formControlName=\"relation\" [compareWith]=\"optionCompareWith\">\n @for (r of supportedRelationships(); track r.id) {\n <mat-option [value]=\"r\">{{ r.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <div class=\"line\"></div>\n\n <yuv-relationship-target-search formControlName=\"targetObject\"\n [config]=\"config()\"\n [availableTargetTypes]=\"availableTargetTypes()\"\n [supportedRelationships]=\"supportedRelationships()\"\n ></yuv-relationship-target-search>\n\n @if (form.hasError('invalidRelationTargetCombination') && (form.touched || form.dirty)) {\n <mat-error class=\"error\">{{ 'yuv.object-relationship.add-relationship.error.invalidRelationTargetCombination' | translate }}</mat-error>\n }\n </form>\n </main>\n\n <footer>\n <button ymtButton=\"secondary\" type=\"button\" (click)=\"close()\" [disabled]=\"busy()\">\n {{ 'yuv.object-relationship.add-relationship.action.cancel' | translate }}\n </button>\n <button ymtButton=\"primary\" type=\"button\" (click)=\"submit()\" [disabled]=\"form.invalid || busy()\">\n {{ 'yuv.object-relationship.add-relationship.action.submit' | translate }}\n </button>\n </footer>\n</yuv-dialog>\n", styles: [":host main form{padding:var(--ymt-spacing-m);display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);align-items:center}:host main form .source{border:2px solid var(--ymt-outline);border-radius:var(--ymt-corner-s);padding:var(--ymt-spacing-xs);display:flex;flex-direction:column}:host main form .source em{font-style:normal;font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}:host main form .line{width:2px;background-color:var(--ymt-outline);min-height:2em;position:relative}:host main form .line:before{content:\"\";position:absolute;transform:rotate(135deg);width:8px;inset-block-end:0;translate:-3px 2px;height:8px;border:4px solid transparent;border-block-start-color:var(--ymt-outline);border-inline-end-color:var(--ymt-outline)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "component", type: DialogComponent, selector: "yuv-dialog", inputs: ["headertitel"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "directive", type: YmtButtonDirective, selector: "button[ymtButton], a[ymtButton]", inputs: ["ymtButton", "disabled", "aria-disabled", "disableRipple", "disabledInteractive", "button-size"] }, { kind: "component", type: RelationshipTargetSearchComponent, selector: "yuv-relationship-target-search", inputs: ["supportedRelationships", "availableTargetTypes", "config"] }, { kind: "directive", type: BusyOverlayDirective, selector: "[yuvBusyOverlay]", inputs: ["yuvBusyOverlay", "yuvBusyOverlayConfig"] }] }); }
408
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: AddRelationshipComponent, isStandalone: true, selector: "yuv-add-relationship", inputs: { object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<yuv-dialog class=\"not-separated\" [headertitel]=\"'yuv.object-relationship.add-relationship.headline' | translate\">\n <main [yuvBusyOverlay]=\"busy()\">\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n @let sn = sourceNode();\n @if (sn) {\n <div class=\"source\">\n {{ sn.title }}\n @if (sn.description) {\n <em>{{ sn.description }}</em>\n }\n </div>\n <div class=\"line\"></div>\n }\n <mat-form-field>\n <mat-label>{{ 'cm.app.cases.action.add-link.search.relation.label' | translate }}</mat-label>\n <mat-select formControlName=\"relation\" [compareWith]=\"optionCompareWith\">\n @for (r of supportedRelationships(); track r.id) {\n <mat-option [value]=\"r\">{{ r.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <div class=\"line\"></div>\n\n <yuv-relationship-target-search formControlName=\"targetObject\"\n [config]=\"config()\"\n [availableTargetTypes]=\"availableTargetTypes()\"\n [supportedRelationships]=\"supportedRelationships()\"\n ></yuv-relationship-target-search>\n\n @if (form.hasError('invalidRelationTargetCombination') && (form.touched || form.dirty)) {\n <mat-error class=\"error\">{{ 'yuv.object-relationship.add-relationship.error.invalidRelationTargetCombination' | translate }}</mat-error>\n }\n \n @if (error()) {\n <div class=\"error\">{{ error() }}</div>\n }\n </form>\n </main>\n\n <footer>\n <button ymtButton=\"secondary\" type=\"button\" (click)=\"close()\" [disabled]=\"busy()\">\n {{ 'yuv.object-relationship.add-relationship.action.cancel' | translate }}\n </button>\n <button ymtButton=\"primary\" type=\"button\" (click)=\"submit()\" [disabled]=\"form.invalid || busy()\">\n {{ 'yuv.object-relationship.add-relationship.action.submit' | translate }}\n </button>\n </footer>\n</yuv-dialog>\n", styles: [":host .error{background-color:var(--ymt-danger-container);color:var(--ymt-on-danger-container);width:100%;padding:var(--ymt-spacing-m);border-radius:var(--ymt-spacing-xs)}:host main form{padding:var(--ymt-spacing-m);display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);align-items:center}:host main form .source{border:2px solid var(--ymt-outline);border-radius:var(--ymt-corner-s);padding:var(--ymt-spacing-xs);display:flex;flex-direction:column}:host main form .source em{font-style:normal;font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}:host main form yuv-relationship-target-search,:host main form mat-form-field{width:100%}:host main form .line{width:2px;background-color:var(--ymt-outline);min-height:2em;position:relative}:host main form .line:before{content:\"\";position:absolute;transform:rotate(135deg);width:8px;inset-block-end:0;translate:-3px 2px;height:8px;border:4px solid transparent;border-block-start-color:var(--ymt-outline);border-inline-end-color:var(--ymt-outline)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "component", type: DialogComponent, selector: "yuv-dialog", inputs: ["headertitel"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "directive", type: YmtButtonDirective, selector: "button[ymtButton], a[ymtButton]", inputs: ["ymtButton", "disabled", "aria-disabled", "disableRipple", "disabledInteractive", "button-size"] }, { kind: "component", type: RelationshipTargetSearchComponent, selector: "yuv-relationship-target-search", inputs: ["supportedRelationships", "availableTargetTypes", "config"] }, { kind: "directive", type: BusyOverlayDirective, selector: "[yuvBusyOverlay]", inputs: ["yuvBusyOverlay", "yuvBusyOverlayConfig"] }] }); }
393
409
  }
394
410
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: AddRelationshipComponent, decorators: [{
395
411
  type: Component,
@@ -404,7 +420,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
404
420
  YmtButtonDirective,
405
421
  RelationshipTargetSearchComponent,
406
422
  BusyOverlayDirective
407
- ], template: "<yuv-dialog class=\"not-separated\" [headertitel]=\"'yuv.object-relationship.add-relationship.headline' | translate\">\n <main [yuvBusyOverlay]=\"busy()\">\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n @let sn = sourceNode();\n @if (sn) {\n <div class=\"source\">\n {{ sn.title }}\n @if (sn.description) {\n <em>{{ sn.description }}</em>\n }\n </div>\n <div class=\"line\"></div>\n }\n <mat-form-field>\n <mat-label>{{ 'cm.app.cases.action.add-link.search.relation.label' | translate }}</mat-label>\n <mat-select formControlName=\"relation\" [compareWith]=\"optionCompareWith\">\n @for (r of supportedRelationships(); track r.id) {\n <mat-option [value]=\"r\">{{ r.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <div class=\"line\"></div>\n\n <yuv-relationship-target-search formControlName=\"targetObject\"\n [config]=\"config()\"\n [availableTargetTypes]=\"availableTargetTypes()\"\n [supportedRelationships]=\"supportedRelationships()\"\n ></yuv-relationship-target-search>\n\n @if (form.hasError('invalidRelationTargetCombination') && (form.touched || form.dirty)) {\n <mat-error class=\"error\">{{ 'yuv.object-relationship.add-relationship.error.invalidRelationTargetCombination' | translate }}</mat-error>\n }\n </form>\n </main>\n\n <footer>\n <button ymtButton=\"secondary\" type=\"button\" (click)=\"close()\" [disabled]=\"busy()\">\n {{ 'yuv.object-relationship.add-relationship.action.cancel' | translate }}\n </button>\n <button ymtButton=\"primary\" type=\"button\" (click)=\"submit()\" [disabled]=\"form.invalid || busy()\">\n {{ 'yuv.object-relationship.add-relationship.action.submit' | translate }}\n </button>\n </footer>\n</yuv-dialog>\n", styles: [":host main form{padding:var(--ymt-spacing-m);display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);align-items:center}:host main form .source{border:2px solid var(--ymt-outline);border-radius:var(--ymt-corner-s);padding:var(--ymt-spacing-xs);display:flex;flex-direction:column}:host main form .source em{font-style:normal;font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}:host main form .line{width:2px;background-color:var(--ymt-outline);min-height:2em;position:relative}:host main form .line:before{content:\"\";position:absolute;transform:rotate(135deg);width:8px;inset-block-end:0;translate:-3px 2px;height:8px;border:4px solid transparent;border-block-start-color:var(--ymt-outline);border-inline-end-color:var(--ymt-outline)}\n"] }]
423
+ ], template: "<yuv-dialog class=\"not-separated\" [headertitel]=\"'yuv.object-relationship.add-relationship.headline' | translate\">\n <main [yuvBusyOverlay]=\"busy()\">\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n @let sn = sourceNode();\n @if (sn) {\n <div class=\"source\">\n {{ sn.title }}\n @if (sn.description) {\n <em>{{ sn.description }}</em>\n }\n </div>\n <div class=\"line\"></div>\n }\n <mat-form-field>\n <mat-label>{{ 'cm.app.cases.action.add-link.search.relation.label' | translate }}</mat-label>\n <mat-select formControlName=\"relation\" [compareWith]=\"optionCompareWith\">\n @for (r of supportedRelationships(); track r.id) {\n <mat-option [value]=\"r\">{{ r.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <div class=\"line\"></div>\n\n <yuv-relationship-target-search formControlName=\"targetObject\"\n [config]=\"config()\"\n [availableTargetTypes]=\"availableTargetTypes()\"\n [supportedRelationships]=\"supportedRelationships()\"\n ></yuv-relationship-target-search>\n\n @if (form.hasError('invalidRelationTargetCombination') && (form.touched || form.dirty)) {\n <mat-error class=\"error\">{{ 'yuv.object-relationship.add-relationship.error.invalidRelationTargetCombination' | translate }}</mat-error>\n }\n \n @if (error()) {\n <div class=\"error\">{{ error() }}</div>\n }\n </form>\n </main>\n\n <footer>\n <button ymtButton=\"secondary\" type=\"button\" (click)=\"close()\" [disabled]=\"busy()\">\n {{ 'yuv.object-relationship.add-relationship.action.cancel' | translate }}\n </button>\n <button ymtButton=\"primary\" type=\"button\" (click)=\"submit()\" [disabled]=\"form.invalid || busy()\">\n {{ 'yuv.object-relationship.add-relationship.action.submit' | translate }}\n </button>\n </footer>\n</yuv-dialog>\n", styles: [":host .error{background-color:var(--ymt-danger-container);color:var(--ymt-on-danger-container);width:100%;padding:var(--ymt-spacing-m);border-radius:var(--ymt-spacing-xs)}:host main form{padding:var(--ymt-spacing-m);display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);align-items:center}:host main form .source{border:2px solid var(--ymt-outline);border-radius:var(--ymt-corner-s);padding:var(--ymt-spacing-xs);display:flex;flex-direction:column}:host main form .source em{font-style:normal;font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}:host main form yuv-relationship-target-search,:host main form mat-form-field{width:100%}:host main form .line{width:2px;background-color:var(--ymt-outline);min-height:2em;position:relative}:host main form .line:before{content:\"\";position:absolute;transform:rotate(135deg);width:8px;inset-block-end:0;translate:-3px 2px;height:8px;border:4px solid transparent;border-block-start-color:var(--ymt-outline);border-inline-end-color:var(--ymt-outline)}\n"] }]
408
424
  }] });
409
425
 
410
426
  class ObjectRelationshipGraphComponent {
@@ -435,7 +451,6 @@ class ObjectRelationshipGraphComponent {
435
451
  const rel = this.#relations();
436
452
  if (!rel)
437
453
  return;
438
- // this.#outerObjectNodeIds = rel.objects.items.map((i) => i.fields.get(BaseObjectTypeField.OBJECT_ID) as string);
439
454
  this.#_updateGraph(rel.originId, rel.relations, rel.objects);
440
455
  });
441
456
  /**
@@ -494,6 +509,7 @@ class ObjectRelationshipGraphComponent {
494
509
  addRelationship() {
495
510
  this.#dialog
496
511
  .open(AddRelationshipComponent, {
512
+ ...ADD_RELATIONSHIP_DIALOG_OPTIONS,
497
513
  data: {
498
514
  object: this.selectedObject(),
499
515
  config: this.config()
@@ -507,7 +523,21 @@ class ObjectRelationshipGraphComponent {
507
523
  deleteRelationship() {
508
524
  this.#objectRelationshipService.deleteRelationship(this.selectedRelation().id, false).subscribe((success) => {
509
525
  if (success) {
526
+ const deletedEdge = this.#edges.get(this.selectedRelation().id);
527
+ if (!deletedEdge)
528
+ return;
529
+ const sourceNodeId = deletedEdge.from;
530
+ const targetNodeId = deletedEdge.to;
510
531
  this.#edges.remove(this.selectedRelation().id);
532
+ // check if the source/target node of the deleted edge has other connections to another node in the current graph
533
+ const otherEdgesForSource = this.#edges.get().filter((edge) => edge.from === sourceNodeId || edge.to === sourceNodeId);
534
+ const otherEdgesForTarget = this.#edges.get().filter((edge) => edge.from === targetNodeId || edge.to === targetNodeId);
535
+ if (sourceNodeId && otherEdgesForSource.length === 0) {
536
+ this.#nodes.remove(sourceNodeId);
537
+ }
538
+ if (targetNodeId && otherEdgesForTarget.length === 0) {
539
+ this.#nodes.remove(targetNodeId);
540
+ }
511
541
  }
512
542
  else {
513
543
  this.#snack.danger(this.translate.instant('yuv.object-relationship.delete-relation.error-message'));
@@ -548,7 +578,7 @@ class ObjectRelationshipGraphComponent {
548
578
  const incoming = relations.items.filter((item) => item.fields.get(RelationshipTypeField.TARGET_ID) === originId);
549
579
  const outgoing = relations.items.filter((item) => item.fields.get(RelationshipTypeField.SOURCE_ID) === originId);
550
580
  const rootNode = this.#toNode(originId);
551
- const _nodes = originId === this.#relations()?.originId
581
+ const _nodes = originId === this.#relations()?.originId && this.#nodes.get([originId]).length === 0
552
582
  ? [
553
583
  {
554
584
  ...rootNode,
@@ -565,7 +595,7 @@ class ObjectRelationshipGraphComponent {
565
595
  outgoing.forEach((item) => {
566
596
  const relatedObjectId = item.fields.get(RelationshipTypeField.TARGET_ID);
567
597
  const n = this.#toNode(relatedObjectId);
568
- const e = this.#toEdge(item, originId, relatedObjectId, 'from');
598
+ const e = this.#toEdge(item, originId, relatedObjectId, 'to');
569
599
  if (n) {
570
600
  // check if the node is already in the rendered graph
571
601
  const nodeExists = _nodes.some((node) => node.id === n.id) || this.#nodes.get([n.id]).length > 0;
@@ -580,7 +610,7 @@ class ObjectRelationshipGraphComponent {
580
610
  incoming.forEach((item) => {
581
611
  const relatedObjectId = item.fields.get(RelationshipTypeField.SOURCE_ID);
582
612
  const n = this.#toNode(relatedObjectId);
583
- const e = this.#toEdge(item, relatedObjectId, originId, 'to');
613
+ const e = this.#toEdge(item, relatedObjectId, originId, 'from');
584
614
  if (n) {
585
615
  // check if the node is already in the rendered graph
586
616
  const nodeExists = _nodes.some((node) => node.id === n.id) || this.#nodes.get([n.id]).length > 0;
@@ -714,7 +744,7 @@ class ObjectRelationshipGraphComponent {
714
744
  forceAtlas2Based: {
715
745
  // gravitationalConstant: -50,
716
746
  // centralGravity: 0.01,
717
- springLength: 200,
747
+ springLength: 200
718
748
  }
719
749
  },
720
750
  nodes: {
@@ -727,36 +757,20 @@ class ObjectRelationshipGraphComponent {
727
757
  nodes: this.#nodes,
728
758
  edges: this.#edges
729
759
  }, options);
730
- this.#network.on('selectNode', (params) => {
731
- const selectedNodeId = params.nodes[0];
732
- this.selectedObject.set(this.#objectQA[selectedNodeId]);
733
- });
734
- this.#network.on('deselectNode', (params) => {
735
- this.selectedObject.set(null);
736
- });
737
- this.#network.on('dragEnd', (params) => {
738
- const selectedNodeId = params.nodes[0];
739
- this.selectedObject.set(this.#objectQA[selectedNodeId]);
740
- });
741
- this.#network.on('selectEdge', (params) => {
742
- const selectedEdgeId = params.edges[0];
743
- const nodesSelected = params.nodes.length > 0;
744
- this.selectedRelation.set(params.edges.length === 1 && !nodesSelected ? this.#relationQA[selectedEdgeId] : null);
745
- });
746
- this.#network.on('deselectEdge', (params) => {
747
- const selectedEdgeId = params.edges[0];
748
- const nodesSelected = params.nodes.length > 0;
749
- this.selectedRelation.set(params.edges.length === 1 && !nodesSelected ? this.#relationQA[selectedEdgeId] : null);
750
- });
751
- this.#network.on('click', (params) => {
752
- const selectedNodeId = params.nodes[0];
753
- this.selectedObject.set(this.#objectQA[selectedNodeId]);
754
- });
760
+ this.#network.on('selectNode', (params) => this.#setSelection(params));
761
+ this.#network.on('deselectNode', (params) => this.#setSelection(params));
762
+ this.#network.on('dragEnd', (params) => this.#setSelection(params));
763
+ this.#network.on('selectEdge', (params) => this.#setSelection(params));
764
+ this.#network.on('deselectEdge', (params) => this.#setSelection(params));
765
+ this.#network.on('click', (params) => this.#setSelection(params));
755
766
  this.#network.on('doubleClick', (params) => {
756
- const selectedNodeId = params.nodes[0];
757
- this.#expand(selectedNodeId);
767
+ this.#expand(params.nodes[0]);
758
768
  });
759
769
  }
770
+ #setSelection(params) {
771
+ this.selectedObject.set(params.nodes.length === 1 ? this.#objectQA[params.nodes[0]] : null);
772
+ this.selectedRelation.set(params.edges.length === 1 && params.nodes.length === 0 ? this.#relationQA[params.edges[0]] : null);
773
+ }
760
774
  ngOnDestroy() {
761
775
  if (this.#network) {
762
776
  this.#network.destroy();
@@ -832,7 +846,7 @@ class ObjectRelationshipListComponent {
832
846
  #createLists(objectId, relations, objects) {
833
847
  const incoming = relations.items.filter((item) => item.fields.get(RelationshipTypeField.TARGET_ID) === objectId);
834
848
  const outgoing = relations.items.filter((item) => item.fields.get(RelationshipTypeField.SOURCE_ID) === objectId);
835
- objects.items.forEach((item) => {
849
+ objects?.items.forEach((item) => {
836
850
  const id = item.fields.get(BaseObjectTypeField.OBJECT_ID);
837
851
  this.#objectQA[id] = new DmsObject(item);
838
852
  });
@@ -897,6 +911,10 @@ class ObjectRelationshipComponent {
897
911
  this.#dialog = inject(MatDialog);
898
912
  this.#DEFAULT_MODE = 'graph';
899
913
  this.mode = signal(this.#DEFAULT_MODE);
914
+ this.empty = computed(() => {
915
+ const rel = this.#relations();
916
+ return rel && rel.relations.totalNumItems === 0;
917
+ });
900
918
  /**
901
919
  * ID of the object to display relations for.
902
920
  */
@@ -915,6 +933,7 @@ class ObjectRelationshipComponent {
915
933
  const obj = this.originObject();
916
934
  return obj ? this.#system.getSupportedRelationships(obj) : [];
917
935
  });
936
+ this.busy = this.#objectRelationshipService.busy;
918
937
  this.#relations = this.#objectRelationshipService.relations;
919
938
  this.#relationsEffect = effect(() => {
920
939
  const rel = this.#relations();
@@ -923,7 +942,8 @@ class ObjectRelationshipComponent {
923
942
  return;
924
943
  }
925
944
  const originId = rel.originId;
926
- const originItem = rel.objects.items.find((item) => item.fields.get(BaseObjectTypeField.OBJECT_ID) === originId);
945
+ const originItem = rel.objects?.items.find((item) => item.fields.get(BaseObjectTypeField.OBJECT_ID) === originId);
946
+ // TODO: use linked signal instead
927
947
  this.originObject.set(originItem ? new DmsObject(originItem) : null);
928
948
  });
929
949
  this.relationActions = input(null);
@@ -941,6 +961,7 @@ class ObjectRelationshipComponent {
941
961
  }
942
962
  addRelationship() {
943
963
  this.#dialog.open(AddRelationshipComponent, {
964
+ ...ADD_RELATIONSHIP_DIALOG_OPTIONS,
944
965
  data: {
945
966
  object: this.originObject(),
946
967
  config: this.config()
@@ -948,7 +969,7 @@ class ObjectRelationshipComponent {
948
969
  });
949
970
  }
950
971
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ObjectRelationshipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
951
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: ObjectRelationshipComponent, isStandalone: true, selector: "yuv-object-relationship", inputs: { objectId: { classPropertyName: "objectId", publicName: "objectId", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, relationActions: { classPropertyName: "relationActions", publicName: "relationActions", isSignal: true, isRequired: false, transformFunction: null } }, providers: [ObjectRelationshipService], ngImport: i0, template: "<ymt-pane [plain]=\"true\" [topBarActions]=\"topBarActions\">\n <ymt-pane-body>\n @switch (mode()) {\n @case ('graph') {\n <yuv-object-relationship-graph [config]=\"config()\"></yuv-object-relationship-graph>\n }\n @case ('list') {\n <yuv-object-relationship-list [config]=\"config()\"> </yuv-object-relationship-list>\n }\n }\n </ymt-pane-body>\n</ymt-pane>\n\n<ng-template #topBarActions>\n @if (supportedRelationships().length > 0) {\n <button ymt-icon-button icon-button-size=\"small\" [matTooltip]=\"'yuv.object-relationship.add.tooltip' | translate\" (click)=\"addRelationship()\">\n <mat-icon>add</mat-icon>\n </button>\n }\n <button\n ymt-icon-button\n icon-button-size=\"small\"\n [matTooltip]=\"mode() === 'list' ? ('yuv.object-relationship.mode.graph.tooltip' | translate) : ('yuv.object-relationship.mode.list.tooltip' | translate)\"\n (click)=\"toggleMode()\"\n >\n <mat-icon>{{ mode() === 'list' ? 'graph_3' : 'list' }}</mat-icon>\n </button>\n</ng-template>\n", styles: [":host{display:block;position:relative;height:100%;background-color:var(--ymt-surface)}:host .toggle{position:absolute;inset-block-start:var(--ymt-spacing-s);inset-inline-end:var(--ymt-spacing-s);z-index:10;background-color:var(--ymt-surface);padding:var(--ymt-spacing-xs);border-radius:var(--ymt-corner-full)}:host yuv-object-relationship-list,:host yuv-object-relationship-graph{height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "ngmodule", type: YmtPanesModule }, { kind: "component", type: i4$1.YmtPaneComponent, selector: "ymt-pane", inputs: ["topBarActions", "plain", "mode", "modeAlign"], outputs: ["paneToggled", "navigationClicked"] }, { kind: "component", type: i4$1.YmtPaneBodyComponent, selector: "ymt-pane-body" }, { kind: "directive", type: YmtIconButtonDirective, selector: "button[ymtIconButton],button[ymt-icon-button],a[ymtIconButton],a[ymt-icon-button]", inputs: ["disabled", "disableRipple", "aria-disabled", "disabledInteractive", "icon-button-size"] }, { kind: "component", type: ObjectRelationshipGraphComponent, selector: "yuv-object-relationship-graph", inputs: ["config"], outputs: ["objectSelected", "relationSelected"] }, { kind: "component", type: ObjectRelationshipListComponent, selector: "yuv-object-relationship-list", inputs: ["config"] }] }); }
972
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: ObjectRelationshipComponent, isStandalone: true, selector: "yuv-object-relationship", inputs: { objectId: { classPropertyName: "objectId", publicName: "objectId", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, relationActions: { classPropertyName: "relationActions", publicName: "relationActions", isSignal: true, isRequired: false, transformFunction: null } }, providers: [ObjectRelationshipService], ngImport: i0, template: "<ymt-pane [plain]=\"true\" [topBarActions]=\"topBarActions\">\n <ymt-pane-body [yuvBusyOverlay]=\"busy()\">\n @if (empty()) {\n <div class=\"no-relations\" inert>\n <mat-icon>link_off</mat-icon>\n <p>{{ 'yuv.object-relationship.no-relations' | translate }}</p>\n </div>\n }\n @switch (mode()) {\n @case ('graph') {\n <yuv-object-relationship-graph [config]=\"config()\"></yuv-object-relationship-graph>\n }\n @case ('list') {\n <yuv-object-relationship-list [config]=\"config()\"> </yuv-object-relationship-list>\n }\n }\n </ymt-pane-body>\n</ymt-pane>\n\n<ng-template #topBarActions>\n @if (supportedRelationships().length > 0) {\n <button ymt-icon-button icon-button-size=\"small\" [matTooltip]=\"'yuv.object-relationship.add.tooltip' | translate\" (click)=\"addRelationship()\">\n <mat-icon>add</mat-icon>\n </button>\n }\n <button\n ymt-icon-button\n icon-button-size=\"small\"\n [matTooltip]=\"mode() === 'list' ? ('yuv.object-relationship.mode.graph.tooltip' | translate) : ('yuv.object-relationship.mode.list.tooltip' | translate)\"\n (click)=\"toggleMode()\"\n >\n <mat-icon>{{ mode() === 'list' ? 'graph_3' : 'list' }}</mat-icon>\n </button>\n</ng-template>\n", styles: [":host{display:block;position:relative;height:100%;background-color:var(--ymt-surface)}:host .toggle{position:absolute;inset-block-start:var(--ymt-spacing-s);inset-inline-end:var(--ymt-spacing-s);z-index:10;background-color:var(--ymt-surface);padding:var(--ymt-spacing-xs);border-radius:var(--ymt-corner-full)}:host yuv-object-relationship-list,:host yuv-object-relationship-graph{height:100%}:host .no-relations{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;padding:var(--ymt-spacing-l);gap:var(--ymt-spacing-m);overflow:hidden}:host .no-relations mat-icon{scale:4;color:var(--ymt-text-color-subtle);opacity:.5;transform:translateY(-.5em)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "ngmodule", type: YmtPanesModule }, { kind: "component", type: i4$1.YmtPaneComponent, selector: "ymt-pane", inputs: ["topBarActions", "plain", "mode", "modeAlign"], outputs: ["paneToggled", "navigationClicked"] }, { kind: "component", type: i4$1.YmtPaneBodyComponent, selector: "ymt-pane-body" }, { kind: "directive", type: YmtIconButtonDirective, selector: "button[ymtIconButton],button[ymt-icon-button],a[ymtIconButton],a[ymt-icon-button]", inputs: ["disabled", "disableRipple", "aria-disabled", "disabledInteractive", "icon-button-size"] }, { kind: "component", type: ObjectRelationshipGraphComponent, selector: "yuv-object-relationship-graph", inputs: ["config"], outputs: ["objectSelected", "relationSelected"] }, { kind: "component", type: ObjectRelationshipListComponent, selector: "yuv-object-relationship-list", inputs: ["config"] }, { kind: "directive", type: BusyOverlayDirective, selector: "[yuvBusyOverlay]", inputs: ["yuvBusyOverlay", "yuvBusyOverlayConfig"] }] }); }
952
973
  }
953
974
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ObjectRelationshipComponent, decorators: [{
954
975
  type: Component,
@@ -962,8 +983,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
962
983
  YmtIconButtonDirective,
963
984
  ObjectRelationshipGraphComponent,
964
985
  ObjectRelationshipListComponent,
965
- YmtIconButtonDirective
966
- ], providers: [ObjectRelationshipService], template: "<ymt-pane [plain]=\"true\" [topBarActions]=\"topBarActions\">\n <ymt-pane-body>\n @switch (mode()) {\n @case ('graph') {\n <yuv-object-relationship-graph [config]=\"config()\"></yuv-object-relationship-graph>\n }\n @case ('list') {\n <yuv-object-relationship-list [config]=\"config()\"> </yuv-object-relationship-list>\n }\n }\n </ymt-pane-body>\n</ymt-pane>\n\n<ng-template #topBarActions>\n @if (supportedRelationships().length > 0) {\n <button ymt-icon-button icon-button-size=\"small\" [matTooltip]=\"'yuv.object-relationship.add.tooltip' | translate\" (click)=\"addRelationship()\">\n <mat-icon>add</mat-icon>\n </button>\n }\n <button\n ymt-icon-button\n icon-button-size=\"small\"\n [matTooltip]=\"mode() === 'list' ? ('yuv.object-relationship.mode.graph.tooltip' | translate) : ('yuv.object-relationship.mode.list.tooltip' | translate)\"\n (click)=\"toggleMode()\"\n >\n <mat-icon>{{ mode() === 'list' ? 'graph_3' : 'list' }}</mat-icon>\n </button>\n</ng-template>\n", styles: [":host{display:block;position:relative;height:100%;background-color:var(--ymt-surface)}:host .toggle{position:absolute;inset-block-start:var(--ymt-spacing-s);inset-inline-end:var(--ymt-spacing-s);z-index:10;background-color:var(--ymt-surface);padding:var(--ymt-spacing-xs);border-radius:var(--ymt-corner-full)}:host yuv-object-relationship-list,:host yuv-object-relationship-graph{height:100%}\n"] }]
986
+ YmtIconButtonDirective,
987
+ BusyOverlayDirective
988
+ ], providers: [ObjectRelationshipService], template: "<ymt-pane [plain]=\"true\" [topBarActions]=\"topBarActions\">\n <ymt-pane-body [yuvBusyOverlay]=\"busy()\">\n @if (empty()) {\n <div class=\"no-relations\" inert>\n <mat-icon>link_off</mat-icon>\n <p>{{ 'yuv.object-relationship.no-relations' | translate }}</p>\n </div>\n }\n @switch (mode()) {\n @case ('graph') {\n <yuv-object-relationship-graph [config]=\"config()\"></yuv-object-relationship-graph>\n }\n @case ('list') {\n <yuv-object-relationship-list [config]=\"config()\"> </yuv-object-relationship-list>\n }\n }\n </ymt-pane-body>\n</ymt-pane>\n\n<ng-template #topBarActions>\n @if (supportedRelationships().length > 0) {\n <button ymt-icon-button icon-button-size=\"small\" [matTooltip]=\"'yuv.object-relationship.add.tooltip' | translate\" (click)=\"addRelationship()\">\n <mat-icon>add</mat-icon>\n </button>\n }\n <button\n ymt-icon-button\n icon-button-size=\"small\"\n [matTooltip]=\"mode() === 'list' ? ('yuv.object-relationship.mode.graph.tooltip' | translate) : ('yuv.object-relationship.mode.list.tooltip' | translate)\"\n (click)=\"toggleMode()\"\n >\n <mat-icon>{{ mode() === 'list' ? 'graph_3' : 'list' }}</mat-icon>\n </button>\n</ng-template>\n", styles: [":host{display:block;position:relative;height:100%;background-color:var(--ymt-surface)}:host .toggle{position:absolute;inset-block-start:var(--ymt-spacing-s);inset-inline-end:var(--ymt-spacing-s);z-index:10;background-color:var(--ymt-surface);padding:var(--ymt-spacing-xs);border-radius:var(--ymt-corner-full)}:host yuv-object-relationship-list,:host yuv-object-relationship-graph{height:100%}:host .no-relations{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;padding:var(--ymt-spacing-l);gap:var(--ymt-spacing-m);overflow:hidden}:host .no-relations mat-icon{scale:4;color:var(--ymt-text-color-subtle);opacity:.5;transform:translateY(-.5em)}\n"] }]
967
989
  }] });
968
990
 
969
991
  const cmp = [ObjectRelationshipComponent];