@yuuvis/client-framework 3.5.0 → 3.7.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.
- package/fesm2022/yuuvis-client-framework-object-details.mjs +13 -74
- package/fesm2022/yuuvis-client-framework-object-details.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-object-relationship.mjs +139 -90
- package/fesm2022/yuuvis-client-framework-object-relationship.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-renderer.mjs +192 -3
- package/fesm2022/yuuvis-client-framework-renderer.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-tile-list.mjs +7 -0
- package/fesm2022/yuuvis-client-framework-tile-list.mjs.map +1 -1
- package/lib/assets/i18n/de.json +5 -5
- package/lib/assets/i18n/en.json +5 -5
- package/package.json +5 -5
- package/renderer/README.md +212 -2
- package/types/yuuvis-client-framework-object-details.d.ts +1 -9
- package/types/yuuvis-client-framework-object-relationship.d.ts +1 -1
- package/types/yuuvis-client-framework-renderer.d.ts +63 -3
|
@@ -6,8 +6,8 @@ import * as i1$2 from '@angular/material/icon';
|
|
|
6
6
|
import { MatIconModule } from '@angular/material/icon';
|
|
7
7
|
import * as i2 from '@angular/material/tooltip';
|
|
8
8
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
9
|
-
import { SearchService, BaseObjectTypeField, Operator, TranslatePipe, SystemService, DmsService, EventService, TranslateService, RelationshipTypeField, YuvEventType, SystemType, DmsObject } from '@yuuvis/client-core';
|
|
10
|
-
import { DialogComponent, BusyOverlayDirective, ConfirmService,
|
|
9
|
+
import { SearchService, BaseObjectTypeField, Operator, TranslatePipe, SystemService, DmsService, EventService, TranslateService, RelationshipTypeField, YuvEventType, SystemType, DmsObject, LocalizationService } from '@yuuvis/client-core';
|
|
10
|
+
import { DialogComponent, BusyOverlayDirective, ConfirmService, ThemeService } from '@yuuvis/client-framework/common';
|
|
11
11
|
import { YmtIconButtonDirective, YmtButtonDirective } from '@yuuvis/material';
|
|
12
12
|
import * as i3 from '@yuuvis/material/panes';
|
|
13
13
|
import { YmtPanesModule } from '@yuuvis/material/panes';
|
|
@@ -522,14 +522,15 @@ class ObjectRelationshipGraphComponent {
|
|
|
522
522
|
constructor() {
|
|
523
523
|
this.#dialog = inject(MatDialog);
|
|
524
524
|
this.#system = inject(SystemService);
|
|
525
|
-
this.#
|
|
525
|
+
this.#localization = inject(LocalizationService);
|
|
526
|
+
this.#theme = inject(ThemeService);
|
|
526
527
|
this.#objectRelationshipService = inject(ObjectRelationshipService);
|
|
527
528
|
this.#snack = inject(SnackBarService);
|
|
528
|
-
this
|
|
529
|
+
this.#translate = inject(TranslateService);
|
|
529
530
|
this.container = viewChild.required('graphContainer');
|
|
530
531
|
this.#mode = computed(() => {
|
|
531
|
-
const
|
|
532
|
-
switch (
|
|
532
|
+
const mode = this.#theme.mode();
|
|
533
|
+
switch (mode) {
|
|
533
534
|
case 'dark': {
|
|
534
535
|
return 'dark';
|
|
535
536
|
}
|
|
@@ -546,7 +547,7 @@ class ObjectRelationshipGraphComponent {
|
|
|
546
547
|
const rel = this.#relations();
|
|
547
548
|
if (!rel)
|
|
548
549
|
return;
|
|
549
|
-
this.#
|
|
550
|
+
this.#updateGraph(rel.originId, rel.relations, rel.objects);
|
|
550
551
|
}, ...(ngDevMode ? [{ debugName: "#relationsEffect" }] : /* istanbul ignore next */ []));
|
|
551
552
|
/**
|
|
552
553
|
* Configuration for the relations component.
|
|
@@ -559,6 +560,8 @@ class ObjectRelationshipGraphComponent {
|
|
|
559
560
|
this.#relationQA = {};
|
|
560
561
|
this.#nodes = new DataSet([]);
|
|
561
562
|
this.#edges = new DataSet([]);
|
|
563
|
+
// tracks what each node's expansion added, so a second double-click can collapse it again
|
|
564
|
+
this.#expansions = new Map();
|
|
562
565
|
this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
563
566
|
this.selectedRelation = signal(null, ...(ngDevMode ? [{ debugName: "selectedRelation" }] : /* istanbul ignore next */ []));
|
|
564
567
|
this.#selectedRelationEffect = effect(() => {
|
|
@@ -566,10 +569,10 @@ class ObjectRelationshipGraphComponent {
|
|
|
566
569
|
}, ...(ngDevMode ? [{ debugName: "#selectedRelationEffect" }] : /* istanbul ignore next */ []));
|
|
567
570
|
this.selectedObject = signal(null, ...(ngDevMode ? [{ debugName: "selectedObject" }] : /* istanbul ignore next */ []));
|
|
568
571
|
this.#selectedObjectEffect = effect(() => {
|
|
569
|
-
const
|
|
570
|
-
if (
|
|
572
|
+
const selectedObj = this.selectedObject();
|
|
573
|
+
if (selectedObj)
|
|
571
574
|
this.selectedRelation.set(null);
|
|
572
|
-
this.objectSelected.emit(
|
|
575
|
+
this.objectSelected.emit(selectedObj);
|
|
573
576
|
}, ...(ngDevMode ? [{ debugName: "#selectedObjectEffect" }] : /* istanbul ignore next */ []));
|
|
574
577
|
this.detailsNode = computed(() => {
|
|
575
578
|
const object = this.selectedObject();
|
|
@@ -579,9 +582,11 @@ class ObjectRelationshipGraphComponent {
|
|
|
579
582
|
}
|
|
580
583
|
#dialog;
|
|
581
584
|
#system;
|
|
582
|
-
#
|
|
585
|
+
#localization;
|
|
586
|
+
#theme;
|
|
583
587
|
#objectRelationshipService;
|
|
584
588
|
#snack;
|
|
589
|
+
#translate;
|
|
585
590
|
#mode;
|
|
586
591
|
#relations;
|
|
587
592
|
#relationsEffect;
|
|
@@ -590,10 +595,62 @@ class ObjectRelationshipGraphComponent {
|
|
|
590
595
|
#relationQA;
|
|
591
596
|
#nodes;
|
|
592
597
|
#edges;
|
|
598
|
+
// tracks what each node's expansion added, so a second double-click can collapse it again
|
|
599
|
+
#expansions;
|
|
593
600
|
#selectedRelationEffect;
|
|
594
601
|
#selectedObjectEffect;
|
|
602
|
+
// #region: Lifecycle Hooks */
|
|
603
|
+
ngOnDestroy() {
|
|
604
|
+
if (this.#network) {
|
|
605
|
+
this.#network.destroy();
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
ngAfterViewInit() {
|
|
609
|
+
const options = {
|
|
610
|
+
physics: {
|
|
611
|
+
enabled: true,
|
|
612
|
+
solver: 'forceAtlas2Based',
|
|
613
|
+
forceAtlas2Based: {
|
|
614
|
+
// gravitationalConstant: -50,
|
|
615
|
+
// centralGravity: 0.01,
|
|
616
|
+
springLength: 200
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
nodes: {
|
|
620
|
+
shape: 'dot',
|
|
621
|
+
size: 20,
|
|
622
|
+
font: { size: 12 }
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
this.#network = new Network(this.container().nativeElement, {
|
|
626
|
+
nodes: this.#nodes,
|
|
627
|
+
edges: this.#edges
|
|
628
|
+
}, options);
|
|
629
|
+
this.#network.on('selectNode', (params) => this.#setSelection(params));
|
|
630
|
+
this.#network.on('deselectNode', (params) => this.#setSelection(params));
|
|
631
|
+
this.#network.on('dragEnd', (params) => this.#setSelection(params));
|
|
632
|
+
this.#network.on('selectEdge', (params) => this.#setSelection(params));
|
|
633
|
+
this.#network.on('deselectEdge', (params) => this.#setSelection(params));
|
|
634
|
+
this.#network.on('click', (params) => this.#setSelection(params));
|
|
635
|
+
this.#network.on('doubleClick', (params) => {
|
|
636
|
+
const nodeId = params.nodes[0];
|
|
637
|
+
if (!nodeId)
|
|
638
|
+
return;
|
|
639
|
+
// the root node never collapses - a double-click on it just re-expands
|
|
640
|
+
const isRoot = nodeId === this.#relations()?.originId;
|
|
641
|
+
if (!isRoot && this.#expansions.has(nodeId)) {
|
|
642
|
+
this.#collapse(nodeId);
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
this.#expand(nodeId);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
// #endregion
|
|
595
650
|
download() {
|
|
596
651
|
const canvas = this.container().nativeElement.querySelector('canvas');
|
|
652
|
+
if (!canvas)
|
|
653
|
+
return;
|
|
597
654
|
const image = canvas.toDataURL('image/png');
|
|
598
655
|
const link = document.createElement('a');
|
|
599
656
|
link.href = image;
|
|
@@ -611,7 +668,7 @@ class ObjectRelationshipGraphComponent {
|
|
|
611
668
|
}
|
|
612
669
|
})
|
|
613
670
|
.afterClosed()
|
|
614
|
-
.subscribe((
|
|
671
|
+
.subscribe(() => {
|
|
615
672
|
this.expandSelected();
|
|
616
673
|
});
|
|
617
674
|
}
|
|
@@ -624,7 +681,8 @@ class ObjectRelationshipGraphComponent {
|
|
|
624
681
|
const sourceNodeId = deletedEdge.from;
|
|
625
682
|
const targetNodeId = deletedEdge.to;
|
|
626
683
|
this.#edges.remove(this.selectedRelation().id);
|
|
627
|
-
// check if the source/target node of the deleted edge has other connections to
|
|
684
|
+
// check if the source/target node of the deleted edge has other connections to
|
|
685
|
+
// another node in the current graph
|
|
628
686
|
const otherEdgesForSource = this.#edges
|
|
629
687
|
.get()
|
|
630
688
|
.filter((edge) => edge.from === sourceNodeId || edge.to === sourceNodeId);
|
|
@@ -639,7 +697,7 @@ class ObjectRelationshipGraphComponent {
|
|
|
639
697
|
}
|
|
640
698
|
}
|
|
641
699
|
else {
|
|
642
|
-
this.#snack.danger(this
|
|
700
|
+
this.#snack.danger(this.#translate.instant('yuv.object-relationship.delete-relation.error-message'));
|
|
643
701
|
}
|
|
644
702
|
});
|
|
645
703
|
}
|
|
@@ -654,7 +712,11 @@ class ObjectRelationshipGraphComponent {
|
|
|
654
712
|
this.busy.set(true);
|
|
655
713
|
this.#objectRelationshipService.fetchRelations(nodeId).subscribe({
|
|
656
714
|
next: (res) => {
|
|
657
|
-
this.#
|
|
715
|
+
const added = this.#updateGraph(nodeId, res.relations, res.objects);
|
|
716
|
+
// record the expansion so it can be toggled closed again - but never make the root collapsible
|
|
717
|
+
if (nodeId !== this.#relations()?.originId) {
|
|
718
|
+
this.#expansions.set(nodeId, added);
|
|
719
|
+
}
|
|
658
720
|
this.busy.set(false);
|
|
659
721
|
},
|
|
660
722
|
error: (err) => {
|
|
@@ -664,7 +726,24 @@ class ObjectRelationshipGraphComponent {
|
|
|
664
726
|
});
|
|
665
727
|
}
|
|
666
728
|
}
|
|
667
|
-
|
|
729
|
+
// remove the nodes/edges that this node's expansion introduced (and any expanded sub-trees),
|
|
730
|
+
// keeping nodes that are still connected to the rest of the graph
|
|
731
|
+
#collapse(nodeId) {
|
|
732
|
+
const expansion = this.#expansions.get(nodeId);
|
|
733
|
+
if (!expansion)
|
|
734
|
+
return;
|
|
735
|
+
this.#expansions.delete(nodeId);
|
|
736
|
+
this.#edges.remove(expansion.edgeIds);
|
|
737
|
+
expansion.nodeIds.forEach((id) => {
|
|
738
|
+
// collapse the sub-tree first so its edges are gone before the orphan check
|
|
739
|
+
if (this.#expansions.has(id))
|
|
740
|
+
this.#collapse(id);
|
|
741
|
+
const stillConnected = this.#edges.get().some((edge) => edge.from === id || edge.to === id);
|
|
742
|
+
if (!stillConnected)
|
|
743
|
+
this.#nodes.remove(id);
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
#updateGraph(originId, relations, objects) {
|
|
668
747
|
objects.items.forEach((item) => {
|
|
669
748
|
const id = item.fields.get(BaseObjectTypeField.OBJECT_ID);
|
|
670
749
|
this.#objectQA[id] = new DmsObject(item);
|
|
@@ -677,7 +756,7 @@ class ObjectRelationshipGraphComponent {
|
|
|
677
756
|
const incoming = relations.items.filter((item) => item.fields.get(RelationshipTypeField.TARGET_ID) === originId);
|
|
678
757
|
const outgoing = relations.items.filter((item) => item.fields.get(RelationshipTypeField.SOURCE_ID) === originId);
|
|
679
758
|
const rootNode = this.#toNode(originId);
|
|
680
|
-
const _nodes = originId === this.#relations()?.originId && this.#nodes.get([originId]).length === 0
|
|
759
|
+
const _nodes = rootNode && originId === this.#relations()?.originId && this.#nodes.get([originId]).length === 0
|
|
681
760
|
? [
|
|
682
761
|
{
|
|
683
762
|
...rootNode,
|
|
@@ -693,52 +772,57 @@ class ObjectRelationshipGraphComponent {
|
|
|
693
772
|
// add nodes and edges from search result
|
|
694
773
|
outgoing.forEach((item) => {
|
|
695
774
|
const relatedObjectId = item.fields.get(RelationshipTypeField.TARGET_ID);
|
|
696
|
-
const
|
|
697
|
-
const
|
|
698
|
-
if (
|
|
775
|
+
const node = this.#toNode(relatedObjectId);
|
|
776
|
+
const edge = this.#toEdge(item, originId, relatedObjectId, 'to');
|
|
777
|
+
if (node) {
|
|
699
778
|
// check if the node is already in the rendered graph
|
|
700
|
-
const nodeExists = _nodes.some((
|
|
779
|
+
const nodeExists = _nodes.some((existing) => existing.id === node.id) || this.#nodes.get([node.id]).length > 0;
|
|
701
780
|
if (!nodeExists)
|
|
702
|
-
_nodes.push(
|
|
781
|
+
_nodes.push(node);
|
|
703
782
|
// check if edge already exists
|
|
704
|
-
const edgeExists = _edges.some((
|
|
783
|
+
const edgeExists = _edges.some((existing) => existing.id === edge.id) || this.#edges.get([edge.id]).length > 0;
|
|
705
784
|
if (!edgeExists)
|
|
706
|
-
_edges.push(
|
|
785
|
+
_edges.push(edge);
|
|
707
786
|
}
|
|
708
787
|
});
|
|
709
788
|
incoming.forEach((item) => {
|
|
710
789
|
const relatedObjectId = item.fields.get(RelationshipTypeField.SOURCE_ID);
|
|
711
|
-
const
|
|
712
|
-
const
|
|
713
|
-
if (
|
|
790
|
+
const toNode = this.#toNode(relatedObjectId);
|
|
791
|
+
const toEdge = this.#toEdge(item, relatedObjectId, originId, 'from');
|
|
792
|
+
if (toNode) {
|
|
714
793
|
// check if the node is already in the rendered graph
|
|
715
|
-
const nodeExists = _nodes.some((node) => node.id ===
|
|
794
|
+
const nodeExists = _nodes.some((node) => node.id === toNode.id) || this.#nodes.get([toNode.id]).length > 0;
|
|
716
795
|
if (!nodeExists)
|
|
717
|
-
_nodes.push(
|
|
796
|
+
_nodes.push(toNode);
|
|
718
797
|
// check if edge already exists
|
|
719
|
-
const edgeExists = _edges.some((edge) => edge.id ===
|
|
798
|
+
const edgeExists = _edges.some((edge) => edge.id === toEdge.id) || this.#edges.get([toEdge.id]).length > 0;
|
|
720
799
|
if (!edgeExists)
|
|
721
|
-
_edges.push(
|
|
800
|
+
_edges.push(toEdge);
|
|
722
801
|
}
|
|
723
802
|
});
|
|
724
803
|
this.#nodes.add(_nodes);
|
|
725
804
|
this.#edges.add(_edges);
|
|
805
|
+
// report what was added so the caller can later collapse it - never the root node
|
|
806
|
+
const rootId = this.#relations()?.originId;
|
|
807
|
+
return {
|
|
808
|
+
nodeIds: _nodes.map((node) => node.id).filter((id) => id !== rootId),
|
|
809
|
+
edgeIds: _edges.map((edge) => edge.id)
|
|
810
|
+
};
|
|
726
811
|
}
|
|
727
|
-
#getNodeConfig(
|
|
812
|
+
#getNodeConfig(dmsObject) {
|
|
728
813
|
const cfg = this.config();
|
|
729
|
-
if (!
|
|
814
|
+
if (!dmsObject)
|
|
730
815
|
return undefined;
|
|
731
|
-
const objectTypeIds = [...
|
|
732
|
-
return [cfg.rootNode, ...cfg.nodes].find((
|
|
816
|
+
const objectTypeIds = [...dmsObject.sots, dmsObject.objectTypeId];
|
|
817
|
+
return [cfg.rootNode, ...cfg.nodes].find((node) => objectTypeIds.includes(node.objectType));
|
|
733
818
|
}
|
|
734
819
|
// convert search result item to vis node
|
|
735
820
|
// will return NULL if the item object type is not configured in the component config
|
|
736
821
|
#toNode(objectId) {
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
if (!o)
|
|
822
|
+
const dmsObject = this.#objectQA[objectId];
|
|
823
|
+
if (!dmsObject)
|
|
740
824
|
return null;
|
|
741
|
-
const cfgNode = this.#getNodeConfig(
|
|
825
|
+
const cfgNode = this.#getNodeConfig(dmsObject);
|
|
742
826
|
if (!cfgNode)
|
|
743
827
|
return null;
|
|
744
828
|
return {
|
|
@@ -746,7 +830,7 @@ class ObjectRelationshipGraphComponent {
|
|
|
746
830
|
objectType: cfgNode.objectType,
|
|
747
831
|
color: this.#getNodeColor(),
|
|
748
832
|
id: objectId,
|
|
749
|
-
label: this.#maxLength(
|
|
833
|
+
label: this.#maxLength(dmsObject.data[cfgNode.labelField], 30),
|
|
750
834
|
icon: {
|
|
751
835
|
face: 'Material Symbols Sharp',
|
|
752
836
|
code: cfgNode.icon,
|
|
@@ -759,7 +843,7 @@ class ObjectRelationshipGraphComponent {
|
|
|
759
843
|
};
|
|
760
844
|
}
|
|
761
845
|
#toEdge(item, from, to, direction) {
|
|
762
|
-
const label = this.#
|
|
846
|
+
const label = this.#localization.getLocalizedLabel(item.fields.get(BaseObjectTypeField.OBJECT_TYPE_ID)) ||
|
|
763
847
|
item.fields.get(BaseObjectTypeField.OBJECT_TYPE_ID);
|
|
764
848
|
return {
|
|
765
849
|
...EDGE_SETTINGS,
|
|
@@ -836,48 +920,12 @@ class ObjectRelationshipGraphComponent {
|
|
|
836
920
|
else
|
|
837
921
|
return s;
|
|
838
922
|
}
|
|
839
|
-
ngAfterViewInit() {
|
|
840
|
-
const options = {
|
|
841
|
-
physics: {
|
|
842
|
-
enabled: true,
|
|
843
|
-
solver: 'forceAtlas2Based',
|
|
844
|
-
forceAtlas2Based: {
|
|
845
|
-
// gravitationalConstant: -50,
|
|
846
|
-
// centralGravity: 0.01,
|
|
847
|
-
springLength: 200
|
|
848
|
-
}
|
|
849
|
-
},
|
|
850
|
-
nodes: {
|
|
851
|
-
shape: 'dot',
|
|
852
|
-
size: 20,
|
|
853
|
-
font: { size: 12 }
|
|
854
|
-
}
|
|
855
|
-
};
|
|
856
|
-
this.#network = new Network(this.container().nativeElement, {
|
|
857
|
-
nodes: this.#nodes,
|
|
858
|
-
edges: this.#edges
|
|
859
|
-
}, options);
|
|
860
|
-
this.#network.on('selectNode', (params) => this.#setSelection(params));
|
|
861
|
-
this.#network.on('deselectNode', (params) => this.#setSelection(params));
|
|
862
|
-
this.#network.on('dragEnd', (params) => this.#setSelection(params));
|
|
863
|
-
this.#network.on('selectEdge', (params) => this.#setSelection(params));
|
|
864
|
-
this.#network.on('deselectEdge', (params) => this.#setSelection(params));
|
|
865
|
-
this.#network.on('click', (params) => this.#setSelection(params));
|
|
866
|
-
this.#network.on('doubleClick', (params) => {
|
|
867
|
-
this.#expand(params.nodes[0]);
|
|
868
|
-
});
|
|
869
|
-
}
|
|
870
923
|
#setSelection(params) {
|
|
871
924
|
this.selectedObject.set(params.nodes.length === 1 ? this.#objectQA[params.nodes[0]] : null);
|
|
872
925
|
this.selectedRelation.set(params.edges.length === 1 && params.nodes.length === 0 ? this.#relationQA[params.edges[0]] : null);
|
|
873
926
|
}
|
|
874
|
-
ngOnDestroy() {
|
|
875
|
-
if (this.#network) {
|
|
876
|
-
this.#network.destroy();
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
927
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ObjectRelationshipGraphComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
880
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: ObjectRelationshipGraphComponent, isStandalone: true, selector: "yuv-object-relationship-graph", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { objectSelected: "objectSelected", relationSelected: "relationSelected" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["graphContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"relations\" [yuvBusyOverlay]=\"busy()\">\n <div class=\"toolbar\">\n <button ymtIconButton (click)=\"download()\" [matTooltip]=\"'yuv.object-relationship.download.tooltip' | translate\">\n <mat-icon>download</mat-icon>\n </button>\n @if (selectedRelation()) {\n <button
|
|
928
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: ObjectRelationshipGraphComponent, isStandalone: true, selector: "yuv-object-relationship-graph", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { objectSelected: "objectSelected", relationSelected: "relationSelected" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["graphContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"relations\" [yuvBusyOverlay]=\"busy()\">\n <div class=\"toolbar\">\n <button ymtIconButton (click)=\"download()\" [matTooltip]=\"'yuv.object-relationship.download.tooltip' | translate\">\n <mat-icon>download</mat-icon>\n </button>\n @if (selectedRelation()) {\n <button\n ymtIconButton\n (click)=\"deleteRelationship()\"\n [matTooltip]=\"'yuv.object-relationship.delete-relation.tooltip' | translate\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n <div class=\"graph-container\" #graphContainer></div>\n\n @let dn = detailsNode();\n @if (dn) {\n <div class=\"selected\">\n <yuv-node-summary [object]=\"dn.object\" [nodeConfig]=\"dn.config\" [actions]=\"additionalSummaryActions\" />\n </div>\n }\n</div>\n\n<ng-template #additionalSummaryActions>\n <button\n ymt-icon-button\n icon-button-size=\"small\"\n [matTooltip]=\"'yuv.object-relationship.selected.add-relationship.tooltip' | translate\"\n (click)=\"addRelationship()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n <button\n ymt-icon-button\n icon-button-size=\"small\"\n [matTooltip]=\"'yuv.object-relationship.selected.expand.tooltip' | translate\"\n (click)=\"expandSelected()\"\n >\n <mat-icon>open_with</mat-icon>\n </button>\n</ng-template>\n", styles: [":host{--_container-background-color: var(--container-background-color, var(--ymt-surface));--_node-background-color: var(--node-background-color, var(--ymt-surface-container));--_node-border-color: var(--node-border-color, var(--ymt-outline));--_node-font-color: var(--node-font-color, var(--ymt-text-color));--_node-background-color-highlight: var(--node-background-color-highlight, var(--ymt-primary-container));--_node-color-highlight: var(--node-color-highlight, var(--ymt-on-primary-container));--_edge-font-color: var(--edge-font-color, var(--ymt-text-color-subtle));--_edge-color: var(--edge-color, var(--ymt-outline));--_edge-color-highlight: var(--edge-color-highlight, var(--ymt-primary));--_edge-color-hover: var(--edge-color-hover, var(--ymt-primary))}:host .relations{position:relative;display:block;height:100%}:host .relations .toolbar{position:absolute;inset-block-start:var(--ymt-spacing-2xs);inset-inline-start:var(--ymt-spacing-s);z-index:10}:host .relations .graph-container{background-color:var(--_container-background-color);width:100%;height:100%}:host .relations .selected{position:absolute;inset-inline-end:var(--ymt-spacing-m);inset-block-start:var(--ymt-spacing-m);inset-block-end:var(--ymt-spacing-m);padding:var(--ymt-spacing-s) var(--ymt-spacing-m);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);background-color:rgb(from var(--ymt-surface) r g b/.8);border:1px solid var(--ymt-outline-variant);border-radius:var(--ymt-corner-s);max-width:33%;z-index:10;min-width:200px}:host .relations .selected yuv-node-summary{height:100%}\n"], dependencies: [{ kind: "component", type: NodeSummaryComponent, selector: "yuv-node-summary", inputs: ["object", "nodeConfig", "actions"] }, { kind: "directive", type: BusyOverlayDirective, selector: "[yuvBusyOverlay]", inputs: ["yuvBusyOverlay", "yuvBusyOverlayConfig", "yuvBusyError"], outputs: ["yuvBusyErrorDismiss"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$2.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: "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: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
881
929
|
}
|
|
882
930
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ObjectRelationshipGraphComponent, decorators: [{
|
|
883
931
|
type: Component,
|
|
@@ -888,7 +936,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
888
936
|
MatTooltipModule,
|
|
889
937
|
TranslatePipe,
|
|
890
938
|
YmtIconButtonDirective
|
|
891
|
-
], template: "<div class=\"relations\" [yuvBusyOverlay]=\"busy()\">\n <div class=\"toolbar\">\n <button ymtIconButton (click)=\"download()\" [matTooltip]=\"'yuv.object-relationship.download.tooltip' | translate\">\n <mat-icon>download</mat-icon>\n </button>\n @if (selectedRelation()) {\n <button
|
|
939
|
+
], template: "<div class=\"relations\" [yuvBusyOverlay]=\"busy()\">\n <div class=\"toolbar\">\n <button ymtIconButton (click)=\"download()\" [matTooltip]=\"'yuv.object-relationship.download.tooltip' | translate\">\n <mat-icon>download</mat-icon>\n </button>\n @if (selectedRelation()) {\n <button\n ymtIconButton\n (click)=\"deleteRelationship()\"\n [matTooltip]=\"'yuv.object-relationship.delete-relation.tooltip' | translate\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n <div class=\"graph-container\" #graphContainer></div>\n\n @let dn = detailsNode();\n @if (dn) {\n <div class=\"selected\">\n <yuv-node-summary [object]=\"dn.object\" [nodeConfig]=\"dn.config\" [actions]=\"additionalSummaryActions\" />\n </div>\n }\n</div>\n\n<ng-template #additionalSummaryActions>\n <button\n ymt-icon-button\n icon-button-size=\"small\"\n [matTooltip]=\"'yuv.object-relationship.selected.add-relationship.tooltip' | translate\"\n (click)=\"addRelationship()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n <button\n ymt-icon-button\n icon-button-size=\"small\"\n [matTooltip]=\"'yuv.object-relationship.selected.expand.tooltip' | translate\"\n (click)=\"expandSelected()\"\n >\n <mat-icon>open_with</mat-icon>\n </button>\n</ng-template>\n", styles: [":host{--_container-background-color: var(--container-background-color, var(--ymt-surface));--_node-background-color: var(--node-background-color, var(--ymt-surface-container));--_node-border-color: var(--node-border-color, var(--ymt-outline));--_node-font-color: var(--node-font-color, var(--ymt-text-color));--_node-background-color-highlight: var(--node-background-color-highlight, var(--ymt-primary-container));--_node-color-highlight: var(--node-color-highlight, var(--ymt-on-primary-container));--_edge-font-color: var(--edge-font-color, var(--ymt-text-color-subtle));--_edge-color: var(--edge-color, var(--ymt-outline));--_edge-color-highlight: var(--edge-color-highlight, var(--ymt-primary));--_edge-color-hover: var(--edge-color-hover, var(--ymt-primary))}:host .relations{position:relative;display:block;height:100%}:host .relations .toolbar{position:absolute;inset-block-start:var(--ymt-spacing-2xs);inset-inline-start:var(--ymt-spacing-s);z-index:10}:host .relations .graph-container{background-color:var(--_container-background-color);width:100%;height:100%}:host .relations .selected{position:absolute;inset-inline-end:var(--ymt-spacing-m);inset-block-start:var(--ymt-spacing-m);inset-block-end:var(--ymt-spacing-m);padding:var(--ymt-spacing-s) var(--ymt-spacing-m);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);background-color:rgb(from var(--ymt-surface) r g b/.8);border:1px solid var(--ymt-outline-variant);border-radius:var(--ymt-corner-s);max-width:33%;z-index:10;min-width:200px}:host .relations .selected yuv-node-summary{height:100%}\n"] }]
|
|
892
940
|
}], propDecorators: { container: [{ type: i0.ViewChild, args: ['graphContainer', { isSignal: true }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], objectSelected: [{ type: i0.Output, args: ["objectSelected"] }], relationSelected: [{ type: i0.Output, args: ["relationSelected"] }] } });
|
|
893
941
|
|
|
894
942
|
class ObjectRelationshipListItemComponent {
|
|
@@ -896,7 +944,7 @@ class ObjectRelationshipListItemComponent {
|
|
|
896
944
|
this.#router = inject(Router);
|
|
897
945
|
this.#snack = inject(SnackBarService);
|
|
898
946
|
this.#objectRelationshipService = inject(ObjectRelationshipService);
|
|
899
|
-
this
|
|
947
|
+
this.#translate = inject(TranslateService);
|
|
900
948
|
this.link = input.required(...(ngDevMode ? [{ debugName: "link" }] : /* istanbul ignore next */ []));
|
|
901
949
|
this.direction = input.required(...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
|
|
902
950
|
this.enableSourceLink = input(false, ...(ngDevMode ? [{ debugName: "enableSourceLink" }] : /* istanbul ignore next */ []));
|
|
@@ -905,16 +953,17 @@ class ObjectRelationshipListItemComponent {
|
|
|
905
953
|
#router;
|
|
906
954
|
#snack;
|
|
907
955
|
#objectRelationshipService;
|
|
956
|
+
#translate;
|
|
908
957
|
deleteRelationship() {
|
|
909
958
|
this.#objectRelationshipService.deleteRelationship(this.link().id, true).subscribe((success) => {
|
|
910
959
|
if (!success) {
|
|
911
|
-
this.#snack.danger(this
|
|
960
|
+
this.#snack.danger(this.#translate.instant('yuv.object-relationship.delete-relation.error-message'));
|
|
912
961
|
}
|
|
913
962
|
});
|
|
914
963
|
}
|
|
915
|
-
open(link, skip) {
|
|
964
|
+
async open(link, skip) {
|
|
916
965
|
if (link && !skip)
|
|
917
|
-
this.#router.navigateByUrl(link);
|
|
966
|
+
await this.#router.navigateByUrl(link);
|
|
918
967
|
}
|
|
919
968
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ObjectRelationshipListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
920
969
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: ObjectRelationshipListItemComponent, isStandalone: true, selector: "yuv-object-relationship-list-item", inputs: { link: { classPropertyName: "link", publicName: "link", isSignal: true, isRequired: true, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: true, transformFunction: null }, enableSourceLink: { classPropertyName: "enableSourceLink", publicName: "enableSourceLink", isSignal: true, isRequired: false, transformFunction: null }, enableTargetLink: { classPropertyName: "enableTargetLink", publicName: "enableTargetLink", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@let lnk = link();\n\n<button\n class=\"node source\"\n (click)=\"open(lnk.source.objectLink, !enableSourceLink())\"\n [attr.aria-readonly]=\"!enableSourceLink() || !lnk.source.objectLink\"\n [class.self]=\"direction() === 'out'\"\n>\n @if (lnk.source.icon) {\n <mat-icon>{{ lnk.source.icon }}</mat-icon>\n }\n <span class=\"title\">{{ lnk.source.label }}</span>\n <span class=\"description\">{{ lnk.source.description }}</span>\n</button>\n\n<div class=\"line\" [class.arr]=\"direction() === 'in'\"></div>\n<div class=\"relation\">{{ lnk.type.label }}</div>\n<div class=\"line\" [class.arr]=\"direction() === 'out'\"></div>\n\n<button\n class=\"node target\"\n (click)=\"open(lnk.target.objectLink, !enableTargetLink())\"\n [attr.aria-readonly]=\"!enableTargetLink() || !lnk.target.objectLink\"\n [class.self]=\"direction() === 'in'\"\n>\n @if (lnk.target.icon) {\n <mat-icon>{{ lnk.target.icon }}</mat-icon>\n }\n <span class=\"title\">{{ lnk.target.label }}</span>\n <span class=\"description\">{{ lnk.target.description }}</span>\n</button>\n\n<div class=\"actions\"> \n <button ymtIconButton icon-button-size=\"small\" (click)=\"deleteRelationship()\"><mat-icon>delete</mat-icon></button>\n</div>\n", styles: [":host{margin-block-end:var(--ymt-spacing-xs);display:grid;grid-template-columns:auto 1fr auto 1fr auto auto;align-items:center;grid-template-areas:\"source line relation arrow target actions\";column-gap:var(--ymt-spacing-xs);background-color:var(--ymt-surface);border-radius:var(--ymt-corner-s);padding:var(--ymt-spacing-2xs);border:1px solid var(--ymt-outline-variant)}:host .relation{color:var(--ymt-text-color-subtle);text-align:center;line-height:1.1em}:host>button{grid-area:source;display:grid;grid-template-rows:auto auto;grid-template-columns:auto 1fr;align-items:center;grid-template-areas:\"icon label\" \"icon desc\";border-radius:var(--ymt-corner-xs);border:2px solid var(--ymt-outline-variant);padding:var(--ymt-spacing-xs);font:var(--ymt-font-title-smallest);background-color:transparent;text-align:start}:host>button mat-icon{grid-area:icon;scale:.6;margin-inline-end:var(--ymt-spacing-xs)}:host>button .title{grid-area:label}:host>button .description{grid-area:desc;font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}:host>button.self{background-color:var(--ymt-surface-container);color:var(--ymt-on-surface)}:host>button:not(.self):hover{background-color:var(--ymt-hover-background)}:host>button:not([aria-readonly=true]){cursor:pointer}:host>button.target{grid-area:target}:host>button:not([aria-readonly]){cursor:pointer}:host .line{grid-area:line;height:2px;position:relative;background-color:var(--ymt-outline);opacity:.5;min-width:1em}:host .line.arr{grid-area:arrow}:host .line.arr:before{content:\"\";position:absolute;transform:rotate(45deg);transform-origin:top right;width:7px;inset-inline-end:0;translate:2px 1px;height:7px;border:3.5px solid transparent;border-block-start-color:var(--ymt-outline);border-inline-end-color:var(--ymt-outline)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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"] }] }); }
|
|
@@ -927,7 +976,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
927
976
|
class ObjectRelationshipListComponent {
|
|
928
977
|
constructor() {
|
|
929
978
|
this.#search = inject(SearchService);
|
|
930
|
-
this.#
|
|
979
|
+
this.#localization = inject(LocalizationService);
|
|
931
980
|
this.#objectRelationshipService = inject(ObjectRelationshipService);
|
|
932
981
|
/**
|
|
933
982
|
* Configuration for the relations component.
|
|
@@ -945,7 +994,7 @@ class ObjectRelationshipListComponent {
|
|
|
945
994
|
this.#objectQA = {};
|
|
946
995
|
}
|
|
947
996
|
#search;
|
|
948
|
-
#
|
|
997
|
+
#localization;
|
|
949
998
|
#objectRelationshipService;
|
|
950
999
|
#relations;
|
|
951
1000
|
#relationsEffect;
|
|
@@ -957,8 +1006,8 @@ class ObjectRelationshipListComponent {
|
|
|
957
1006
|
const id = item.fields.get(BaseObjectTypeField.OBJECT_ID);
|
|
958
1007
|
this.#objectQA[id] = new DmsObject(item);
|
|
959
1008
|
});
|
|
960
|
-
this.incomingLinks.set(incoming.map((rel) => this.#map(rel)).filter((
|
|
961
|
-
this.outgoingLinks.set(outgoing.map((rel) => this.#map(rel)).filter((
|
|
1009
|
+
this.incomingLinks.set(incoming.map((rel) => this.#map(rel)).filter((link) => link !== undefined));
|
|
1010
|
+
this.outgoingLinks.set(outgoing.map((rel) => this.#map(rel)).filter((link) => link !== undefined));
|
|
962
1011
|
}
|
|
963
1012
|
#map(relation) {
|
|
964
1013
|
const sourceObject = this.#objectQA[relation.fields.get(RelationshipTypeField.SOURCE_ID)];
|
|
@@ -981,7 +1030,7 @@ class ObjectRelationshipListComponent {
|
|
|
981
1030
|
id: relID,
|
|
982
1031
|
type: {
|
|
983
1032
|
id: relObjectTypeID,
|
|
984
|
-
label: this.#
|
|
1033
|
+
label: this.#localization.getLocalizedLabel(relObjectTypeID) || relObjectTypeID
|
|
985
1034
|
},
|
|
986
1035
|
source: {
|
|
987
1036
|
id: relation.fields.get(RelationshipTypeField.SOURCE_ID),
|
|
@@ -1003,7 +1052,7 @@ class ObjectRelationshipListComponent {
|
|
|
1003
1052
|
const cfg = this.config();
|
|
1004
1053
|
if (!object)
|
|
1005
1054
|
return undefined;
|
|
1006
|
-
return cfg.nodes.find((
|
|
1055
|
+
return cfg.nodes.find((nodes) => object.data[BaseObjectTypeField.SECONDARY_OBJECT_TYPE_IDS].includes(nodes.objectType));
|
|
1007
1056
|
}
|
|
1008
1057
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ObjectRelationshipListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1009
1058
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: ObjectRelationshipListComponent, isStandalone: true, selector: "yuv-object-relationship-list", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@let incoming = incomingLinks();\n@let outgoing = outgoingLinks();\n\n@if (incoming.length > 0) {\n <h3>{{ 'yuv.object-relationship.list.incoming' | translate }}</h3>\n @for (n of incoming; track n.id) {\n <yuv-object-relationship-list-item\n [enableSourceLink]=\"true\"\n [enableTargetLink]=\"false\"\n [link]=\"n\"\n direction=\"in\"\n ></yuv-object-relationship-list-item>\n }\n}\n@if (outgoing.length > 0) {\n <h3>{{ 'yuv.object-relationship.list.outgoing' | translate }}</h3>\n @for (n of outgoing; track n.id) {\n <yuv-object-relationship-list-item\n [enableSourceLink]=\"false\"\n [enableTargetLink]=\"true\"\n [link]=\"n\"\n direction=\"out\"\n ></yuv-object-relationship-list-item>\n }\n}\n", styles: [":host{display:block;padding:var(--ymt-spacing-m)}\n"], dependencies: [{ kind: "component", type: ObjectRelationshipListItemComponent, selector: "yuv-object-relationship-list-item", inputs: ["link", "direction", "enableSourceLink", "enableTargetLink"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
@@ -1024,7 +1073,7 @@ class ObjectRelationshipComponent {
|
|
|
1024
1073
|
this.mode = model('graph', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
1025
1074
|
this.empty = computed(() => {
|
|
1026
1075
|
const rel = this.#relations();
|
|
1027
|
-
return rel
|
|
1076
|
+
return rel?.relations.totalNumItems === 0;
|
|
1028
1077
|
}, ...(ngDevMode ? [{ debugName: "empty" }] : /* istanbul ignore next */ []));
|
|
1029
1078
|
/**
|
|
1030
1079
|
* ID of the object to display relations for.
|