diff --git a/projects/aas-lib/src/lib/aas-table/aas-table.filter.ts b/projects/aas-lib/src/lib/aas-table/aas-table.filter.ts index 4a2126f..89911b5 100644 --- a/projects/aas-lib/src/lib/aas-table/aas-table.filter.ts +++ b/projects/aas-lib/src/lib/aas-table/aas-table.filter.ts @@ -22,6 +22,8 @@ import { parseDate, toBoolean, flat, + getModelTypeFromAbbreviation, + AASAbbreviation, } from 'aas-core'; export type ElementValueType = 'string' | 'boolean' | 'number' | 'Date' | 'bigint'; @@ -110,7 +112,7 @@ export class AASTableFilter { } private any(element: aas.Referable, query: AASQuery): boolean { - if (element.modelType === query.modelType) { + if (element.modelType === getModelTypeFromAbbreviation(query.modelType as AASAbbreviation)) { if (this.containsString(element.idShort, query.name)) { if (!element || !query.value) { return true; diff --git a/projects/aas-lib/src/lib/aas-table/aas-table.store.ts b/projects/aas-lib/src/lib/aas-table/aas-table.store.ts index 62bc882..ad9db2e 100644 --- a/projects/aas-lib/src/lib/aas-table/aas-table.store.ts +++ b/projects/aas-lib/src/lib/aas-table/aas-table.store.ts @@ -12,13 +12,27 @@ import { AASDocument } from 'aas-core'; import { ViewMode } from '../types/view-mode'; import { AASTableRow, AASTableTree } from './aas-table-row'; +type AASTableState = { + documents: AASDocument[]; + totalRows: AASTableRow[]; + rows: AASTableRow[]; +}; + +const initialState: AASTableState = { + documents: [], + totalRows: [], + rows: [], +}; + @Injectable() export class AASTableStore { - private readonly _totalRows = signal([]); - private readonly _rows = signal([]); + private readonly _totalRows = signal(initialState.totalRows); + private readonly _rows = signal(initialState.rows); public readonly rows = this._rows.asReadonly(); + public readonly documents = signal(initialState.documents); + public setSelected(documents: AASDocument[], viewMode: ViewMode): void { const tree = new AASTableTree(untracked(this._totalRows)); tree.selectedElements = documents; 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 ca3d32c..bb1b1f4 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 @@ -6,9 +6,9 @@ * *****************************************************************************/ +import trim from 'lodash-es/trim'; import { Injectable, untracked } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import trim from 'lodash-es/trim'; import { aas, AASAbbreviation, @@ -21,7 +21,21 @@ import { import { normalize } from '../convert'; import { AASTreeRow } from './aas-tree-row'; -import { AASTreeService, Operator, SearchQuery, SearchTerm } from './aas-tree.service'; +import { AASTreeStore } from './aas-tree.store'; + +export type Operator = '=' | '<' | '>' | '<=' | '>=' | '!='; + +export interface SearchQuery { + modelType: aas.ModelType; + operator?: Operator; + name?: string; + value?: string | boolean; +} + +export interface SearchTerm { + text?: string; + query?: SearchQuery; +} @Injectable() export class AASTreeSearch { @@ -29,12 +43,12 @@ export class AASTreeSearch { private terms: SearchTerm[] = []; public constructor( - private readonly store: AASTreeService, + private readonly store: AASTreeStore, private readonly translate: TranslateService, ) {} public find(referable: aas.Referable): void { - const rows = untracked(this.store.state).rows; + const rows = untracked(this.store.state$).rows; const index = rows.findIndex(row => row.element === referable); if (index >= 0) { this.store.setMatchIndex(index); @@ -73,7 +87,7 @@ export class AASTreeSearch { public findNext(): boolean { let completed = false; - const state = untracked(this.store.state); + const state = untracked(this.store.state$); if (state.rows.length > 0 && this.terms.length > 0) { let match = false; let i = state.matchIndex < 0 ? 0 : state.matchIndex + 1; @@ -106,7 +120,7 @@ export class AASTreeSearch { public findPrevious(): boolean { let completed = false; - const state = untracked(this.store.state); + const state = untracked(this.store.state$); if (state.rows.length > 0 && this.terms.length > 0) { let match = false; let i = state.matchIndex <= 0 ? state.rows.length - 1 : state.matchIndex - 1; @@ -138,7 +152,7 @@ export class AASTreeSearch { } private findFirst(): void { - const state = untracked(this.store.state); + const state = untracked(this.store.state$); if (state.rows.length > 0 && this.terms.length > 0) { let match = false; let i = state.matchIndex < 0 ? 0 : state.matchIndex; diff --git a/projects/aas-lib/src/lib/aas-tree/aas-tree.component.ts b/projects/aas-lib/src/lib/aas-tree/aas-tree.component.ts index 29f1c3c..a76baae 100644 --- a/projects/aas-lib/src/lib/aas-tree/aas-tree.component.ts +++ b/projects/aas-lib/src/lib/aas-tree/aas-tree.component.ts @@ -12,17 +12,7 @@ import { Subscription } from 'rxjs'; import { WebSocketSubject } from 'rxjs/webSocket'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { - ChangeDetectionStrategy, - Component, - OnDestroy, - OnInit, - computed, - effect, - input, - output, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, computed, effect, input, output } from '@angular/core'; import { aas, @@ -31,7 +21,6 @@ import { WebSocketData, AASDocument, convertToString, - toLocale, selectSubmodel, getIdShortPath, mimeTypeToExtension, @@ -42,9 +31,10 @@ import { isReferenceElement, isOperation, isSubmodel, + equalDocument, } from 'aas-core'; -import { AASTreeRow } from './aas-tree-row'; +import { AASTree, AASTreeRow } from './aas-tree-row'; import { OnlineState } from '../types/online-state'; import { ShowImageFormComponent } from '../show-image-form/show-image-form.component'; import { ShowVideoFormComponent } from '../show-video-form/show-video-form.component'; @@ -65,7 +55,7 @@ import { } from '../submodel-template/submodel-template'; import { AASTreeApiService } from './aas-tree-api.service'; -import { AASTreeService } from './aas-tree.service'; +import { AASTreeStore } from './aas-tree.store'; @Component({ selector: 'fhg-aas-tree', @@ -73,24 +63,21 @@ import { AASTreeService } from './aas-tree.service'; styleUrls: ['./aas-tree.component.scss'], standalone: true, imports: [NgClass, NgStyle, TranslateModule], - providers: [AASTreeSearch, AASTreeService, AASTreeApiService], + providers: [AASTreeSearch, AASTreeApiService], changeDetection: ChangeDetectionStrategy.OnPush, }) export class AASTreeComponent implements OnInit, OnDestroy { private readonly liveNodes: LiveNode[] = []; private readonly map = new Map(); private readonly subscription = new Subscription(); - private readonly _expanded = signal(false); - private shiftKey = false; - private altKey = false; private webSocketSubject?: WebSocketSubject; public constructor( - private readonly service: AASTreeService, - private readonly router: Router, + private readonly store: AASTreeStore, private readonly api: AASTreeApiService, private readonly searching: AASTreeSearch, + private readonly router: Router, private readonly modal: NgbModal, private readonly window: WindowService, private readonly dom: DocumentService, @@ -100,9 +87,23 @@ export class AASTreeComponent implements OnInit, OnDestroy { private readonly webSocketFactory: WebSocketFactoryService, private readonly clipboard: ClipboardService, ) { - effect(() => this.searching.start(this.searchExpression()), { allowSignalWrites: true }); + effect( + () => { + this.searching.start(this.searchExpression()); + }, + { allowSignalWrites: true }, + ); - effect(() => this.service.updateRows(this.document()), { allowSignalWrites: true }); + effect( + () => { + const document = this.document(); + if (!equalDocument(document, this.store.document)) { + this.store.document = document; + this.updateRows(document); + } + }, + { allowSignalWrites: true }, + ); effect(() => { if (this.state() === 'online') { @@ -112,13 +113,15 @@ export class AASTreeComponent implements OnInit, OnDestroy { } }); - effect(() => this.selected.emit(this.service.selectedElements())); + effect(() => { + this.selected.emit(this.store.selectedElements$()); + }); effect( () => { - const matchIndex = this.service.matchIndex(); + const matchIndex = this.matchIndex(); if (matchIndex >= 0) { - this.service.expandRow(matchIndex); + this.store.expandRow(matchIndex); } }, { allowSignalWrites: true }, @@ -126,7 +129,7 @@ export class AASTreeComponent implements OnInit, OnDestroy { effect( () => { - const row = this.service.matchRow(); + const row = this.matchRow(); if (!row) return; setTimeout(() => { @@ -156,24 +159,27 @@ export class AASTreeComponent implements OnInit, OnDestroy { public readonly modified = computed(() => this.document()?.modified ?? false); public readonly someSelected = computed(() => { - const rows = this.service.rows(); + const rows = this.store.state$().rows; return rows.length > 0 && rows.some(row => row.selected) && !rows.every(row => row.selected); }); public readonly everySelected = computed(() => { - const rows = this.service.rows(); + const rows = this.store.state$().rows; return rows.length > 0 && rows.every(row => row.selected); }); - public readonly nodes = this.service.nodes; + public readonly nodes = computed(() => this.store.state$().nodes); - public readonly rows = this.service.rows; + public readonly rows = computed(() => this.store.state$().rows); - public readonly expanded = this._expanded.asReadonly(); + public readonly expanded = computed(() => this.store.state$().expanded); - public readonly matchIndex = this.service.matchIndex; + public readonly matchIndex = computed(() => this.store.state$().matchIndex); - public readonly matchRow = this.service.matchRow; + public readonly matchRow = computed(() => { + const state = this.store.state$(); + return state.matchIndex >= 0 ? state.rows[state.matchIndex] : undefined; + }); public readonly message = computed(() => { const document = this.document(); @@ -194,7 +200,7 @@ export class AASTreeComponent implements OnInit, OnDestroy { public ngOnInit(): void { this.subscription.add( this.translate.onLangChange.subscribe(() => { - this.service.updateRows(this.document()); + this.updateRows(this.document()); }), ); } @@ -223,31 +229,31 @@ export class AASTreeComponent implements OnInit, OnDestroy { public expand(node?: AASTreeRow): void { if (node) { if (!node.expanded) { - this.service.expandRow(node); + this.store.expandRow(node); } } else { - this.service.expand(); - this._expanded.set(true); + this.store.expand(); + this.store.state$.update(state => ({ ...state, expanded: true })); } } public collapse(node?: AASTreeRow): void { if (node) { if (node.expanded) { - this.service.collapseRow(node); + this.store.collapseRow(node); } } else { - this.service.collapse(); - this._expanded.set(false); + this.store.collapse(); + this.store.state$.update(state => ({ ...state, expanded: false })); } } public toggleSelections(): void { - this.service.toggleSelections(); + this.store.toggleSelections(); } public toggleSelection(node: AASTreeRow): void { - this.service.toggleSelected(node, this.altKey, this.shiftKey); + this.store.toggleSelected(node, this.store.altKey, this.store.shiftKey); } public openReference(reference: aas.Reference | string | undefined): void { @@ -298,6 +304,29 @@ export class AASTreeComponent implements OnInit, OnDestroy { return value.keys.map(key => key.value).join('.'); } + private updateRows(document: AASDocument | null): void { + try { + if (document) { + const tree = AASTree.from(document, this.translate.currentLang); + this.store.state$.update(state => ({ + ...state, + matchIndex: -1, + rows: tree.nodes, + nodes: tree.expanded, + })); + } else { + this.store.state$.update(state => ({ + ...state, + matchIndex: -1, + rows: [], + nodes: [], + })); + } + } catch (error) { + this.notify.error(error); + } + } + private async openFile(file: aas.File): Promise { if (!file.value || this.state() === 'online') return; @@ -335,7 +364,7 @@ export class AASTreeComponent implements OnInit, OnDestroy { getIdShortPath(blob), ); - this.service.update(blob, value); + this.store.update(blob, value); } catch (error) { this.notify.error(error); return; @@ -424,17 +453,9 @@ export class AASTreeComponent implements OnInit, OnDestroy { } } - private getPropertyValue(property: aas.Property): string | boolean { - if (typeof property.value === 'boolean') { - return property.value; - } else { - return toLocale(property.value, property.valueType, this.translate.currentLang) ?? '-'; - } - } - private goOnline(): void { try { - this.prepareOnline(this.service.rows().filter(row => row.selected)); + this.prepareOnline(this.store.rows.filter(row => row.selected)); this.play(); } catch (error) { this.stop(); @@ -551,13 +572,13 @@ export class AASTreeComponent implements OnInit, OnDestroy { }; private keyup = () => { - this.shiftKey = false; - this.altKey = false; + this.store.shiftKey = false; + this.store.altKey = false; }; private keydown = (event: KeyboardEvent) => { - this.shiftKey = event.shiftKey; - this.altKey = event.altKey; + this.store.shiftKey = event.shiftKey; + this.store.altKey = event.altKey; }; private resolveFile(file: aas.File): { url?: string; name?: string } { diff --git a/projects/aas-lib/src/lib/aas-tree/aas-tree.service.ts b/projects/aas-lib/src/lib/aas-tree/aas-tree.store.ts similarity index 59% rename from projects/aas-lib/src/lib/aas-tree/aas-tree.service.ts rename to projects/aas-lib/src/lib/aas-tree/aas-tree.store.ts index 868aa69..0d367c0 100644 --- a/projects/aas-lib/src/lib/aas-tree/aas-tree.service.ts +++ b/projects/aas-lib/src/lib/aas-tree/aas-tree.store.ts @@ -6,64 +6,63 @@ * *****************************************************************************/ -import { Injectable, computed, signal } from '@angular/core'; +import { computed, Injectable, signal, untracked } from '@angular/core'; +import { aas, AASDocument } from 'aas-core'; import { AASTree, AASTreeRow } from './aas-tree-row'; -import { AASDocument, aas } from 'aas-core'; -import { TranslateService } from '@ngx-translate/core'; -import { NotifyService } from '../notify/notify.service'; -export type Operator = '=' | '<' | '>' | '<=' | '>=' | '!='; - -export interface SearchQuery { - modelType: aas.ModelType; - operator?: Operator; - name?: string; - value?: string | boolean; -} - -export interface SearchTerm { - text?: string; - query?: SearchQuery; -} - -interface AASTreeState { +type AASTreeState = { + expanded: boolean; matchIndex: number; rows: AASTreeRow[]; nodes: AASTreeRow[]; -} +}; -@Injectable() -export class AASTreeService { - private readonly _state = signal({ matchIndex: -1, rows: [], nodes: [] }); +const initialState: AASTreeState = { + expanded: false, + matchIndex: 0, + rows: [], + nodes: [], +}; - public constructor( - private readonly notify: NotifyService, - private readonly translate: TranslateService, - ) {} +@Injectable({ + providedIn: 'root', +}) +export class AASTreeStore { + public readonly state$ = signal(initialState); - public readonly state = this._state.asReadonly(); + public readonly selectedRows$ = computed(() => this.state$().rows.filter(row => row.selected)); - public readonly rows = computed(() => this._state().rows); + public readonly selectedElements$ = computed(() => + this.state$() + .rows.filter(row => row.selected) + .map(item => item.element), + ); - public readonly matchIndex = computed(() => this._state().matchIndex); + public get rows(): AASTreeRow[] { + return untracked(this.state$).rows; + } - public readonly nodes = computed(() => this._state().nodes); + public document: AASDocument | null = null; - public readonly selectedRows = computed(() => this._state().rows.filter(row => row.selected)); + public shiftKey = false; - public readonly selectedElements = computed(() => - this._state() - .rows.filter(row => row.selected) - .map(item => item.element), - ); + public altKey = false; - public readonly matchRow = computed(() => { - const state = this._state(); - return state.matchIndex >= 0 ? state.rows[state.matchIndex] : undefined; - }); + public setMatchIndex(matchIndex: number): void { + this.state$.update(state => { + const tree = new AASTree(state.rows); + tree.highlight(matchIndex); + return { + ...state, + matchIndex: matchIndex, + rows: tree.nodes, + nodes: tree.expanded, + }; + }); + } public highlight(node: AASTreeRow): void { - this._state.update(state => { + this.state$.update(state => { const tree = new AASTree(state.rows); tree.highlight(node); return { @@ -75,7 +74,7 @@ export class AASTreeService { } public toggleSelected(row: AASTreeRow, altKey: boolean, shiftKey: boolean): void { - this._state.update(state => { + this.state$.update(state => { const tree = new AASTree(state.rows); tree.toggleSelected(row, altKey, shiftKey); return { @@ -87,7 +86,7 @@ export class AASTreeService { } public toggleSelections(): void { - this._state.update(state => { + this.state$.update(state => { const tree = new AASTree(state.rows); tree.toggleSelections(); return { @@ -99,7 +98,7 @@ export class AASTreeService { } public collapse(): void { - this._state.update(state => { + this.state$.update(state => { const tree = new AASTree(state.rows); tree.collapse(); return { @@ -111,7 +110,7 @@ export class AASTreeService { } public expand(): void { - this._state.update(state => { + this.state$.update(state => { const tree = new AASTree(state.rows); tree.expand(); return { @@ -123,7 +122,7 @@ export class AASTreeService { } public collapseRow(row: AASTreeRow): void { - this._state.update(state => { + this.state$.update(state => { const tree = new AASTree(state.rows); tree.collapse(row); return { @@ -135,7 +134,7 @@ export class AASTreeService { } public expandRow(arg: AASTreeRow | number): void { - this._state.update(state => { + this.state$.update(state => { const tree = new AASTree(state.rows); tree.expand(arg); return { @@ -146,29 +145,8 @@ export class AASTreeService { }); } - public updateRows(document: AASDocument | null): void { - try { - if (document) { - const tree = AASTree.from(document, this.translate.currentLang); - this._state.set({ - matchIndex: -1, - rows: tree.nodes, - nodes: tree.expanded, - }); - } else { - this._state.set({ - matchIndex: -1, - rows: [], - nodes: [], - }); - } - } catch (error) { - this.notify.error(error); - } - } - public setSelectedElements(elements: aas.Referable[]): void { - this._state.update(state => { + this.state$.update(state => { const tree = new AASTree(state.rows); tree.selectedElements = elements; return { @@ -179,20 +157,8 @@ export class AASTreeService { }); } - public setMatchIndex(matchIndex: number): void { - this._state.update(state => { - const tree = new AASTree(state.rows); - tree.highlight(matchIndex); - return { - matchIndex: matchIndex, - rows: tree.nodes, - nodes: tree.expanded, - }; - }); - } - public update(blob: aas.Blob, value: string): void { - this._state.update(state => { + this.state$.update(state => { blob.value = value; const tree = new AASTree(state.rows); tree.update(blob); diff --git a/projects/aas-lib/src/test/aas-tree/aas-tree-search.spec.ts b/projects/aas-lib/src/test/aas-tree/aas-tree-search.spec.ts index 9570e7e..96ac506 100644 --- a/projects/aas-lib/src/test/aas-tree/aas-tree-search.spec.ts +++ b/projects/aas-lib/src/test/aas-tree/aas-tree-search.spec.ts @@ -9,13 +9,12 @@ import { TestBed } from '@angular/core/testing'; import { TranslateFakeLoader, TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { AASTreeSearch } from '../../lib/aas-tree/aas-tree-search'; -import { sampleDocument } from '../assets/sample-document'; -import { AASTreeService } from '../../lib/aas-tree/aas-tree.service'; import { NotifyService } from '../../lib/notify/notify.service'; +import { AASTreeStore } from '../../lib/aas-tree/aas-tree.store'; describe('AASTreeSearch', function () { let search: AASTreeSearch; - let store: AASTreeService; + let store: AASTreeStore; beforeEach(async function () { await TestBed.configureTestingModule({ @@ -25,7 +24,7 @@ describe('AASTreeSearch', function () { provide: NotifyService, useValue: jasmine.createSpyObj(['error']), }, - AASTreeService, + AASTreeStore, ], imports: [ TranslateModule.forRoot({ @@ -37,9 +36,8 @@ describe('AASTreeSearch', function () { ], }); - store = TestBed.inject(AASTreeService); + store = TestBed.inject(AASTreeStore); search = new AASTreeSearch(store, TestBed.inject(TranslateService)); - store.updateRows(sampleDocument); }); it('should create', () => { diff --git a/projects/aas-lib/src/test/template.service.spec.ts b/projects/aas-lib/src/test/template.service.spec.ts index 94fbcef..84ee31c 100644 --- a/projects/aas-lib/src/test/template.service.spec.ts +++ b/projects/aas-lib/src/test/template.service.spec.ts @@ -23,9 +23,13 @@ describe('TemplateService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [], - providers: [{ provide: NotifyService, useValue: jasmine.createSpyObj(['error']) }, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()] -}); + imports: [], + providers: [ + { provide: NotifyService, useValue: jasmine.createSpyObj(['error']) }, + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting(), + ], + }); http = TestBed.inject(HttpClient); service = TestBed.inject(TemplateService); diff --git a/projects/aas-portal/src/app/aas/aas.store.ts b/projects/aas-portal/src/app/aas/aas.store.ts index dcd7550..004eba6 100644 --- a/projects/aas-portal/src/app/aas/aas.store.ts +++ b/projects/aas-portal/src/app/aas/aas.store.ts @@ -8,19 +8,35 @@ import { Injectable, signal, untracked } from '@angular/core'; import { OnlineState } from 'aas-lib'; -import { aas, AASDocument } from 'aas-core'; +import { aas, AASDocument, equalArray } from 'aas-core'; + +type AASState = { + document: AASDocument | null; + state: OnlineState; + searchExpression: string; + selectedElements: aas.Referable[]; +}; + +const initialState: AASState = { + document: null, + state: 'offline', + searchExpression: '', + selectedElements: [], +}; @Injectable({ providedIn: 'root', }) export class AASStore { - public readonly document$ = signal(null); + public readonly document$ = signal(initialState.document); - public readonly state$ = signal('offline'); + public readonly state$ = signal(initialState.state); - public readonly searchExpression$ = signal(''); + public readonly searchExpression$ = signal(initialState.searchExpression); - public readonly selectedElements$ = signal([]); + public readonly selectedElements$ = signal(initialState.selectedElements, { + equal: (a, b) => equalArray(a, b), + }); public get document(): AASDocument | null { return untracked(this.document$); diff --git a/projects/aas-portal/src/app/dashboard/dashboard.store.ts b/projects/aas-portal/src/app/dashboard/dashboard.store.ts index be523b6..366d622 100644 --- a/projects/aas-portal/src/app/dashboard/dashboard.store.ts +++ b/projects/aas-portal/src/app/dashboard/dashboard.store.ts @@ -81,19 +81,21 @@ export interface DashboardRow { columns: DashboardColumn[]; } -export interface DashboardState { +export type DashboardState = { pages: DashboardPage[]; index: number; -} +}; + +const initialState: DashboardState = { + index: 0, + pages: [{ name: 'Dashboard 1', items: [], requests: [] }], +}; @Injectable({ providedIn: 'root', }) export class DashboardStore { - private readonly state$ = signal({ - index: 0, - pages: [{ name: this.createPageName([]), items: [], requests: [] }], - }); + private readonly state$ = signal(initialState); public constructor(private readonly auth: AuthService) { this.auth.ready diff --git a/projects/aas-portal/src/app/start/start.store.ts b/projects/aas-portal/src/app/start/start.store.ts index ba4195d..ece645a 100644 --- a/projects/aas-portal/src/app/start/start.store.ts +++ b/projects/aas-portal/src/app/start/start.store.ts @@ -7,28 +7,50 @@ *****************************************************************************/ import { Injectable, signal, untracked } from '@angular/core'; +import { AASDocument, AASDocumentId, equalArray } from 'aas-core'; import { ViewMode } from 'aas-lib'; -import { AASDocument, AASDocumentId } from 'aas-core'; + +type StartState = { + viewMode: ViewMode; + documents: AASDocument[]; + activeFavorites: string; + limit: number; + filterText: string; + selected: AASDocument[]; + previous: AASDocumentId | null; + next: AASDocumentId | null; +}; + +const initialState: StartState = { + viewMode: ViewMode.Undefined, + documents: [], + activeFavorites: '', + limit: 10, + filterText: '', + selected: [], + previous: null, + next: null, +}; @Injectable({ providedIn: 'root', }) export class StartStore { - public readonly viewMode$ = signal(ViewMode.Undefined); + public readonly viewMode$ = signal(initialState.viewMode); - public readonly documents$ = signal([]); + public readonly documents$ = signal(initialState.documents, { equal: (a, b) => equalArray(a, b) }); - public readonly activeFavorites$ = signal(''); + public readonly activeFavorites$ = signal(initialState.activeFavorites); - public readonly limit$ = signal(10); + public readonly limit$ = signal(initialState.limit); - public readonly filterText$ = signal(''); + public readonly filterText$ = signal(initialState.filterText); - public readonly selected$ = signal([]); + public readonly selected$ = signal(initialState.selected, { equal: (a, b) => equalArray(a, b) }); - public readonly previous$ = signal(null); + public readonly previous$ = signal(initialState.previous); - public readonly next$ = signal(null); + public readonly next$ = signal(initialState.next); public get viewMode(): ViewMode { return untracked(this.viewMode$); diff --git a/projects/aas-portal/src/app/view/view.component.ts b/projects/aas-portal/src/app/view/view.component.ts index 116656c..7b5e263 100644 --- a/projects/aas-portal/src/app/view/view.component.ts +++ b/projects/aas-portal/src/app/view/view.component.ts @@ -14,7 +14,6 @@ import { OnInit, TemplateRef, ViewChild, - signal, } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @@ -32,6 +31,7 @@ import { ViewQuery, ViewQueryParams, } from 'aas-lib'; +import { ViewStore } from './view.store'; @Component({ selector: 'fhg-view', @@ -43,12 +43,11 @@ import { }) export class ViewComponent implements OnInit, AfterViewInit, OnDestroy { private readonly subscription = new Subscription(); - private _template = signal(undefined); - private _submodels = signal([]); public constructor( private readonly route: ActivatedRoute, private readonly api: ViewApiService, + private readonly store: ViewStore, private readonly clipboard: ClipboardService, private readonly toolbar: ToolbarService, ) {} @@ -56,9 +55,9 @@ export class ViewComponent implements OnInit, AfterViewInit, OnDestroy { @ViewChild('viewToolbar', { read: TemplateRef }) public viewToolbar: TemplateRef | null = null; - public readonly template = this._template.asReadonly(); + public readonly template = this.store.template$.asReadonly(); - public readonly submodels = this._submodels.asReadonly(); + public readonly submodels = this.store.submodels$.asReadonly(); public ngOnInit(): void { let query: ViewQuery | undefined; @@ -84,8 +83,8 @@ export class ViewComponent implements OnInit, AfterViewInit, OnDestroy { toArray(), ), ).subscribe(tuple => { - this._submodels.set(tuple[1]); - this._template.set(tuple[0]); + this.store.submodels$.set(tuple[1]); + this.store.template$.set(tuple[0]); }); } } diff --git a/projects/aas-portal/src/app/view/view.store.ts b/projects/aas-portal/src/app/view/view.store.ts new file mode 100644 index 0000000..f738590 --- /dev/null +++ b/projects/aas-portal/src/app/view/view.store.ts @@ -0,0 +1,37 @@ +/****************************************************************************** + * + * Copyright (c) 2019-2024 Fraunhofer IOSB-INA Lemgo, + * eine rechtlich nicht selbstaendige Einrichtung der Fraunhofer-Gesellschaft + * zur Foerderung der angewandten Forschung e.V. + * + *****************************************************************************/ + +import { Injectable, signal, untracked } from '@angular/core'; +import { DocumentSubmodelPair } from 'aas-lib'; + +type ViewState = { + template: string | undefined; + submodels: DocumentSubmodelPair[]; +}; + +const initialState: ViewState = { + template: undefined, + submodels: [], +}; + +@Injectable({ + providedIn: 'root', +}) +export class ViewStore { + public template$ = signal(initialState.template); + + public submodels$ = signal(initialState.submodels); + + public get template(): string | undefined { + return untracked(this.template$); + } + + public get submodels(): DocumentSubmodelPair[] { + return untracked(this.submodels$); + } +}