From 271a41f0b03f9bee6e4f696d990a3bb2c4f31708 Mon Sep 17 00:00:00 2001 From: Ralf Aron Date: Thu, 15 Feb 2024 09:42:23 +0100 Subject: [PATCH] fix: common tree implementation aas-tree, aas-table --- .vscode/launch.json | 2 +- package-lock.json | 6 +- .../lib/aas-table/aas-table.component.html | 113 ++-- .../src/lib/aas-table/aas-table.effects.ts | 26 +- .../src/lib/aas-table/aas-table.reducer.ts | 105 +-- .../src/lib/aas-table/aas-table.state.ts | 76 ++- .../src/lib/aas-tree/aas-tree-search.ts | 4 + .../src/lib/aas-tree/aas-tree.component.html | 66 +- .../src/lib/aas-tree/aas-tree.reducer.ts | 621 +----------------- .../src/lib/aas-tree/aas-tree.selectors.ts | 4 +- .../src/lib/aas-tree/aas-tree.state.ts | 451 +++++++++++-- .../operation-call-form.component.scss | 14 +- .../show-image-form.component.scss | 14 +- .../show-video-form.component.scss | 14 +- .../aas-lib/src/lib/auth/auth.component.scss | 14 +- .../auth/login-form/login-form.component.scss | 14 +- .../profile-form/profile-form.component.scss | 14 +- .../register-form.component.scss | 14 +- .../customer-feedback.component.scss | 14 +- .../digital-nameplate.component.scss | 14 +- .../library-table.component.scss | 14 +- .../src/lib/localize/localize.component.scss | 14 +- .../message-table.component.scss | 14 +- .../src/lib/notify/notify.component.scss | 14 +- .../secured-image.component.scss | 14 +- projects/aas-lib/src/lib/tree.ts | 247 +++++++ .../edit-element-form.component.scss | 14 +- .../new-element-form.component.scss | 14 +- .../src/app/about/about.component.scss | 14 +- .../src/app/main/main.component.scss | 14 +- .../remove-endpoint-form.component.scss | 14 +- .../aas-portal/src/app/start/start.actions.ts | 2 +- .../src/app/start/start.component.scss | 14 +- .../aas-portal/src/app/start/start.effects.ts | 16 +- .../aas-portal/src/app/start/start.reducer.ts | 2 +- .../upload-form/upload-form.component.scss | 14 +- .../src/app/view/view.component.scss | 14 +- 37 files changed, 1024 insertions(+), 1025 deletions(-) create mode 100644 projects/aas-lib/src/lib/tree.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 08fa19c2..712806dc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -28,7 +28,7 @@ "CONTENT_ROOT": "projects/aas-server/build", "WEB_ROOT": "projects/aas-portal/dist", "ASSETS": "projects/aas-server/src/assets", - "USER_STORAGE": "mongodb://localhost:27017/aasportal-users", + // "USER_STORAGE": "mongodb://localhost:27017/aasportal-users", // "TEMPLATE_STORAGE": "http://localhost:8080/templates", // "AAS_INDEX": "mysql://localhost:3306", "ENDPOINTS": "[\"file:///endpoints/samples?name=Samples\"]", diff --git a/package-lock.json b/package-lock.json index 2b0b33d0..3d6a4dbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "aas-portal-project", - "version": "3.0.0-development.17", + "version": "3.0.0-development.22", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "aas-portal-project", - "version": "3.0.0-development.17", + "version": "3.0.0-development.22", "license": "Apache-2.0", "workspaces": [ "./projects/common", @@ -29275,6 +29275,7 @@ "tsoa": "^6.0.0", "tsyringe": "^4.8.0", "uuid": "^8.3.2", + "webdav": "^4.10.0", "winston": "^3.11.0", "winston-daily-rotate-file": "^4.7.1", "ws": "^8.16.0", @@ -34949,6 +34950,7 @@ "tsoa": "^6.0.0", "tsyringe": "^4.8.0", "uuid": "^8.3.2", + "webdav": "^4.10.0", "winston": "^3.11.0", "winston-daily-rotate-file": "^4.7.1", "ws": "^8.16.0", diff --git a/projects/aas-lib/src/lib/aas-table/aas-table.component.html b/projects/aas-lib/src/lib/aas-table/aas-table.component.html index 5c519244..21ffa59d 100644 --- a/projects/aas-lib/src/lib/aas-table/aas-table.component.html +++ b/projects/aas-lib/src/lib/aas-table/aas-table.component.html @@ -72,70 +72,71 @@ } - } @else { - +} @else { +
- - - - - - - - @for (row of rows | async; track row) { - + + + + + + + + @for (row of rows | async; track row) { + - + - + - - } - -
- - -
COLUMN_NAME
-
-
COLUMN_ID
-
+ + +
COLUMN_NAME
+
+
COLUMN_ID
+
- - +
-
- @if (!row.isLeaf && !row.expanded && row.hasChildren) { - -
- -
-
- } - @if (!row.isLeaf && !row.expanded && !row.hasChildren) { -
- -
- } - @if (!row.isLeaf && row.expanded) { - -
- -
-
- } - @if (row.isLeaf) { +
+ @if (row.isLeaf) {
- } -
+ }@else { + @if (row.expanded) { + +
+ +
+
+ }@else { + @if (row.hasChildren) { + +
+ +
+
+ }@else { +
+ +
+ } + } + } +
-
-
+
+ +
-
+
{{row.endpoint}}
{{row.id | max:80}}
-
- } + + + } + + + } diff --git a/projects/aas-lib/src/lib/aas-table/aas-table.effects.ts b/projects/aas-lib/src/lib/aas-table/aas-table.effects.ts index f30ed6ad..11740689 100644 --- a/projects/aas-lib/src/lib/aas-table/aas-table.effects.ts +++ b/projects/aas-lib/src/lib/aas-table/aas-table.effects.ts @@ -53,10 +53,10 @@ export class AASTableEffects { const rows = documents.map(document => { const row = map.get(`${document.endpoint}:${document.id}`); if (row) { - return row.document === document ? row : this.cloneD(row, document); + return row.document === document ? row : this.cloneWithNewDocument(row, document); } - return new AASTableRow(document, false, false, false, -1, -1, -1); + return new AASTableRow(document, -1, false, false, false, false, -1, -1, -1); }); return rows; @@ -69,7 +69,7 @@ export class AASTableEffects { documents.forEach(document => { const row = map.get(`${document.endpoint}:${document.id}`); if (row) { - rows.push(row.document === document ? row : this.cloneD(row, document)); + rows.push(row.document === document ? row : this.cloneWithNewDocument(row, document)); } else { nodes.push(document); } @@ -89,6 +89,8 @@ export class AASTableEffects { const children = nodes.filter(node => this.isChild(root, node)); const rootRow = new AASTableRow( root, + -1, + false, false, false, children.length === 0, @@ -115,7 +117,17 @@ export class AASTableEffects { let previous: AASTableRow | null = null; const children = nodes.filter(node => this.isChild(parent, node)); for (const child of children) { - const row = new AASTableRow(child, false, false, children.length === 0, level, -1, -1); + const row = new AASTableRow( + child, + rows.length - 1, + false, + false, + false, + children.length === 0, + level, + -1, + -1, + ); rows.push(row); if (previous) { @@ -142,8 +154,10 @@ export class AASTableEffects { private clone(row: AASTableRow): AASTableRow { return new AASTableRow( row.document, + row.parent, row.selected, row.expanded, + row.highlighted, row.isLeaf, row.level, row.firstChild, @@ -151,11 +165,13 @@ export class AASTableEffects { ); } - private cloneD(row: AASTableRow, document: AASDocument): AASTableRow { + private cloneWithNewDocument(row: AASTableRow, document: AASDocument): AASTableRow { return new AASTableRow( document, + row.parent, row.selected, row.expanded, + row.highlighted, row.isLeaf, row.level, row.firstChild, diff --git a/projects/aas-lib/src/lib/aas-table/aas-table.reducer.ts b/projects/aas-lib/src/lib/aas-table/aas-table.reducer.ts index 3749b5c3..f08d0e00 100644 --- a/projects/aas-lib/src/lib/aas-table/aas-table.reducer.ts +++ b/projects/aas-lib/src/lib/aas-table/aas-table.reducer.ts @@ -9,7 +9,7 @@ import { createReducer, on } from '@ngrx/store'; import { AASDocument } from 'common'; import * as AASTableActions from './aas-table.actions'; -import { AASTableRow, AASTableState } from './aas-table.state'; +import { AASTableRow, AASTableState, AASTableTree } from './aas-table.state'; import { ViewMode } from '../types/view-mode'; const initialState: AASTableState = { @@ -34,110 +34,39 @@ export const aasTableReducer = createReducer( function setViewMode(state: AASTableState, viewMode: ViewMode): AASTableState { return state.viewMode !== viewMode ? { ...state, rows: [], viewMode } : state; } + function setRows(state: AASTableState, rows: AASTableRow[]): AASTableState { return { ...state, rows }; } function setSelections(state: AASTableState, documents: AASDocument[]): AASTableState { - const rows = [...state.rows]; - const set = new Set(documents); - for (let i = 0, n = rows.length; i < n; i++) { - const row = rows[i]; - if (!row.selected && set.has(row.document)) { - rows[i] = clone(row, true); - } else if (row.selected) { - rows[i] = clone(row, false); - } - } - - return { ...state, rows }; + const tree = new AASTableTree(state.rows); + tree.selectedElements = documents; + return { ...state, rows: tree.nodes }; } function toggleSelected(state: AASTableState, row: AASTableRow, altKey: boolean, shiftKey: boolean): AASTableState { - let rows: AASTableRow[]; - if (altKey) { - rows = state.rows.map(item => - item === row ? clone(row, !row.selected) : item.selected ? clone(item, false) : item, - ); - } else if (shiftKey) { - const index = state.rows.indexOf(row); - let begin = index; - let end = index; - const selection = state.rows.map(row => row.selected); - const last = selection.lastIndexOf(true); - if (last >= 0) { - if (last > index) { - begin = index; - end = selection.indexOf(true); - } else if (last < index) { - begin = last; - end = index; - } - } - - rows = []; - for (let i = 0, n = state.rows.length; i < n; i++) { - const row = state.rows[i]; - if (i < begin || i > end) { - rows.push(row.selected ? clone(row, false) : row); - } else { - rows.push(row.selected ? row : clone(row, true)); - } - } - } else { - const i = state.rows.indexOf(row); - rows = [...state.rows]; - rows[i] = clone(row, !row.selected); - } - - return { ...state, rows }; + const tree = new AASTableTree(state.rows); + tree.toggleSelected(row, altKey, shiftKey); + return { ...state, rows: tree.nodes }; } function toggleSelections(state: AASTableState): AASTableState { - const rows = [...state.rows]; - if (rows.length > 0) { - const value = !rows.every(row => row.selected); - for (let index = 0, n = rows.length; index < n; ++index) { - const row = rows[index]; - if (row.selected !== value) { - rows[index] = clone(row, value); - } - } - } - - return { ...state, rows }; -} - -function clone(row: AASTableRow, selected?: boolean): AASTableRow { - if (selected === undefined) { - selected = row.selected; - } - - return new AASTableRow( - row.document, - selected, - row.expanded, - row.isLeaf, - row.level, - row.firstChild, - row.nextSibling, - ); + const tree = new AASTableTree(state.rows); + tree.toggleSelections(); + return { ...state, rows: tree.nodes }; } function expandRow(state: AASTableState, row: AASTableRow): AASTableState { - const rows = [...state.rows]; - const index = rows.indexOf(row); - rows[index] = new AASTableRow(row.document, false, true, row.isLeaf, row.level, row.firstChild, row.nextSibling); - - return { ...state, rows }; + const tree = new AASTableTree(state.rows); + tree.expand(row); + return { ...state, rows: tree.nodes }; } function collapseRow(state: AASTableState, row: AASTableRow): AASTableState { - const rows = [...state.rows]; - const index = rows.indexOf(row); - rows[index] = new AASTableRow(row.document, false, false, row.isLeaf, row.level, row.firstChild, row.nextSibling); - - return { ...state, rows }; + const tree = new AASTableTree(state.rows); + tree.collapse(row); + return { ...state, rows: tree.nodes }; } function setFilter(state: AASTableState, filter?: string): AASTableState { diff --git a/projects/aas-lib/src/lib/aas-table/aas-table.state.ts b/projects/aas-lib/src/lib/aas-table/aas-table.state.ts index a3479ac6..4cb79bf2 100644 --- a/projects/aas-lib/src/lib/aas-table/aas-table.state.ts +++ b/projects/aas-lib/src/lib/aas-table/aas-table.state.ts @@ -8,17 +8,22 @@ import { AASDocument } from 'common'; import { ViewMode } from '../types/view-mode'; +import { Tree, TreeNode } from '../tree'; -export class AASTableRow { +export class AASTableRow extends TreeNode { public constructor( public readonly document: AASDocument, - public readonly selected: boolean, - public readonly expanded: boolean, + parent: number, + selected: boolean, + expanded: boolean, + highlighted: boolean, public readonly isLeaf: boolean, - public readonly level: number, - public firstChild: number, - public nextSibling: number, - ) {} + level: number, + firstChild: number, + nextSibling: number, + ) { + super(document, parent, level, expanded, selected, highlighted, firstChild, nextSibling); + } public get id(): string { return this.document.id; @@ -36,10 +41,6 @@ export class AASTableRow { return this.document.endpoint; } - public get hasChildren(): boolean { - return this.firstChild >= 0; - } - public get state(): 'loaded' | 'unloaded' | 'unavailable' { if (this.document.content === null) { return 'unloaded'; @@ -51,38 +52,41 @@ export class AASTableRow { return 'unavailable'; } +} - public getChildren(rows: AASTableRow[]): AASTableRow[] { - const children: AASTableRow[] = []; - if (this.firstChild >= 0) { - let child = rows[this.firstChild]; - children.push(child); - while (child.nextSibling >= 0) { - child = rows[child.nextSibling]; - children.push(child); - } - } +export class AASTableTree extends Tree { + private _nodes: AASTableRow[]; - return children; + public constructor(nodes: AASTableRow[]) { + super(); + + this._nodes = nodes; } - public getExpanded(rows: AASTableRow[]): AASTableRow[] { - return this.traverse(rows, this, [this]); + public get nodes(): AASTableRow[] { + return this._nodes; } - private traverse(rows: AASTableRow[], row: AASTableRow, expanded: AASTableRow[]): AASTableRow[] { - if (row.firstChild >= 0 && row.expanded) { - let child = rows[row.firstChild]; - expanded.push(child); - this.traverse(rows, child, expanded); - while (child.nextSibling >= 0) { - child = rows[child.nextSibling]; - expanded.push(child); - this.traverse(rows, child, expanded); - } - } + protected override getNodes(): AASTableRow[] { + return this._nodes; + } + + protected override setNodes(nodes: AASTableRow[]): void { + this._nodes = nodes; + } - return expanded; + protected override cloneNode(node: AASTableRow): AASTableRow { + return new AASTableRow( + node.document, + node.parent, + node.selected, + node.expanded, + node.highlighted, + node.isLeaf, + node.level, + node.firstChild, + node.nextSibling, + ); } } diff --git a/projects/aas-lib/src/lib/aas-tree/aas-tree-search.ts b/projects/aas-lib/src/lib/aas-tree/aas-tree-search.ts index a771dc60..01c27239 100644 --- a/projects/aas-lib/src/lib/aas-tree/aas-tree-search.ts +++ b/projects/aas-lib/src/lib/aas-tree/aas-tree-search.ts @@ -105,6 +105,10 @@ export class AASTreeSearch { if (this.rows.length > 0 && this.terms.length > 0) { let match = false; let i = this.index < 0 ? 0 : this.index + 1; + if (i >= this.rows.length) { + i = 0; + } + const start = i; while (this.loop) { if (this.match(this.rows[i])) { diff --git a/projects/aas-lib/src/lib/aas-tree/aas-tree.component.html b/projects/aas-lib/src/lib/aas-tree/aas-tree.component.html index 270617a3..812cfe0d 100644 --- a/projects/aas-lib/src/lib/aas-tree/aas-tree.component.html +++ b/projects/aas-lib/src/lib/aas-tree/aas-tree.component.html @@ -32,30 +32,33 @@
- @if (!node.isLeaf && !node.expanded && node.hasChildren) { - -
- -
-
- } - @if (!node.isLeaf && !node.expanded && !node.hasChildren) { -
- -
- } - @if (!node.isLeaf && node.expanded) { - -
- -
-
- } @if (node.isLeaf) { -
+
+ }@else { + @if (node.expanded) { + +
+ +
+
+ }@else { + @if (node.hasChildren) { + +
+ +
+
+ }@else { +
+ +
+ } + } } +
{{node.abbreviation}}
+ @if (node.hasSemantic) {
{{node.name}} @@ -63,8 +66,7 @@ {{node.typeInfo}} ]
- } - @if (!node.hasSemantic) { + }@else {
{{node.name}}  [ @@ -75,15 +77,17 @@
@if (node.isLeaf) { - - @if (node.displayType === 'text') { -
{{getValue(node)}}
- } - @if (node.displayType === 'boolean') { - - } - @if (node.displayType === 'url') { - {{node.value}} + + @switch(node.displayType) { + @case('boolean') { + + } + @case ('url') { + {{node.value}} + } + @default { +
{{getValue(node)}}
+ } } } diff --git a/projects/aas-lib/src/lib/aas-tree/aas-tree.reducer.ts b/projects/aas-lib/src/lib/aas-tree/aas-tree.reducer.ts index 7da44a72..4428b0eb 100644 --- a/projects/aas-lib/src/lib/aas-tree/aas-tree.reducer.ts +++ b/projects/aas-lib/src/lib/aas-tree/aas-tree.reducer.ts @@ -6,27 +6,11 @@ * *****************************************************************************/ -import { isEqual } from 'lodash-es'; import { createReducer, on } from '@ngrx/store'; -import { - AASDocument, - convertToString, - getAbbreviation, - getLocaleValue, - aas, - isProperty, - isReferenceElement, - isIdentifiable, - selectReferable, - isBooleanType, - toLocale, - toBoolean, - mimeTypeToExtension, -} from 'common'; +import { aas } from 'common'; -import { AASTreeRow, AASTreeState, DisplayType, SearchTerm } from './aas-tree.state'; +import { AASTree, AASTreeRow, AASTreeState, SearchTerm } from './aas-tree.state'; import * as AASTreeActions from './aas-tree.actions'; -import { basename, normalize } from '../convert'; const initialState: AASTreeState = { rows: [], @@ -48,7 +32,8 @@ export const aasTreeReducer = createReducer( on(AASTreeActions.toggleSelections, state => toggleSelections(state)), on(AASTreeActions.updateRows, (state, { document, localeId }) => { try { - return updateRows(state, document, localeId); + const tree = AASTree.from(document, localeId); + return { ...state, rows: tree.nodes, error: null }; } catch (error) { return { ...state, error }; } @@ -56,538 +41,40 @@ export const aasTreeReducer = createReducer( on(AASTreeActions.setSelectedElements, (state, { elements }) => setSelectedElements(state, elements)), ); -function updateRows(state: AASTreeState, document: AASDocument | null, localeId: string): AASTreeState { - const rows: AASTreeRow[] = []; - const env = document?.content; - if (env) { - for (const shell of env.assetAdministrationShells) { - const row = createRow(shell, -1, 0, true); - rows.push(row); - row.firstChild = hasChildren(shell) ? rows.length : -1; - traverse(shell, rows.length - 1, 1); - for (const stateRow of state.rows) { - if (stateRow.expanded || stateRow.selected) { - const row = findRow(rows, stateRow.element); - if (row) { - row.expanded = stateRow.expanded; - row.selected = stateRow.selected; - } - } - } - } - } - - return { ...state, rows, error: null }; - - function traverse(element: aas.Referable, parent: number, level: number): void { - let previous: AASTreeRow | null = null; - for (const child of getChildren(element)) { - const row = createRow(child, parent, level, false); - rows.push(row); - if (previous) { - previous.nextSibling = rows.length - 1; - } - - const descendants = getChildren(child); - if (descendants.length > 0) { - row.firstChild = rows.length; - traverse(child, rows.length - 1, level + 1); - } - - previous = row; - } - } - - function hasChildren(referable: aas.Referable): boolean { - switch (referable.modelType) { - case 'AssetAdministrationShell': { - const shell = referable as aas.AssetAdministrationShell; - return shell.submodels != null && shell.submodels.length > 0; - } - case 'Submodel': { - const submodel = referable as aas.Submodel; - return submodel.submodelElements != null && submodel.submodelElements.length > 0; - } - case 'SubmodelElementCollection': { - const collection = referable as aas.SubmodelElementCollection; - return collection.value != null && collection.value.length > 0; - } - case 'SubmodelElementList': { - const list = referable as aas.SubmodelElementList; - return list.value != null && list.value.length > 0; - } - default: - return false; - } - } - - function getChildren(referable: aas.Referable): aas.Referable[] { - switch (referable.modelType) { - case 'AssetAdministrationShell': { - const shell = referable as aas.AssetAdministrationShell; - const children: aas.Referable[] = []; - if (shell.submodels) { - for (const reference of shell.submodels) { - const submodel = selectReferable(env!, reference); - if (submodel) { - children.push(submodel); - } - } - } - - return children; - } - case 'Submodel': - return (referable as aas.Submodel).submodelElements ?? []; - case 'SubmodelElementCollection': - return (referable as aas.SubmodelElementCollection).value ?? []; - case 'SubmodelElementList': - return (referable as aas.SubmodelElementList).value ?? []; - default: - return []; - } - } - - function createRow(element: aas.Referable, parent: number, level: number, expanded: boolean): AASTreeRow { - let valueType = DisplayType.undefined; - let isLeaf = true; - switch (element.modelType) { - case 'AssetAdministrationShell': - case 'Submodel': - case 'SubmodelElementCollection': - case 'SubmodelElementList': - isLeaf = false; - break; - case 'Property': - valueType = getPropertyDisplayType(element as aas.Property); - break; - case 'MultiLanguageProperty': - case 'Range': - valueType = DisplayType.Text; - break; - case 'Entity': - case 'ReferenceElement': - case 'Operation': - valueType = DisplayType.Url; - break; - case 'File': { - const file = element as aas.File; - valueType = file.contentType && file.value ? DisplayType.Url : DisplayType.Text; - break; - } - case 'Blob': { - const blob = element as aas.Blob; - valueType = blob.contentType ? DisplayType.Url : DisplayType.Text; - break; - } - } - - return new AASTreeRow( - `row_${rows.length + 1}`, - element, - expanded, - false, - false, - level, - getAbbreviation(element.modelType) ?? '', - element.idShort, - getTypeInfo(element), - getValue(element, localeId), - valueType, - isLeaf, - parent, - -1, - -1, - ); - - function getPropertyDisplayType(property: aas.Property): DisplayType { - switch (property.valueType) { - case 'xs:anyURI': - return DisplayType.Url; - case 'xs:boolean': - return DisplayType.Boolean; - default: - return DisplayType.Text; - } - } - } - - function findRow(rows: AASTreeRow[], referable: aas.Referable): AASTreeRow | undefined { - return rows.find(row => isEqual(createReference(row.element), createReference(referable))); - } - - function createReference(referable: aas.Referable): aas.Reference { - let keys: aas.Key[]; - if (referable.parent) { - keys = [ - ...referable.parent.keys.map(key => ({ ...key })), - { - type: referable.modelType as aas.KeyTypes, - value: referable.idShort, - }, - ]; - } else if (isIdentifiable(referable)) { - keys = [ - { - type: referable.modelType as aas.KeyTypes, - value: referable.id, - }, - ]; - } else { - throw new Error('Unexpected referable.'); - } - - return { type: 'ModelReference', keys }; - } -} - function expandRow(state: AASTreeState, arg: number | AASTreeRow): AASTreeState { - const rows = [...state.rows]; - const ancestors: AASTreeRow[] = []; - let row = typeof arg === 'number' ? state.rows[arg] : arg; - if (!row.expanded) { - expand(row); - } - - let parentRow = row.parent >= 0 ? state.rows[row.parent] : null; - while (parentRow) { - if (parentRow.expanded) { - break; - } - - ancestors.push(parentRow); - row = parentRow; - parentRow = row.parent >= 0 ? state.rows[row.parent] : null; - } - - while (ancestors.length > 0) { - const row = ancestors.pop(); - if (!row) { - break; - } - - expand(row); - } - - return { ...state, rows, error: null }; - - function expand(row: AASTreeRow) { - const index = rows.indexOf(row); - rows[index] = new AASTreeRow( - row.id, - row.element, - true, - row.selected, - row.highlighted, - row.level, - row.abbreviation, - row.name, - row.typeInfo, - row.value, - row.displayType, - row.isLeaf, - row.parent, - row.firstChild, - row.nextSibling, - ); - } + const tree = new AASTree(state.rows); + tree.expand(arg); + return { ...state, rows: tree.nodes, error: null }; } function collapseRow(state: AASTreeState, row: AASTreeRow): AASTreeState { - const rows = [...state.rows]; - const index = rows.indexOf(row); - rows[index] = new AASTreeRow( - row.id, - row.element, - false, - row.selected, - row.highlighted, - row.level, - row.abbreviation, - row.name, - row.typeInfo, - row.value, - row.displayType, - row.isLeaf, - row.parent, - row.firstChild, - row.nextSibling, - ); - - return { ...state, rows, error: null }; + const tree = new AASTree(state.rows); + tree.collapse(row); + return { ...state, rows: tree.nodes, error: null }; } function collapse(state: AASTreeState): AASTreeState { - const rows = state.rows.map((row, index) => { - if (index === 0) { - if (!row.expanded) { - return new AASTreeRow( - row.id, - row.element, - true, - row.selected, - row.highlighted, - row.level, - row.abbreviation, - row.name, - row.typeInfo, - row.value, - row.displayType, - row.isLeaf, - row.parent, - row.firstChild, - row.nextSibling, - ); - } - } else if (!row.isLeaf && row.expanded) { - return new AASTreeRow( - row.id, - row.element, - false, - row.selected, - row.highlighted, - row.level, - row.abbreviation, - row.name, - row.typeInfo, - row.value, - row.displayType, - row.isLeaf, - row.parent, - row.firstChild, - row.nextSibling, - ); - } - - return row; - }); - - return { ...state, rows, error: null }; + const tree = new AASTree(state.rows); + tree.collapse(); + return { ...state, rows: tree.nodes, error: null }; } function toggleSelected(state: AASTreeState, row: AASTreeRow, altKey: boolean, shiftKey: boolean): AASTreeState { - let rows: AASTreeRow[]; - if (altKey) { - rows = state.rows.map(item => - item === row ? clone(row, !row.selected) : item.selected ? clone(item, false) : item, - ); - } else if (shiftKey) { - const index = state.rows.indexOf(row); - let begin = index; - let end = index; - const selection = state.rows.map(row => row.selected); - const last = selection.lastIndexOf(true); - if (last >= 0) { - if (last > index) { - begin = index; - end = selection.indexOf(true); - } else if (last < index) { - begin = last; - end = index; - } - } - - rows = []; - for (let i = 0, n = state.rows.length; i < n; i++) { - const row = state.rows[i]; - if (i < begin || i > end) { - rows.push(row.selected ? clone(row, false) : row); - } else { - rows.push(row.selected ? row : clone(row, true)); - } - } - } else { - const i = state.rows.indexOf(row); - rows = [...state.rows]; - rows[i] = clone(row, !row.selected); - } - - return { ...state, rows, error: null }; + const tree = new AASTree(state.rows); + tree.toggleSelected(row, altKey, shiftKey); + return { ...state, rows: tree.nodes, error: null }; } function toggleSelections(state: AASTreeState): AASTreeState { - const rows = [...state.rows]; - if (rows.length > 0) { - const value = !state.rows.every(row => row.selected); - for (let index = 0, n = rows.length; index < n; ++index) { - const row = rows[index]; - if (row.selected !== value) { - rows[index] = clone(row, value); - } - } - } - - return { ...state, rows, error: null }; + const tree = new AASTree(state.rows); + tree.toggleSelections(); + return { ...state, rows: tree.nodes, error: null }; } function setSelectedElements(state: AASTreeState, elements: aas.Referable[]): AASTreeState { - const rows = [...state.rows]; - const set = new Set(elements); - for (let i = 0, n = rows.length; i < n; i++) { - const row = rows[i]; - if (set.has(row.element)) { - if (!row.selected) { - rows[i] = clone(row, true); - } - } else if (row.selected) { - rows[i] = clone(row, false); - } - } - - return { ...state, rows }; -} - -function clone(row: AASTreeRow, selected: boolean): AASTreeRow { - return new AASTreeRow( - row.id, - row.element, - row.expanded, - selected, - row.highlighted, - row.level, - row.abbreviation, - row.name, - row.typeInfo, - row.value, - row.displayType, - row.isLeaf, - row.parent, - row.firstChild, - row.nextSibling, - ); -} - -function getTypeInfo(referable: aas.Referable | null): string { - let value: string; - if (referable) { - switch (referable.modelType) { - case 'AssetAdministrationShell': - value = (referable as aas.Submodel).id; - break; - case 'Submodel': - value = `Semantic ID: ${referenceToString((referable as aas.Submodel).semanticId)}`; - break; - case 'SubmodelElementCollection': - value = (referable as aas.SubmodelElementCollection).value?.length.toString() ?? '0'; - break; - case 'SubmodelElementList': - value = (referable as aas.SubmodelElementList).value?.length.toString() ?? '0'; - break; - case 'Property': - value = (referable as aas.Property).valueType; - break; - case 'Range': - value = (referable as aas.Range).valueType; - break; - case 'File': - value = (referable as aas.File).contentType; - break; - case 'Blob': - value = (referable as aas.Blob).contentType; - break; - case 'MultiLanguageProperty': { - const mlp = referable as aas.MultiLanguageProperty; - value = ''; - if (mlp && Array.isArray(mlp.value)) { - value += `${mlp.value.map(item => item.language).join(', ')}`; - } - break; - } - case 'Entity': { - const entity = referable as aas.Entity; - value = ''; - if (entity?.globalAssetId) { - value = entity.globalAssetId; - } - break; - } - case 'Operation': { - const operation = referable as aas.Operation; - value = ''; - if (operation.inputVariables && operation.inputVariables.length > 0) { - value += '(' + operation.inputVariables.map(v => variableToString(v.value)).join(', ') + ')'; - } - - if (operation.outputVariables && operation.outputVariables.length === 1) { - value += `: ${variableToString(operation.outputVariables[0].value)}`; - } else if (operation.outputVariables && operation.outputVariables.length > 1) { - value += ': {' + operation.outputVariables.map(v => variableToString(v.value)).join(', ') + '}'; - } - break; - } - default: - value = '-'; - break; - } - } else { - value = '-'; - } - - return value; - - function variableToString(value: aas.SubmodelElement): string { - if (isProperty(value)) { - return `${value.idShort}: ${value.valueType}`; - } - - if (isReferenceElement(value)) { - return `${value.idShort}: ${value?.value?.keys.map(key => key.value).join('/')}`; - } - - return `${value.idShort}: ${value.modelType}`; - } - - function referenceToString(reference?: aas.Reference): string { - return reference?.keys.map(key => key.value).join('/') ?? '-'; - } -} - -function getValue(referable: aas.Referable | null, localeId: string): boolean | string | undefined { - if (referable) { - switch (referable.modelType) { - case 'Property': - return getPropertyValue(referable as aas.Property, localeId); - case 'Range': { - const range = referable as aas.Range; - return `${convertToString(range.min, localeId)} ... ${convertToString(range.max, localeId)}`; - } - case 'File': { - const file = referable as aas.File; - return file.value ? basename(normalize(file.value)) : '-'; - } - case 'Blob': { - const blob = referable as aas.Blob; - const extension = mimeTypeToExtension(blob.contentType) ?? ''; - return blob.contentType ? `${blob.idShort}${extension}` : '-'; - } - case 'ReferenceElement': - return (referable as aas.ReferenceElement).value.keys.map(item => item.value).join('/'); - case 'RelationshipElement': - return getRelationshipElementValue(referable as aas.RelationshipElement); - case 'MultiLanguageProperty': - return getLocaleValue((referable as aas.MultiLanguageProperty).value, localeId) ?? '-'; - case 'Entity': - return (referable as aas.Entity).globalAssetId ?? '-'; - default: - return '-'; - } - } - - return ''; - - function getPropertyValue(property: aas.Property, localeId: string): string | boolean | undefined { - if (isBooleanType(property.valueType)) { - return toBoolean(property.value); - } else { - return toLocale(property.value, property.valueType, localeId); - } - } - - function getRelationshipElementValue(relationship: aas.RelationshipElement): string { - const first = relationship.first.keys.map(key => key.value).join('/'); - const second = relationship.second.keys.map(key => key.value).join('/'); - return `1. ${first}; 2. ${second}`; - } + const tree = new AASTree(state.rows); + tree.selectedElements = elements; + return { ...state, rows: tree.nodes }; } function setSearchText(state: AASTreeState, terms: SearchTerm[]): AASTreeState { @@ -595,65 +82,7 @@ function setSearchText(state: AASTreeState, terms: SearchTerm[]): AASTreeState { } function setMatchIndex(state: AASTreeState, index: number): AASTreeState { - if (index >= 0) { - return { - ...state, - rows: updateHighlighted(index), - index: index, - error: null, - }; - } else { - return { - rows: updateHighlighted(-1), - index: -1, - terms: [], - error: null, - }; - } - - function updateHighlighted(index: number): AASTreeRow[] { - const rows = [...state.rows]; - for (let i = 0; i < rows.length; i++) { - const row = rows[i]; - if (i === index) { - rows[i] = new AASTreeRow( - row.id, - row.element, - row.expanded, - row.selected, - true, - row.level, - row.abbreviation, - row.name, - row.typeInfo, - row.value, - row.displayType, - row.isLeaf, - row.parent, - row.firstChild, - row.nextSibling, - ); - } else if (rows[i].highlighted) { - rows[i] = new AASTreeRow( - row.id, - row.element, - row.expanded, - row.selected, - false, - row.level, - row.abbreviation, - row.name, - row.typeInfo, - row.value, - row.displayType, - row.isLeaf, - row.parent, - row.firstChild, - row.nextSibling, - ); - } - } - - return rows; - } + const tree = new AASTree(state.rows); + tree.highlight(index); + return { ...state, rows: tree.nodes, index }; } diff --git a/projects/aas-lib/src/lib/aas-tree/aas-tree.selectors.ts b/projects/aas-lib/src/lib/aas-tree/aas-tree.selectors.ts index 5d4c40db..b21b35e7 100644 --- a/projects/aas-lib/src/lib/aas-tree/aas-tree.selectors.ts +++ b/projects/aas-lib/src/lib/aas-tree/aas-tree.selectors.ts @@ -7,7 +7,7 @@ *****************************************************************************/ import { createSelector } from '@ngrx/store'; -import { AASTreeFeatureState } from './aas-tree.state'; +import { AASTree, AASTreeFeatureState } from './aas-tree.state'; const getState = (state: AASTreeFeatureState) => state.tree; const getRows = (state: AASTreeFeatureState) => state.tree.rows; @@ -31,7 +31,7 @@ export const selectRow = (index: number) => { return createSelector(getRows, rows => rows[index]); }; -export const selectNodes = createSelector(getRows, rows => rows.find(row => row.level === 0)?.getExpanded(rows) ?? []); +export const selectNodes = createSelector(getRows, rows => new AASTree(rows).expanded); export const selectSelectedElements = createSelector(getRows, rows => rows.filter(row => row.selected).map(item => item.element), diff --git a/projects/aas-lib/src/lib/aas-tree/aas-tree.state.ts b/projects/aas-lib/src/lib/aas-tree/aas-tree.state.ts index 100e6181..f35c9f97 100644 --- a/projects/aas-lib/src/lib/aas-tree/aas-tree.state.ts +++ b/projects/aas-lib/src/lib/aas-tree/aas-tree.state.ts @@ -6,29 +6,49 @@ * *****************************************************************************/ -import { aas, isSubmodel } from 'common'; +import { + AASDocument, + aas, + convertToString, + getAbbreviation, + getLocaleValue, + isBooleanType, + isIdentifiable, + isProperty, + isReferenceElement, + isSubmodel, + mimeTypeToExtension, + selectReferable, + toBoolean, + toLocale, +} from 'common'; import { resolveSemanticId, supportedSubmodelTemplates } from '../submodel-template/submodel-template'; +import { Tree, TreeNode } from '../tree'; +import { basename, normalize } from '../convert'; +import { isEqual } from 'lodash-es'; -export class AASTreeRow { +export class AASTreeRow extends TreeNode { public constructor( public readonly id: string, - public readonly element: aas.Referable, - public expanded: boolean, - public selected: boolean, - public readonly highlighted: boolean, - public readonly level: number, + element: aas.Referable, + expanded: boolean, + selected: boolean, + highlighted: boolean, + level: number, public readonly abbreviation: string, public readonly name: string, public readonly typeInfo: string, public readonly value: string | boolean | undefined, public readonly displayType: DisplayType, public readonly isLeaf: boolean, - public readonly parent: number, - public firstChild: number, - public nextSibling: number, - ) {} + parent: number, + firstChild: number, + nextSibling: number, + ) { + super(element, parent, level, expanded, selected, highlighted, firstChild, nextSibling); + } - public get hasChildren(): boolean { + public override get hasChildren(): boolean { return this.firstChild >= 0; } @@ -41,39 +61,6 @@ export class AASTreeRow { return false; } - - public getChildren(rows: AASTreeRow[]): AASTreeRow[] { - const children: AASTreeRow[] = []; - if (this.firstChild >= 0) { - let child = rows[this.firstChild]; - children.push(child); - while (child.nextSibling >= 0) { - child = rows[child.nextSibling]; - children.push(child); - } - } - - return children; - } - - public getExpanded(rows: AASTreeRow[]): AASTreeRow[] { - return this.traverse(rows, this, [this]); - } - - private traverse(rows: AASTreeRow[], row: AASTreeRow, expanded: AASTreeRow[]): AASTreeRow[] { - if (row.firstChild >= 0 && row.expanded) { - let child = rows[row.firstChild]; - expanded.push(child); - this.traverse(rows, child, expanded); - while (child.nextSibling >= 0) { - child = rows[child.nextSibling]; - expanded.push(child); - this.traverse(rows, child, expanded); - } - } - - return expanded; - } } export enum DisplayType { @@ -107,3 +94,377 @@ export interface AASTreeState { export interface AASTreeFeatureState { tree: AASTreeState; } + +class TreeInitialize { + private readonly rows: AASTreeRow[] = []; + public constructor( + private readonly env: aas.Environment, + private readonly language: string, + ) {} + + public get(): AASTreeRow[] { + for (const shell of this.env.assetAdministrationShells) { + const row = this.createRow(shell, -1, 0, true); + this.rows.push(row); + row.firstChild = this.hasChildren(shell) ? this.rows.length : -1; + this.traverse(shell, this.rows.length - 1, 1); + for (const stateRow of this.rows) { + if (stateRow.expanded || stateRow.selected) { + const row = this.findRow(this.rows, stateRow.element); + if (row) { + row.expanded = stateRow.expanded; + row.selected = stateRow.selected; + } + } + } + } + + return this.rows; + } + + private createRow(element: aas.Referable, parent: number, level: number, expanded: boolean): AASTreeRow { + let valueType = DisplayType.undefined; + let isLeaf = true; + switch (element.modelType) { + case 'AssetAdministrationShell': + case 'Submodel': + case 'SubmodelElementCollection': + case 'SubmodelElementList': + isLeaf = false; + break; + case 'Property': + valueType = this.getPropertyDisplayType(element as aas.Property); + break; + case 'MultiLanguageProperty': + case 'Range': + valueType = DisplayType.Text; + break; + case 'Entity': + case 'ReferenceElement': + case 'Operation': + valueType = DisplayType.Url; + break; + case 'File': { + const file = element as aas.File; + valueType = file.contentType && file.value ? DisplayType.Url : DisplayType.Text; + break; + } + case 'Blob': { + const blob = element as aas.Blob; + valueType = blob.contentType ? DisplayType.Url : DisplayType.Text; + break; + } + } + + return new AASTreeRow( + `row_${this.rows.length + 1}`, + element, + expanded, + false, + false, + level, + getAbbreviation(element.modelType) ?? '', + element.idShort, + this.getTypeInfo(element), + this.getValue(element, this.language), + valueType, + isLeaf, + parent, + -1, + -1, + ); + } + + private traverse(element: aas.Referable, parent: number, level: number): void { + let previous: AASTreeRow | null = null; + for (const child of this.getChildren(element)) { + const row = this.createRow(child, parent, level, false); + this.rows.push(row); + if (previous) { + previous.nextSibling = this.rows.length - 1; + } + + const descendants = this.getChildren(child); + if (descendants.length > 0) { + row.firstChild = this.rows.length; + this.traverse(child, this.rows.length - 1, level + 1); + } + + previous = row; + } + } + + private getChildren(referable: aas.Referable): aas.Referable[] { + switch (referable.modelType) { + case 'AssetAdministrationShell': { + const shell = referable as aas.AssetAdministrationShell; + const children: aas.Referable[] = []; + if (shell.submodels) { + for (const reference of shell.submodels) { + const submodel = selectReferable(this.env!, reference); + if (submodel) { + children.push(submodel); + } + } + } + + return children; + } + case 'Submodel': + return (referable as aas.Submodel).submodelElements ?? []; + case 'SubmodelElementCollection': + return (referable as aas.SubmodelElementCollection).value ?? []; + case 'SubmodelElementList': + return (referable as aas.SubmodelElementList).value ?? []; + default: + return []; + } + } + + private findRow(rows: AASTreeRow[], referable: aas.Referable): AASTreeRow | undefined { + return rows.find(row => isEqual(this.createReference(row.element), this.createReference(referable))); + } + + private createReference(referable: aas.Referable): aas.Reference { + let keys: aas.Key[]; + if (referable.parent) { + keys = [ + ...referable.parent.keys.map(key => ({ ...key })), + { + type: referable.modelType as aas.KeyTypes, + value: referable.idShort, + }, + ]; + } else if (isIdentifiable(referable)) { + keys = [ + { + type: referable.modelType as aas.KeyTypes, + value: referable.id, + }, + ]; + } else { + throw new Error('Unexpected referable.'); + } + + return { type: 'ModelReference', keys }; + } + + private getPropertyDisplayType(property: aas.Property): DisplayType { + switch (property.valueType) { + case 'xs:anyURI': + return DisplayType.Url; + case 'xs:boolean': + return DisplayType.Boolean; + default: + return DisplayType.Text; + } + } + + private getValue(referable: aas.Referable | null, localeId: string): boolean | string | undefined { + if (referable) { + switch (referable.modelType) { + case 'Property': + return this.getPropertyValue(referable as aas.Property, localeId); + case 'Range': { + const range = referable as aas.Range; + return `${convertToString(range.min, localeId)} ... ${convertToString(range.max, localeId)}`; + } + case 'File': { + const file = referable as aas.File; + return file.value ? basename(normalize(file.value)) : '-'; + } + case 'Blob': { + const blob = referable as aas.Blob; + const extension = mimeTypeToExtension(blob.contentType) ?? ''; + return blob.contentType ? `${blob.idShort}${extension}` : '-'; + } + case 'ReferenceElement': + return (referable as aas.ReferenceElement).value.keys.map(item => item.value).join('/'); + case 'RelationshipElement': + return this.getRelationshipElementValue(referable as aas.RelationshipElement); + case 'MultiLanguageProperty': + return getLocaleValue((referable as aas.MultiLanguageProperty).value, localeId) ?? '-'; + case 'Entity': + return (referable as aas.Entity).globalAssetId ?? '-'; + default: + return '-'; + } + } + + return ''; + } + + private hasChildren(referable: aas.Referable): boolean { + switch (referable.modelType) { + case 'AssetAdministrationShell': { + const shell = referable as aas.AssetAdministrationShell; + return shell.submodels != null && shell.submodels.length > 0; + } + case 'Submodel': { + const submodel = referable as aas.Submodel; + return submodel.submodelElements != null && submodel.submodelElements.length > 0; + } + case 'SubmodelElementCollection': { + const collection = referable as aas.SubmodelElementCollection; + return collection.value != null && collection.value.length > 0; + } + case 'SubmodelElementList': { + const list = referable as aas.SubmodelElementList; + return list.value != null && list.value.length > 0; + } + default: + return false; + } + } + + private getPropertyValue(property: aas.Property, localeId: string): string | boolean | undefined { + if (isBooleanType(property.valueType)) { + return toBoolean(property.value); + } else { + return toLocale(property.value, property.valueType, localeId); + } + } + + private getRelationshipElementValue(relationship: aas.RelationshipElement): string { + const first = relationship.first.keys.map(key => key.value).join('/'); + const second = relationship.second.keys.map(key => key.value).join('/'); + return `1. ${first}; 2. ${second}`; + } + + private getTypeInfo(referable: aas.Referable | null): string { + let value: string; + if (referable) { + switch (referable.modelType) { + case 'AssetAdministrationShell': + value = (referable as aas.Submodel).id; + break; + case 'Submodel': + value = `Semantic ID: ${this.referenceToString((referable as aas.Submodel).semanticId)}`; + break; + case 'SubmodelElementCollection': + value = (referable as aas.SubmodelElementCollection).value?.length.toString() ?? '0'; + break; + case 'SubmodelElementList': + value = (referable as aas.SubmodelElementList).value?.length.toString() ?? '0'; + break; + case 'Property': + value = (referable as aas.Property).valueType; + break; + case 'Range': + value = (referable as aas.Range).valueType; + break; + case 'File': + value = (referable as aas.File).contentType; + break; + case 'Blob': + value = (referable as aas.Blob).contentType; + break; + case 'MultiLanguageProperty': { + const mlp = referable as aas.MultiLanguageProperty; + value = ''; + if (mlp && Array.isArray(mlp.value)) { + value += `${mlp.value.map(item => item.language).join(', ')}`; + } + break; + } + case 'Entity': { + const entity = referable as aas.Entity; + value = ''; + if (entity?.globalAssetId) { + value = entity.globalAssetId; + } + break; + } + case 'Operation': { + const operation = referable as aas.Operation; + value = ''; + if (operation.inputVariables && operation.inputVariables.length > 0) { + value += + '(' + operation.inputVariables.map(v => this.variableToString(v.value)).join(', ') + ')'; + } + + if (operation.outputVariables && operation.outputVariables.length === 1) { + value += `: ${this.variableToString(operation.outputVariables[0].value)}`; + } else if (operation.outputVariables && operation.outputVariables.length > 1) { + value += + ': {' + operation.outputVariables.map(v => this.variableToString(v.value)).join(', ') + '}'; + } + break; + } + default: + value = '-'; + break; + } + } else { + value = '-'; + } + + return value; + } + + private variableToString(value: aas.SubmodelElement): string { + if (isProperty(value)) { + return `${value.idShort}: ${value.valueType}`; + } + + if (isReferenceElement(value)) { + return `${value.idShort}: ${value?.value?.keys.map(key => key.value).join('/')}`; + } + + return `${value.idShort}: ${value.modelType}`; + } + + private referenceToString(reference?: aas.Reference): string { + return reference?.keys.map(key => key.value).join('/') ?? '-'; + } +} + +export class AASTree extends Tree { + private _nodes: AASTreeRow[]; + + public constructor(nodes: AASTreeRow[]) { + super(); + + this._nodes = nodes; + } + + public get nodes(): AASTreeRow[] { + return this._nodes; + } + + public static from(document: AASDocument | null, language: string): AASTree { + if (!document || !document.content) { + return new AASTree([]); + } + + return new AASTree(new TreeInitialize(document.content, language).get()); + } + + protected override getNodes(): AASTreeRow[] { + return this._nodes; + } + + protected override setNodes(nodes: AASTreeRow[]): void { + this._nodes = nodes; + } + + protected override cloneNode(node: AASTreeRow): AASTreeRow { + return new AASTreeRow( + node.id, + node.element, + node.expanded, + node.selected, + node.highlighted, + node.level, + node.abbreviation, + node.name, + node.typeInfo, + node.value, + node.displayType, + node.isLeaf, + node.parent, + node.firstChild, + node.nextSibling, + ); + } +} diff --git a/projects/aas-lib/src/lib/aas-tree/operation-call-form/operation-call-form.component.scss b/projects/aas-lib/src/lib/aas-tree/operation-call-form/operation-call-form.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/aas-tree/operation-call-form/operation-call-form.component.scss +++ b/projects/aas-lib/src/lib/aas-tree/operation-call-form/operation-call-form.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/aas-tree/show-image-form/show-image-form.component.scss b/projects/aas-lib/src/lib/aas-tree/show-image-form/show-image-form.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/aas-tree/show-image-form/show-image-form.component.scss +++ b/projects/aas-lib/src/lib/aas-tree/show-image-form/show-image-form.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/aas-tree/show-video-form/show-video-form.component.scss b/projects/aas-lib/src/lib/aas-tree/show-video-form/show-video-form.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/aas-tree/show-video-form/show-video-form.component.scss +++ b/projects/aas-lib/src/lib/aas-tree/show-video-form/show-video-form.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/auth/auth.component.scss b/projects/aas-lib/src/lib/auth/auth.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/auth/auth.component.scss +++ b/projects/aas-lib/src/lib/auth/auth.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/auth/login-form/login-form.component.scss b/projects/aas-lib/src/lib/auth/login-form/login-form.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/auth/login-form/login-form.component.scss +++ b/projects/aas-lib/src/lib/auth/login-form/login-form.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/auth/profile-form/profile-form.component.scss b/projects/aas-lib/src/lib/auth/profile-form/profile-form.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/auth/profile-form/profile-form.component.scss +++ b/projects/aas-lib/src/lib/auth/profile-form/profile-form.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/auth/register-form/register-form.component.scss b/projects/aas-lib/src/lib/auth/register-form/register-form.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/auth/register-form/register-form.component.scss +++ b/projects/aas-lib/src/lib/auth/register-form/register-form.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/customer-feedback/customer-feedback.component.scss b/projects/aas-lib/src/lib/customer-feedback/customer-feedback.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/customer-feedback/customer-feedback.component.scss +++ b/projects/aas-lib/src/lib/customer-feedback/customer-feedback.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/digital-nameplate/digital-nameplate.component.scss b/projects/aas-lib/src/lib/digital-nameplate/digital-nameplate.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/digital-nameplate/digital-nameplate.component.scss +++ b/projects/aas-lib/src/lib/digital-nameplate/digital-nameplate.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/library-table/library-table.component.scss b/projects/aas-lib/src/lib/library-table/library-table.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/library-table/library-table.component.scss +++ b/projects/aas-lib/src/lib/library-table/library-table.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/localize/localize.component.scss b/projects/aas-lib/src/lib/localize/localize.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/localize/localize.component.scss +++ b/projects/aas-lib/src/lib/localize/localize.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/message-table/message-table.component.scss b/projects/aas-lib/src/lib/message-table/message-table.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/message-table/message-table.component.scss +++ b/projects/aas-lib/src/lib/message-table/message-table.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/notify/notify.component.scss b/projects/aas-lib/src/lib/notify/notify.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/notify/notify.component.scss +++ b/projects/aas-lib/src/lib/notify/notify.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/secured-image/secured-image.component.scss b/projects/aas-lib/src/lib/secured-image/secured-image.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-lib/src/lib/secured-image/secured-image.component.scss +++ b/projects/aas-lib/src/lib/secured-image/secured-image.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-lib/src/lib/tree.ts b/projects/aas-lib/src/lib/tree.ts new file mode 100644 index 00000000..8f0ac7f0 --- /dev/null +++ b/projects/aas-lib/src/lib/tree.ts @@ -0,0 +1,247 @@ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ + +export abstract class TreeNode { + protected constructor( + public readonly element: TElement, + public readonly parent: number, + public readonly level: number, + public expanded: boolean, + public selected: boolean, + public highlighted: boolean, + public firstChild: number, + public nextSibling: number, + ) {} + + public get hasChildren(): boolean { + return this.firstChild >= 0; + } + + public abstract get isLeaf(): boolean; +} + +export abstract class Tree> { + public get expanded(): TNode[] { + const root = this.getNodes().find(node => node.level === 0); + if (!root) { + return []; + } + + return this.traverseNodes(root, [root]); + } + + public get selectedElements(): TElement[] { + return this.getNodes() + .filter(node => node.selected) + .map(node => node.element); + } + + public set selectedElements(elements: TElement[]) { + const nodes = [...this.getNodes()]; + const set = new Set(elements); + for (let i = 0, n = nodes.length; i < n; i++) { + const row = nodes[i]; + if (set.has(row.element)) { + if (!row.selected) { + nodes[i] = this.clone(row, true); + } + } else if (row.selected) { + nodes[i] = this.clone(row, false); + } + } + + this.setNodes(nodes); + } + + public getChildren(node: TNode): TNode[] { + const children: TNode[] = []; + const nodes = this.getNodes(); + if (node.firstChild >= 0) { + let child = nodes[node.firstChild]; + children.push(child); + while (child.nextSibling >= 0) { + child = nodes[child.nextSibling]; + children.push(child); + } + } + + return children; + } + + public get highlighted(): TNode[] { + return []; + } + + public expand(arg: number | TNode): void { + const nodes = [...this.getNodes()]; + const ancestors: TNode[] = []; + let node = typeof arg === 'number' ? nodes[arg] : arg; + if (!node.expanded) { + this.expandNode(node, nodes); + } + + let parentRow = node.parent >= 0 ? nodes[node.parent] : null; + while (parentRow) { + if (parentRow.expanded) { + break; + } + + ancestors.push(parentRow); + node = parentRow; + parentRow = node.parent >= 0 ? nodes[node.parent] : null; + } + + while (ancestors.length > 0) { + const ancestor = ancestors.pop(); + if (!ancestor) { + break; + } + + this.expandNode(ancestor, nodes); + } + + this.setNodes(nodes); + } + + public collapse(node?: TNode): void { + let nodes: TNode[]; + if (node) { + nodes = [...this.getNodes()]; + const index = nodes.indexOf(node); + const clone = this.cloneNode(node); + clone.expanded = false; + nodes[index] = clone; + } else { + nodes = this.getNodes().map((node, index) => { + if (index === 0) { + if (!node.expanded) { + const clone = this.cloneNode(node); + clone.expanded = true; + return clone; + } + } else if (!node.isLeaf && node.expanded) { + const clone = this.cloneNode(node); + clone.expanded = false; + return clone; + } + + return node; + }); + } + + this.setNodes(nodes); + } + + public toggleSelected(node: TNode, altKey: boolean, shiftKey: boolean): void { + let nodes: TNode[]; + if (altKey) { + nodes = this.getNodes().map(item => + item === node ? this.clone(node, !node.selected) : item.selected ? this.clone(item, false) : item, + ); + } else if (shiftKey) { + const index = this.getNodes().indexOf(node); + let begin = index; + let end = index; + const selection = this.getNodes().map(row => row.selected); + const last = selection.lastIndexOf(true); + if (last >= 0) { + if (last > index) { + begin = index; + end = selection.indexOf(true); + } else if (last < index) { + begin = last; + end = index; + } + } + + nodes = this.getNodes().map((node, i) => { + if (i < begin || i > end) { + return node.selected ? this.clone(node, false) : node; + } else { + return node.selected ? node : this.clone(node, true); + } + }); + } else { + nodes = [...this.getNodes()]; + const i = nodes.indexOf(node); + nodes[i] = this.clone(node, !node.selected); + } + + this.setNodes(nodes); + } + + public toggleSelections(): void { + const nodes = [...this.getNodes()]; + if (nodes.length > 0) { + const value = !nodes.every(row => row.selected); + for (let index = 0, n = nodes.length; index < n; ++index) { + const node = nodes[index]; + if (node.selected !== value) { + nodes[index] = this.clone(node, value); + } + } + } + + this.setNodes(nodes); + } + + public highlight(arg: TNode | number): void { + const index = typeof arg === 'number' ? arg : this.getNodes().indexOf(arg); + this.updateHighlighted(index); + } + + protected abstract getNodes(): TNode[]; + + protected abstract setNodes(nodes: TNode[]): void; + + protected abstract cloneNode(node: TNode): TNode; + + private expandNode(node: TNode, nodes: TNode[]) { + const index = nodes.indexOf(node); + const clone = this.cloneNode(node); + clone.expanded = true; + nodes[index] = clone; + } + + private updateHighlighted(index: number): void { + const nodes = [...this.getNodes()]; + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + if (i === index) { + nodes[i] = this.cloneNode(node); + nodes[i].highlighted = true; + } else if (node.highlighted) { + nodes[i] = this.cloneNode(node); + nodes[i].highlighted = false; + } + } + + this.setNodes(nodes); + } + + private traverseNodes(node: TNode, expanded: TNode[]): TNode[] { + if (node.firstChild >= 0 && node.expanded) { + let child = this.getNodes()[node.firstChild]; + expanded.push(child); + this.traverseNodes(child, expanded); + while (child.nextSibling >= 0) { + child = this.getNodes()[child.nextSibling]; + expanded.push(child); + this.traverseNodes(child, expanded); + } + } + + return expanded; + } + + private clone(node: TNode, selected: boolean): TNode { + const clone = this.cloneNode(node); + clone.selected = selected; + return clone; + } +} diff --git a/projects/aas-portal/src/app/aas/edit-element-form/edit-element-form.component.scss b/projects/aas-portal/src/app/aas/edit-element-form/edit-element-form.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-portal/src/app/aas/edit-element-form/edit-element-form.component.scss +++ b/projects/aas-portal/src/app/aas/edit-element-form/edit-element-form.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-portal/src/app/aas/new-element-form/new-element-form.component.scss b/projects/aas-portal/src/app/aas/new-element-form/new-element-form.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-portal/src/app/aas/new-element-form/new-element-form.component.scss +++ b/projects/aas-portal/src/app/aas/new-element-form/new-element-form.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-portal/src/app/about/about.component.scss b/projects/aas-portal/src/app/about/about.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-portal/src/app/about/about.component.scss +++ b/projects/aas-portal/src/app/about/about.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-portal/src/app/main/main.component.scss b/projects/aas-portal/src/app/main/main.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-portal/src/app/main/main.component.scss +++ b/projects/aas-portal/src/app/main/main.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-portal/src/app/start/remove-endpoint-form/remove-endpoint-form.component.scss b/projects/aas-portal/src/app/start/remove-endpoint-form/remove-endpoint-form.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-portal/src/app/start/remove-endpoint-form/remove-endpoint-form.component.scss +++ b/projects/aas-portal/src/app/start/remove-endpoint-form/remove-endpoint-form.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-portal/src/app/start/start.actions.ts b/projects/aas-portal/src/app/start/start.actions.ts index bf51c7c4..6745dbbf 100644 --- a/projects/aas-portal/src/app/start/start.actions.ts +++ b/projects/aas-portal/src/app/start/start.actions.ts @@ -62,7 +62,7 @@ export const setPage = createAction( export const setContent = createAction( StartActionType.SET_CONTENT, - props<{ document: AASDocument; content: aas.Environment }>(), + props<{ document: AASDocument; content: aas.Environment | null | undefined }>(), ); export const getFavorites = createAction( diff --git a/projects/aas-portal/src/app/start/start.component.scss b/projects/aas-portal/src/app/start/start.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-portal/src/app/start/start.component.scss +++ b/projects/aas-portal/src/app/start/start.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-portal/src/app/start/start.effects.ts b/projects/aas-portal/src/app/start/start.effects.ts index edfdda22..a064772e 100644 --- a/projects/aas-portal/src/app/start/start.effects.ts +++ b/projects/aas-portal/src/app/start/start.effects.ts @@ -10,7 +10,7 @@ import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Action, Store } from '@ngrx/store'; import { TranslateService } from '@ngx-translate/core'; -import { EMPTY, exhaustMap, map, mergeMap, first, concat, of, from, Observable } from 'rxjs'; +import { EMPTY, exhaustMap, map, mergeMap, first, concat, of, from, Observable, catchError } from 'rxjs'; import { AASDocument, AASDocumentId, AASPage } from 'common'; import { ViewMode } from 'aas-lib'; @@ -189,9 +189,10 @@ export class StartEffects { of(StartActions.setFavorites({ name: action.name, documents: action.documents })), from(action.documents).pipe( mergeMap(document => - this.api - .getContent(document.endpoint, document.id) - .pipe(map(content => StartActions.setContent({ document, content }))), + this.api.getContent(document.endpoint, document.id).pipe( + catchError(() => of(undefined)), + map(content => StartActions.setContent({ document, content })), + ), ), ), ), @@ -208,9 +209,10 @@ export class StartEffects { of(StartActions.setPage({ page, limit, filter })), from(page.documents).pipe( mergeMap(document => - this.api - .getContent(document.endpoint, document.id) - .pipe(map(content => StartActions.setContent({ document, content }))), + this.api.getContent(document.endpoint, document.id).pipe( + catchError(() => of(undefined)), + map(content => StartActions.setContent({ document, content })), + ), ), ), ); diff --git a/projects/aas-portal/src/app/start/start.reducer.ts b/projects/aas-portal/src/app/start/start.reducer.ts index ecc5b720..ed2cee1f 100644 --- a/projects/aas-portal/src/app/start/start.reducer.ts +++ b/projects/aas-portal/src/app/start/start.reducer.ts @@ -59,7 +59,7 @@ function setFavorites(state: StartState, favorites: string, documents: AASDocume return { ...state, favorites, documents, viewMode: ViewMode.List }; } -function setContent(state: StartState, document: AASDocument, content: aas.Environment): StartState { +function setContent(state: StartState, document: AASDocument, content: aas.Environment | null | undefined): StartState { const documents = [...state.documents]; const index = documents.findIndex(item => item.endpoint === document.endpoint && item.id === document.id); if (index >= 0) { diff --git a/projects/aas-portal/src/app/start/upload-form/upload-form.component.scss b/projects/aas-portal/src/app/start/upload-form/upload-form.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-portal/src/app/start/upload-form/upload-form.component.scss +++ b/projects/aas-portal/src/app/start/upload-form/upload-form.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ diff --git a/projects/aas-portal/src/app/view/view.component.scss b/projects/aas-portal/src/app/view/view.component.scss index 1b4785f3..3caf3822 100644 --- a/projects/aas-portal/src/app/view/view.component.scss +++ b/projects/aas-portal/src/app/view/view.component.scss @@ -1,7 +1,7 @@ -/****************************************************************************** - * - * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, - * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft - * zur Foerderung der angewandten Forschung e.V. - * - *****************************************************************************/ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/