From 9c8c17c65dd59ca93df06af0e17e2ac8c885eddb Mon Sep 17 00:00:00 2001 From: Johann Schoen Date: Wed, 30 Jun 2021 17:18:15 +0200 Subject: [PATCH 1/7] feat(table): adds support for handling table actions that are loaded by an observable --- .../table/src/data/table-data-source.ts | 78 ++++++++++++++++--- projects/components/table/src/models.ts | 56 ++++++++++++- .../src/pipes/table-actions-to-render.pipe.ts | 7 +- .../table-actions.component.html | 18 +++-- .../subcomponents/table-actions.component.ts | 36 ++++++++- .../subcomponents/table-data.component.html | 6 +- .../table-row-actions.component.html | 4 +- .../table-row-actions.component.ts | 7 +- .../table/src/table.component.spec.ts | 70 ++++++++++++++--- projects/components/table/src/table.module.ts | 2 + .../app/table-demo/table-demo.component.ts | 41 +++++++++- 11 files changed, 287 insertions(+), 38 deletions(-) diff --git a/projects/components/table/src/data/table-data-source.ts b/projects/components/table/src/data/table-data-source.ts index 9baefa33..068fac2f 100644 --- a/projects/components/table/src/data/table-data-source.ts +++ b/projects/components/table/src/data/table-data-source.ts @@ -1,9 +1,11 @@ import { DataSource, SelectionModel } from '@angular/cdk/collections'; import { TrackByFunction } from '@angular/core'; -import { BehaviorSubject, NEVER, Observable, of, Subject, Subscription } from 'rxjs'; +import { BehaviorSubject, isObservable, NEVER, Observable, of, Subject, Subscription } from 'rxjs'; import { catchError, finalize, map, take, tap } from 'rxjs/operators'; + import { _isNumberValue } from '../helper/table.helper'; -import { IExtendedPsTableUpdateDataInfo, IPsTableAction, IPsTableUpdateDataInfo, PsTableActionScope } from '../models'; +import { IExtendedPsTableUpdateDataInfo, IPsTableAction, IPsTableUpdateDataInfo, PsTableAction, PsTableActionScope } from '../models'; + /** * Corresponds to `Number.MAX_SAFE_INTEGER`. Moved out into a variable here due to * flaky browser support and the value not being defined in Closure's typings. @@ -13,7 +15,7 @@ const MAX_SAFE_INTEGER = 9007199254740991; export interface PsTableDataSourceOptions { loadTrigger$?: Observable; loadDataFn: (updateInfo: IExtendedPsTableUpdateDataInfo) => Observable>; - actions?: IPsTableAction[]; + actions?: IPsTableAction[] | Observable[]>; mode?: PsTableMode; moreMenuThreshold?: number; } @@ -86,18 +88,29 @@ export class PsTableDataSource extends DataSource { public readonly mode: PsTableMode; /** List of actions which can be executed for a single row */ - public readonly rowActions: IPsTableAction[]; + public get rowActions(): PsTableAction[] { + return this._rowActions || []; + } + private _rowActions: PsTableAction[]; /** List of actions which can be executed for a selection of rows */ - public readonly listActions: IPsTableAction[]; + public get listActions(): PsTableAction[] { + return this._listActions || []; + } + private _listActions: PsTableAction[]; public readonly moreMenuThreshold: number; + /** Action-Definition -> Action Store */ + private readonly _store = new WeakMap, PsTableAction>(); + /** Stream that emits when a new data array is set on the data source. */ private readonly _updateDataTrigger$: Observable; private readonly _loadData: (updateInfo: IExtendedPsTableUpdateDataInfo) => Observable>; + private readonly _actions: IPsTableAction[] | Observable[]>; + /** Stream that emits when a new data array is set on the data source. */ private readonly _data: BehaviorSubject = new BehaviorSubject([]); @@ -109,6 +122,11 @@ export class PsTableDataSource extends DataSource { private _lastLoadTriggerData: TTrigger = null; + /** + * Subscription to the result of the _actions observable. + */ + private _loadActionsSubscription = Subscription.EMPTY; + /** * Subscription to the result of the loadData function. */ @@ -143,10 +161,7 @@ export class PsTableDataSource extends DataSource { 'loadDataFn' in optionsOrLoadDataFn ? optionsOrLoadDataFn : { loadDataFn: optionsOrLoadDataFn, actions: [], mode: mode }; this.mode = options.mode || 'client'; - // tslint:disable-next-line:no-bitwise - this.rowActions = options.actions?.filter((a) => a.scope & PsTableActionScope.row) || []; - // tslint:disable-next-line:no-bitwise - this.listActions = options.actions?.filter((a) => a.scope & PsTableActionScope.list) || []; + this._actions = options.actions; this._updateDataTrigger$ = options.loadTrigger$ || NEVER; this._loadData = options.loadDataFn; this.moreMenuThreshold = options.moreMenuThreshold ?? 3; @@ -298,6 +313,49 @@ export class PsTableDataSource extends DataSource { return data; } + /** + * Gets the existing PsTableAction object by the declaration from the store or creates a new one. + * + * Because actions can have async children, we cannot create for each declaration the coresponding PsTableAction at the beginning. + * This forces us to create the PsTableAction dynamicly. But we do not want to execute the same observables multiple times, so we cache them. + * Otherwise the observable would be executed for each row. + * + * @param declaration Action declaration information + * @returns Existing or new PsTableAction + */ + public getAction(declaration: IPsTableAction): PsTableAction { + if (!this._store.has(declaration)) { + const action = new PsTableAction(declaration, this); + this._store.set(declaration, action); + } + + return this._store.get(declaration); + } + + public updateActions(): void { + if (!this._actions) { + return; + } + + const setActions = (actions: PsTableAction[]) => { + this._rowActions = actions?.filter((a) => a.scope & PsTableActionScope.row) || []; + this._listActions = actions?.filter((a) => a.scope & PsTableActionScope.list) || []; + }; + + if (Array.isArray(this._actions) && this._actions.length) { + setActions(this._actions.map((x) => this.getAction(x))); + } else if (isObservable(this._actions)) { + this._loadActionsSubscription?.unsubscribe(); + this._loadActionsSubscription = this._actions + .pipe( + take(1), + map((x) => x.map((y) => this.getAction(y))), + finalize(() => this._internalDetectChanges.next()) + ) + .subscribe((actions) => setActions(actions)); + } + } + /** * Reloads the data */ @@ -371,6 +429,7 @@ export class PsTableDataSource extends DataSource { this._lastLoadTriggerData = data; this.updateData(); }); + this.updateActions(); return this._renderData; } @@ -382,6 +441,7 @@ export class PsTableDataSource extends DataSource { this._renderChangesSubscription.unsubscribe(); this._updateDataTriggerSub.unsubscribe(); this._loadDataSubscription.unsubscribe(); + this._loadActionsSubscription.unsubscribe(); } /** diff --git a/projects/components/table/src/models.ts b/projects/components/table/src/models.ts index eceb8336..bf7e2c78 100644 --- a/projects/components/table/src/models.ts +++ b/projects/components/table/src/models.ts @@ -1,3 +1,7 @@ +import { isObservable, Observable, of } from 'rxjs'; +import { map, shareReplay, switchMap, tap } from 'rxjs/operators'; +import { PsTableDataSource } from './data/table-data-source'; + export interface IPsTableSortDefinition { prop: string; displayName: string; @@ -28,9 +32,59 @@ export interface IPsTableAction { label: string; icon: string; iconColor?: string; - children?: IPsTableAction[]; + children?: IPsTableAction[] | Observable[]>; scope: PsTableActionScope; isDisabledFn?: (items: T[]) => boolean; isHiddenFn?: (items: T[]) => boolean; actionFn?: (items: T[]) => void; } + +export class PsTableAction { + public readonly label: string; + public readonly icon: string; + public readonly iconColor?: string; + public readonly scope: PsTableActionScope; + public readonly isDisabledFn?: (items: T[]) => boolean; + public readonly isHiddenFn?: (items: T[]) => boolean; + public readonly actionFn?: (items: T[]) => void; + public readonly isObservable: boolean; + + public children$: Observable[]>; + + public get isLoading(): boolean { + return this._isLoading; + } + private _isLoading = false; + + public get hasChildren(): boolean { + return this._hasChildren; + } + private _hasChildren = false; + + constructor(declaration: IPsTableAction, private dataSource: PsTableDataSource) { + this.label = declaration.label; + this.icon = declaration.icon; + this.iconColor = declaration.iconColor; + this.scope = declaration.scope; + this.isDisabledFn = declaration.isDisabledFn; + this.isHiddenFn = declaration.isHiddenFn; + this.actionFn = declaration.actionFn; + + this.isObservable = isObservable(declaration.children); + this._hasChildren = Array.isArray(declaration.children) || this.isObservable; + + if (this._hasChildren) { + this.children$ = isObservable(declaration.children) + ? of(void 0).pipe( + tap(() => (this._isLoading = true)), + switchMap(() => declaration.children as Observable[]>), + tap(() => (this._isLoading = false)), + shareReplay({ bufferSize: 1, refCount: false }), + map((x) => x.map((y) => this.dataSource.getAction(y) as PsTableAction) || []) + ) + : of(declaration.children.map((x) => this.dataSource.getAction(x) as PsTableAction)); + } else { + this.children$ = of([]); + } + } +} diff --git a/projects/components/table/src/pipes/table-actions-to-render.pipe.ts b/projects/components/table/src/pipes/table-actions-to-render.pipe.ts index 47d7044c..f85bdcf5 100644 --- a/projects/components/table/src/pipes/table-actions-to-render.pipe.ts +++ b/projects/components/table/src/pipes/table-actions-to-render.pipe.ts @@ -1,5 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { IPsTableAction } from '../models'; + +import { PsTableAction } from '../models'; /** * Filters out hidden actions @@ -9,8 +10,8 @@ import { IPsTableAction } from '../models'; pure: true, }) export class PsTableActionsToRenderPipe implements PipeTransform { - transform(actions: IPsTableAction[], ...args: [T | T[]]): any { + transform(actions: PsTableAction[], ...args: [T | T[]]): any { const elements = Array.isArray(args[0]) ? args[0] : [args[0]]; - return actions.filter((a) => !a.isHiddenFn || !a.isHiddenFn(elements)); + return actions?.filter((a) => !a.isHiddenFn || !a.isHiddenFn(elements)) || []; } } diff --git a/projects/components/table/src/subcomponents/table-actions.component.html b/projects/components/table/src/subcomponents/table-actions.component.html index 540f2343..5041d964 100644 --- a/projects/components/table/src/subcomponents/table-actions.component.html +++ b/projects/components/table/src/subcomponents/table-actions.component.html @@ -6,12 +6,15 @@ @@ -19,7 +22,12 @@ {{ action.icon }} {{ action.label }} - + diff --git a/projects/components/table/src/subcomponents/table-actions.component.ts b/projects/components/table/src/subcomponents/table-actions.component.ts index abc36df0..25bbaf4c 100644 --- a/projects/components/table/src/subcomponents/table-actions.component.ts +++ b/projects/components/table/src/subcomponents/table-actions.component.ts @@ -1,15 +1,35 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, Output, ViewChild } from '@angular/core'; import { MatMenu } from '@angular/material/menu'; import { IPsTableIntlTexts } from '@prosoft/components/core'; -import { IPsTableAction } from '../models'; +import { merge, Subscription } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +import { PsTableDataSource } from '../data/table-data-source'; +import { PsTableAction } from '../models'; @Component({ selector: 'ps-table-actions', templateUrl: './table-actions.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PsTableActionsComponent { - @Input() public actions: IPsTableAction[]; +export class PsTableActionsComponent implements OnDestroy { + @Input() + public get actions(): PsTableAction[] { + return this._actions; + } + public set actions(value: PsTableAction[]) { + this._actions = value || []; + this._subscription?.unsubscribe(); + const observables = this.actions?.filter((x) => x.isObservable).map((x) => x.children$); + + if (observables) { + this._subscription = merge(observables) + .pipe(tap(() => this.cd.markForCheck())) + .subscribe(); + } + } + private _actions: PsTableAction[]; + @Input() public dataSource: PsTableDataSource<{ [key: string]: any }>; @Input() public items: unknown[]; @Input() public refreshable: boolean; @Input() public settingsEnabled: boolean; @@ -19,4 +39,12 @@ export class PsTableActionsComponent { @Output() public showSettings = new EventEmitter(); @ViewChild('menu', { static: true }) menu: MatMenu; + + private _subscription: Subscription = Subscription.EMPTY; + + constructor(private cd: ChangeDetectorRef) {} + + public ngOnDestroy(): void { + this._subscription?.unsubscribe(); + } } diff --git a/projects/components/table/src/subcomponents/table-data.component.html b/projects/components/table/src/subcomponents/table-data.component.html index 18850548..235a32e4 100644 --- a/projects/components/table/src/subcomponents/table-data.component.html +++ b/projects/components/table/src/subcomponents/table-data.component.html @@ -5,7 +5,7 @@ - + @@ -13,7 +13,7 @@ - + @@ -57,6 +57,7 @@ [refreshable]="refreshable" [settingsEnabled]="settingsEnabled" [intl]="intl" + [dataSource]="dataSource" (refreshData)="onRefreshDataClicked()" (showSettings)="onShowSettingsClicked()" > @@ -68,6 +69,7 @@ diff --git a/projects/components/table/src/subcomponents/table-row-actions.component.html b/projects/components/table/src/subcomponents/table-row-actions.component.html index 7f9f136d..6a62fc41 100644 --- a/projects/components/table/src/subcomponents/table-row-actions.component.html +++ b/projects/components/table/src/subcomponents/table-row-actions.component.html @@ -3,7 +3,7 @@ - + @@ -30,7 +30,7 @@ > {{ action.icon }} - + diff --git a/projects/components/table/src/subcomponents/table-row-actions.component.ts b/projects/components/table/src/subcomponents/table-row-actions.component.ts index c881d239..f97963b2 100644 --- a/projects/components/table/src/subcomponents/table-row-actions.component.ts +++ b/projects/components/table/src/subcomponents/table-row-actions.component.ts @@ -1,5 +1,7 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges, TemplateRef } from '@angular/core'; -import { IPsTableAction } from '../models'; + +import { PsTableDataSource } from '../data/table-data-source'; +import { PsTableAction } from '../models'; @Component({ selector: 'ps-table-row-actions', @@ -7,8 +9,9 @@ import { IPsTableAction } from '../models'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class PsTableRowActionsComponent implements OnChanges { - @Input() public actions: IPsTableAction[]; + @Input() public actions: PsTableAction[]; @Input() public actionsTemplate: TemplateRef | null = null; + @Input() public dataSource: PsTableDataSource<{ [key: string]: any }>; @Input() public moreMenuThreshold: number; @Input() public item: any; diff --git a/projects/components/table/src/table.component.spec.ts b/projects/components/table/src/table.component.spec.ts index 6bc23388..17cd184c 100644 --- a/projects/components/table/src/table.component.spec.ts +++ b/projects/components/table/src/table.component.spec.ts @@ -103,20 +103,14 @@ function createColDef(data: { property?: string; header?: string; sortable?: boo -
- custom header -
+
custom header
custom settings {{ settings.pageSize }}
-
- custom button section -
+
custom button section
- + @@ -783,5 +777,63 @@ describe('PsTableComponent', () => { const firstRowSecondPage = (await table.getRows())[0]; expect(await (await firstRowSecondPage.getCells({ columnName: 'str' }))[0].getText()).toEqual('item 15'); }); + + describe('async table actions', () => { + beforeEach(async () => { + await initTestComponent( + new PsTableDataSource({ + loadDataFn: () => + of([ + { id: 1, str: 'item 1' }, + { id: 2, str: 'item 2' }, + { id: 3, str: 'item 3' }, + ]), + mode: 'client', + actions: of([ + { + label: 'custom action', + icon: '', + scope: PsTableActionScope.all, + actionFn: (selection) => component.onListActionExecute(selection), + }, + { + label: 'custom list action', + icon: '', + scope: PsTableActionScope.list, + actionFn: () => {}, + }, + { + label: 'custom row action', + icon: '', + scope: PsTableActionScope.row, + actionFn: () => {}, + }, + ]), + }) + ); + }); + + it('async list actions should work', async () => { + component.refreshable = false; + fixture.detectChanges(); + const listActionsButtonHarness = await table.getListActionsButton(); + await listActionsButtonHarness.open(); + const listActions = await listActionsButtonHarness.getItems(); + expect(listActions.length).toEqual(2); + expect(await listActions[0].getText()).toEqual('custom action'); + expect(await listActions[1].getText()).toEqual('custom list action'); + }); + + it('async row actions should work', async () => { + component.refreshable = false; + fixture.detectChanges(); + const rowActionsButtonHarness = await table.getRowActionsButton(1); + await rowActionsButtonHarness.open(); + const rowActions = await rowActionsButtonHarness.getItems(); + expect(rowActions.length).toEqual(2); + expect(await rowActions[0].getText()).toEqual('custom action'); + expect(await rowActions[1].getText()).toEqual('custom row action'); + }); + }); }); }); diff --git a/projects/components/table/src/table.module.ts b/projects/components/table/src/table.module.ts index d6908773..88aced9e 100644 --- a/projects/components/table/src/table.module.ts +++ b/projects/components/table/src/table.module.ts @@ -8,6 +8,7 @@ import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatMenuModule } from '@angular/material/menu'; import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; @@ -77,6 +78,7 @@ import { PsTableComponent } from './table.component'; MatInputModule, MatCardModule, MatTooltipModule, + MatProgressSpinnerModule, PsFlipContainerModule, PsSavebarModule, PsBlockUiModule, diff --git a/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts b/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts index 6325fa85..51c33395 100644 --- a/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts +++ b/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts @@ -3,7 +3,7 @@ import { PageEvent } from '@angular/material/paginator'; import { PsTableComponent, PsTableDataSource } from '@prosoft/components/table'; import { PsTableActionScope } from '@prosoft/components/table/src/models'; import { of, timer } from 'rxjs'; -import { first, map } from 'rxjs/operators'; +import { delay, first, map } from 'rxjs/operators'; interface ISampleData { id: number; @@ -169,6 +169,45 @@ export class TableDemoComponent { }, ], }, + { + label: 'async table actions', + icon: 'cancel', + scope: PsTableActionScope.all, + children: of([ + { + label: 'async table action 1', + icon: 'cancel', + scope: PsTableActionScope.all, + }, + { + label: 'async table action 2', + icon: 'cancel', + scope: PsTableActionScope.all, + }, + { + label: 'async table action 3', + icon: 'cancel', + scope: PsTableActionScope.all, + children: of([ + { + label: 'sub async table action 1', + icon: 'cancel', + scope: PsTableActionScope.all, + }, + { + label: 'sub async table action 2', + icon: 'cancel', + scope: PsTableActionScope.all, + }, + { + label: 'sub async table action 3', + icon: 'cancel', + scope: PsTableActionScope.all, + }, + ]).pipe(delay(400)), + }, + ]).pipe(delay(500)), + }, ], }); From d2107f151eddf80602cd1fb8cce11de8bccf0d37 Mon Sep 17 00:00:00 2001 From: Johann Schoen Date: Thu, 1 Jul 2021 08:38:31 +0200 Subject: [PATCH 2/7] feat(table): adds support for svg icons in table actions --- projects/components/table/src/models.ts | 14 ++++++-------- .../subcomponents/table-actions.component.html | 6 ++++-- .../src/app/app.component.ts | 8 +++++++- .../src/app/app.module.ts | 4 ++++ .../src/app/table-demo/table-demo.component.ts | 3 ++- .../src/assets/angular.svg | 16 ++++++++++++++++ 6 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 projects/prosoft-components-demo/src/assets/angular.svg diff --git a/projects/components/table/src/models.ts b/projects/components/table/src/models.ts index bf7e2c78..2e8a091c 100644 --- a/projects/components/table/src/models.ts +++ b/projects/components/table/src/models.ts @@ -31,6 +31,7 @@ export const enum PsTableActionScope { export interface IPsTableAction { label: string; icon: string; + isSvgIcon?: boolean; iconColor?: string; children?: IPsTableAction[] | Observable[]>; scope: PsTableActionScope; @@ -42,13 +43,14 @@ export interface IPsTableAction { export class PsTableAction { public readonly label: string; public readonly icon: string; + public readonly isSvgIcon: boolean; public readonly iconColor?: string; public readonly scope: PsTableActionScope; public readonly isDisabledFn?: (items: T[]) => boolean; public readonly isHiddenFn?: (items: T[]) => boolean; public readonly actionFn?: (items: T[]) => void; public readonly isObservable: boolean; - + public readonly hasChildren: boolean; public children$: Observable[]>; public get isLoading(): boolean { @@ -56,14 +58,10 @@ export class PsTableAction { } private _isLoading = false; - public get hasChildren(): boolean { - return this._hasChildren; - } - private _hasChildren = false; - constructor(declaration: IPsTableAction, private dataSource: PsTableDataSource) { this.label = declaration.label; this.icon = declaration.icon; + this.isSvgIcon = declaration.isSvgIcon === true; this.iconColor = declaration.iconColor; this.scope = declaration.scope; this.isDisabledFn = declaration.isDisabledFn; @@ -71,9 +69,9 @@ export class PsTableAction { this.actionFn = declaration.actionFn; this.isObservable = isObservable(declaration.children); - this._hasChildren = Array.isArray(declaration.children) || this.isObservable; + this.hasChildren = Array.isArray(declaration.children) || this.isObservable; - if (this._hasChildren) { + if (this.hasChildren) { this.children$ = isObservable(declaration.children) ? of(void 0).pipe( tap(() => (this._isLoading = true)), diff --git a/projects/components/table/src/subcomponents/table-actions.component.html b/projects/components/table/src/subcomponents/table-actions.component.html index 5041d964..c3da2a29 100644 --- a/projects/components/table/src/subcomponents/table-actions.component.html +++ b/projects/components/table/src/subcomponents/table-actions.component.html @@ -11,7 +11,8 @@ [disabled]="action.isLoading || (action.isDisabledFn && action.isDisabledFn(items))" (click)="action.actionFn && action.actionFn(items)" > - {{ action.icon }} + {{ action.icon }} + @@ -19,7 +20,8 @@ + + + + + + + + + From 7836f62de9de6426913e6c4b2cf5ee916b288c43 Mon Sep 17 00:00:00 2001 From: Johann Schoen Date: Thu, 1 Jul 2021 08:54:49 +0200 Subject: [PATCH 3/7] refactor(table): unifies table action icon --- .../subcomponents/table-actions.component.html | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/projects/components/table/src/subcomponents/table-actions.component.html b/projects/components/table/src/subcomponents/table-actions.component.html index c3da2a29..98f06e6e 100644 --- a/projects/components/table/src/subcomponents/table-actions.component.html +++ b/projects/components/table/src/subcomponents/table-actions.component.html @@ -11,17 +11,12 @@ [disabled]="action.isLoading || (action.isDisabledFn && action.isDisabledFn(items))" (click)="action.actionFn && action.actionFn(items)" > - {{ action.icon }} - - - - + {{ action.label }} + + + {{ action.icon }} + + + + + From e0e41a778cc8296f3d6d59f06a9cae608a417e6b Mon Sep 17 00:00:00 2001 From: Johann Schoen Date: Thu, 1 Jul 2021 10:08:34 +0200 Subject: [PATCH 4/7] feat(table): adds support for routerLink for table actions --- projects/components/table/src/models.ts | 8 ++++- .../table-actions.component.html | 33 ++++++++++++++----- .../subcomponents/table-actions.component.ts | 5 ++- .../subcomponents/table-data.component.html | 1 + .../src/subcomponents/table-data.component.ts | 3 ++ .../table-row-actions.component.html | 16 +++++++-- .../table-row-actions.component.ts | 4 ++- projects/components/table/src/table.module.ts | 2 ++ .../app/table-demo/table-demo.component.ts | 5 +-- 9 files changed, 61 insertions(+), 16 deletions(-) diff --git a/projects/components/table/src/models.ts b/projects/components/table/src/models.ts index 2e8a091c..cf923e7a 100644 --- a/projects/components/table/src/models.ts +++ b/projects/components/table/src/models.ts @@ -21,7 +21,7 @@ export interface IExtendedPsTableUpdateDataInfo extends IPsTableUpdate triggerData: TTrigger; } -export const enum PsTableActionScope { +export enum PsTableActionScope { row = 1, list = 2, // tslint:disable-next-line:no-bitwise @@ -38,6 +38,8 @@ export interface IPsTableAction { isDisabledFn?: (items: T[]) => boolean; isHiddenFn?: (items: T[]) => boolean; actionFn?: (items: T[]) => void; + routerLink?: (item: T) => []; + routerLinkQueryParams?: { [key: string]: any }; } export class PsTableAction { @@ -51,6 +53,8 @@ export class PsTableAction { public readonly actionFn?: (items: T[]) => void; public readonly isObservable: boolean; public readonly hasChildren: boolean; + public readonly routerLink?: (item: T) => string[]; + public readonly routerLinkQueryParams?: { [key: string]: any }; public children$: Observable[]>; public get isLoading(): boolean { @@ -67,6 +71,8 @@ export class PsTableAction { this.isDisabledFn = declaration.isDisabledFn; this.isHiddenFn = declaration.isHiddenFn; this.actionFn = declaration.actionFn; + this.routerLink = declaration.routerLink; + this.routerLinkQueryParams = declaration.routerLinkQueryParams; this.isObservable = isObservable(declaration.children); this.hasChildren = Array.isArray(declaration.children) || this.isObservable; diff --git a/projects/components/table/src/subcomponents/table-actions.component.html b/projects/components/table/src/subcomponents/table-actions.component.html index 98f06e6e..0e7815d6 100644 --- a/projects/components/table/src/subcomponents/table-actions.component.html +++ b/projects/components/table/src/subcomponents/table-actions.component.html @@ -5,15 +5,29 @@ - + + + + {{ action.label }} + + + + + - + @@ -30,7 +36,13 @@ > {{ action.icon }} - + diff --git a/projects/components/table/src/subcomponents/table-row-actions.component.ts b/projects/components/table/src/subcomponents/table-row-actions.component.ts index f97963b2..4cc2e3d3 100644 --- a/projects/components/table/src/subcomponents/table-row-actions.component.ts +++ b/projects/components/table/src/subcomponents/table-row-actions.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges, TemplateRef } from '@angular/core'; import { PsTableDataSource } from '../data/table-data-source'; -import { PsTableAction } from '../models'; +import { PsTableAction, PsTableActionScope } from '../models'; @Component({ selector: 'ps-table-row-actions', @@ -15,6 +15,8 @@ export class PsTableRowActionsComponent implements OnChanges { @Input() public moreMenuThreshold: number; @Input() public item: any; + public psTableActionScopes = PsTableActionScope; + public itemAsArray: any[]; public ngOnChanges(changes: SimpleChanges) { diff --git a/projects/components/table/src/table.module.ts b/projects/components/table/src/table.module.ts index 88aced9e..67921793 100644 --- a/projects/components/table/src/table.module.ts +++ b/projects/components/table/src/table.module.ts @@ -13,6 +13,7 @@ import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { RouterModule } from '@angular/router'; import { PsBlockUiModule } from '@prosoft/components/block-ui'; import { PsFlipContainerModule } from '@prosoft/components/flip-container'; import { PsSavebarModule } from '@prosoft/components/savebar'; @@ -67,6 +68,7 @@ import { PsTableComponent } from './table.component'; imports: [ CommonModule, FormsModule, + RouterModule, MatSortModule, MatTableModule, MatPaginatorModule, diff --git a/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts b/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts index 57775f61..3e221a89 100644 --- a/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts +++ b/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ViewChild } from '@angular/core'; import { PageEvent } from '@angular/material/paginator'; import { PsTableComponent, PsTableDataSource } from '@prosoft/components/table'; -import { PsTableActionScope } from '@prosoft/components/table/src/models'; +import { IPsTableAction, PsTableActionScope } from '@prosoft/components/table/src/models'; import { of, timer } from 'rxjs'; import { delay, first, map } from 'rxjs/operators'; @@ -194,6 +194,7 @@ export class TableDemoComponent { label: 'sub async table action 1', icon: 'cancel', scope: PsTableActionScope.all, + routerLink: (item: ISampleData) => ['..', item.id], }, { label: 'sub async table action 2', @@ -207,7 +208,7 @@ export class TableDemoComponent { }, ]).pipe(delay(400)), }, - ]).pipe(delay(500)), + ] as IPsTableAction[]).pipe(delay(500)), }, ], }); From 45edb905aedecba867a1cfc8c2056cf83f9cc632 Mon Sep 17 00:00:00 2001 From: Johann Schoen Date: Thu, 1 Jul 2021 10:52:44 +0200 Subject: [PATCH 5/7] test(table): adds test for icon/svgIcon support for table actions --- .../table/src/table.component.spec.ts | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/projects/components/table/src/table.component.spec.ts b/projects/components/table/src/table.component.spec.ts index 17cd184c..18a35c5b 100644 --- a/projects/components/table/src/table.component.spec.ts +++ b/projects/components/table/src/table.component.spec.ts @@ -3,12 +3,15 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { CommonModule } from '@angular/common'; import { ChangeDetectorRef, Component, Injectable, QueryList, ViewChild } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { IconType, MatIconHarness } from '@angular/material/icon/testing'; +import { MatMenuItemHarness } from '@angular/material/menu/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { ActivatedRoute, convertToParamMap, ParamMap, Params, Router } from '@angular/router'; import { IPsTableIntlTexts, PsIntlService, PsIntlServiceEn } from '@prosoft/components/core'; import { filterAsync } from '@prosoft/components/utils/src/array'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; + import { PsTableDataSource } from './data/table-data-source'; import { PsTableColumnDirective } from './directives/table.directives'; import { PsTableMemoryStateManager } from './helper/state-manager'; @@ -778,6 +781,108 @@ describe('PsTableComponent', () => { expect(await (await firstRowSecondPage.getCells({ columnName: 'str' }))[0].getText()).toEqual('item 15'); }); + describe('table actions', () => { + beforeEach(async () => { + await initTestComponent( + new PsTableDataSource({ + loadDataFn: () => + of([ + { id: 1, str: 'item 1' }, + { id: 2, str: 'item 2' }, + { id: 3, str: 'item 3' }, + ]), + mode: 'client', + actions: [ + { + label: 'custom action 1', + icon: 'list', + scope: PsTableActionScope.all, + actionFn: (selection) => component.onListActionExecute(selection), + }, + { + label: 'custom action 2', + icon: 'angular', + isSvgIcon: true, + scope: PsTableActionScope.all, + actionFn: (selection) => component.onListActionExecute(selection), + }, + { + label: 'custom list action 1', + icon: 'list', + scope: PsTableActionScope.list, + actionFn: () => {}, + }, + { + label: 'custom list action 2', + icon: 'angular', + isSvgIcon: true, + scope: PsTableActionScope.list, + actionFn: () => {}, + }, + { + label: 'custom row action 1', + icon: 'list', + scope: PsTableActionScope.row, + actionFn: () => {}, + }, + { + label: 'custom row action 2', + icon: 'angular', + isSvgIcon: true, + scope: PsTableActionScope.row, + actionFn: () => {}, + }, + ], + }) + ); + + component.refreshable = false; + component.showSettings = false; + }); + + it('icon and svgIcon should be displayed correctly', async () => { + fixture.detectChanges(); + + // list actions + const listActionButtonHarness = await table.getListActionsButton(); + await listActionButtonHarness.open(); + const listActionHarnesses = await listActionButtonHarness.getItems(); + expect(listActionHarnesses.length).toBe(4); + await checkAction$(listActionHarnesses[0], 'list', IconType.FONT, 'custom action 1'); + await checkAction$(listActionHarnesses[1], 'angular', IconType.SVG, 'custom action 2'); + await checkAction$(listActionHarnesses[2], 'list', IconType.FONT, 'custom list action 1'); + await checkAction$(listActionHarnesses[3], 'angular', IconType.SVG, 'custom list action 2'); + + // row actions + const rowActionButtonHarness = await table.getRowActionsButton(1); + await rowActionButtonHarness.open(); + const rowActionHarnesses = await rowActionButtonHarness.getItems(); + expect(rowActionHarnesses.length).toBe(4); + + await checkAction$(rowActionHarnesses[0], 'list', IconType.FONT, 'custom action 1'); + await checkAction$(rowActionHarnesses[1], 'angular', IconType.SVG, 'custom action 2'); + await checkAction$(rowActionHarnesses[2], 'list', IconType.FONT, 'custom row action 1'); + await checkAction$(rowActionHarnesses[3], 'angular', IconType.SVG, 'custom row action 2'); + }); + + async function checkAction$( + menuItemHarness: MatMenuItemHarness, + expectedIcon: string, + expectedIconType: IconType, + expectedText: string + ): Promise { + expect(menuItemHarness).toBeTruthy(); + + const iconHarness = await menuItemHarness.getHarness(MatIconHarness); + expect(iconHarness).toBeTruthy(); + expect(await iconHarness.getName()).toEqual(expectedIcon); + expect(await iconHarness.getType()).toEqual(expectedIconType); + + // we use toContain here, because getText() includes the icon name as well + expect(await menuItemHarness.getText()).toContain(expectedText); + } + }); + describe('async table actions', () => { beforeEach(async () => { await initTestComponent( From c3783a5f6ebca95a31b2da92f36014675404b0b6 Mon Sep 17 00:00:00 2001 From: Johann Schoen Date: Thu, 1 Jul 2021 13:38:50 +0200 Subject: [PATCH 6/7] test(table): tests for routerlink table actions --- projects/components/table/src/models.ts | 4 +-- .../table-actions.component.html | 2 +- .../table/src/table.component.spec.ts | 33 ++++++++++++++----- .../app/table-demo/table-demo.component.ts | 12 +++++-- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/projects/components/table/src/models.ts b/projects/components/table/src/models.ts index cf923e7a..e3d246a6 100644 --- a/projects/components/table/src/models.ts +++ b/projects/components/table/src/models.ts @@ -39,7 +39,7 @@ export interface IPsTableAction { isHiddenFn?: (items: T[]) => boolean; actionFn?: (items: T[]) => void; routerLink?: (item: T) => []; - routerLinkQueryParams?: { [key: string]: any }; + routerLinkQueryParams?: (item: T) => { [key: string]: any }; } export class PsTableAction { @@ -54,7 +54,7 @@ export class PsTableAction { public readonly isObservable: boolean; public readonly hasChildren: boolean; public readonly routerLink?: (item: T) => string[]; - public readonly routerLinkQueryParams?: { [key: string]: any }; + public readonly routerLinkQueryParams?: (item: T) => { [key: string]: any }; public children$: Observable[]>; public get isLoading(): boolean { diff --git a/projects/components/table/src/subcomponents/table-actions.component.html b/projects/components/table/src/subcomponents/table-actions.component.html index 0e7815d6..81bd920a 100644 --- a/projects/components/table/src/subcomponents/table-actions.component.html +++ b/projects/components/table/src/subcomponents/table-actions.component.html @@ -11,7 +11,7 @@ mat-menu-item [disabled]="action.isLoading || (action.isDisabledFn && action.isDisabledFn(items))" [routerLink]="action.routerLink(items[0])" - [queryParams]="action.routerLinkQueryParams" + [queryParams]="(action.routerLinkQueryParams && action.routerLinkQueryParams(items[0])) || undefined" > {{ action.label }} diff --git a/projects/components/table/src/table.component.spec.ts b/projects/components/table/src/table.component.spec.ts index 18a35c5b..f0c62665 100644 --- a/projects/components/table/src/table.component.spec.ts +++ b/projects/components/table/src/table.component.spec.ts @@ -5,8 +5,10 @@ import { ChangeDetectorRef, Component, Injectable, QueryList, ViewChild } from ' import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { IconType, MatIconHarness } from '@angular/material/icon/testing'; import { MatMenuItemHarness } from '@angular/material/menu/testing'; +import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { ActivatedRoute, convertToParamMap, ParamMap, Params, Router } from '@angular/router'; +import { ActivatedRoute, convertToParamMap, ParamMap, Params, RouterLinkWithHref } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; import { IPsTableIntlTexts, PsIntlService, PsIntlServiceEn } from '@prosoft/components/core'; import { filterAsync } from '@prosoft/components/utils/src/array'; import { BehaviorSubject, Observable, of } from 'rxjs'; @@ -15,7 +17,7 @@ import { map } from 'rxjs/operators'; import { PsTableDataSource } from './data/table-data-source'; import { PsTableColumnDirective } from './directives/table.directives'; import { PsTableMemoryStateManager } from './helper/state-manager'; -import { IPsTableSortDefinition, PsTableActionScope } from './models'; +import { IPsTableAction, IPsTableSortDefinition, PsTableActionScope } from './models'; import { IPsTableSetting, PsTableSettingsService } from './services/table-settings.service'; import { PsTablePaginationComponent } from './subcomponents/table-pagination.component'; import { PsTableComponent } from './table.component'; @@ -43,6 +45,7 @@ class TestSettingsService extends PsTableSettingsService { const router: any = { navigate: (_route: any, _options: any) => {}, + navigateByUrl: (_urltree: any, _extras: any) => {}, }; const queryParams$ = new BehaviorSubject(convertToParamMap({ other: 'value' })); @@ -480,16 +483,12 @@ describe('PsTableComponent', () => { } beforeEach(async () => { - queryParams$.next(convertToParamMap({})); - await TestBed.configureTestingModule({ - imports: [NoopAnimationsModule, CommonModule, PsTableModule], + imports: [NoopAnimationsModule, CommonModule, PsTableModule, RouterTestingModule], declarations: [TestComponent], providers: [ { provide: PsTableSettingsService, useClass: TestSettingsService }, { provide: PsIntlService, useClass: PsIntlServiceEn }, - { provide: ActivatedRoute, useValue: route }, - { provide: Router, useValue: router }, ], }); @@ -823,7 +822,10 @@ describe('PsTableComponent', () => { label: 'custom row action 1', icon: 'list', scope: PsTableActionScope.row, - actionFn: () => {}, + routerLink: (item: any) => [item.id], + routerLinkQueryParams: (item: any) => ({ + a: item.str.replace(' ', '_'), + }), }, { label: 'custom row action 2', @@ -832,7 +834,7 @@ describe('PsTableComponent', () => { scope: PsTableActionScope.row, actionFn: () => {}, }, - ], + ] as IPsTableAction[], }) ); @@ -865,6 +867,19 @@ describe('PsTableComponent', () => { await checkAction$(rowActionHarnesses[3], 'angular', IconType.SVG, 'custom row action 2'); }); + it('routerlinks should be displayed correctly', async () => { + fixture.detectChanges(); + + const rowActionButtonHarness = await table.getRowActionsButton(1); + await rowActionButtonHarness.open(); + const rowActionHarnesses = await rowActionButtonHarness.getItems(); + expect(rowActionHarnesses.length).toBe(4); + + const links = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); + expect(links.length).toEqual(1); + expect(links[0].attributes['href']).toEqual('/1?a=item_1'); + }); + async function checkAction$( menuItemHarness: MatMenuItemHarness, expectedIcon: string, diff --git a/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts b/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts index 3e221a89..8aecd9c1 100644 --- a/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts +++ b/projects/prosoft-components-demo/src/app/table-demo/table-demo.component.ts @@ -193,8 +193,7 @@ export class TableDemoComponent { { label: 'sub async table action 1', icon: 'cancel', - scope: PsTableActionScope.all, - routerLink: (item: ISampleData) => ['..', item.id], + scope: PsTableActionScope.row, }, { label: 'sub async table action 2', @@ -208,6 +207,15 @@ export class TableDemoComponent { }, ]).pipe(delay(400)), }, + { + label: 'router link', + icon: 'cancel', + scope: PsTableActionScope.row, + routerLink: (item: ISampleData) => ['..', item.id], + routerLinkQueryParams: (item: ISampleData) => ({ + a: item.string, + }), + }, ] as IPsTableAction[]).pipe(delay(500)), }, ], From ddbef581f288d431c5fe188cccf661b22cffbae5 Mon Sep 17 00:00:00 2001 From: Johann Schoen Date: Thu, 1 Jul 2021 17:11:40 +0200 Subject: [PATCH 7/7] refactor(table): refactores table action caching and removes obsolete dataSource references --- projects/components/table/public_api.ts | 1 + .../table/src/data/table-data-source.ts | 28 ++++--------------- .../table/src/helper/action-store.spec.ts | 0 .../table/src/helper/action-store.ts | 28 +++++++++++++++++++ projects/components/table/src/models.ts | 8 +++--- .../table-actions.component.html | 1 - .../subcomponents/table-actions.component.ts | 2 -- .../subcomponents/table-data.component.html | 2 -- .../table-row-actions.component.html | 9 +----- .../table-row-actions.component.ts | 2 -- 10 files changed, 39 insertions(+), 42 deletions(-) create mode 100644 projects/components/table/src/helper/action-store.spec.ts create mode 100644 projects/components/table/src/helper/action-store.ts diff --git a/projects/components/table/public_api.ts b/projects/components/table/public_api.ts index 8dff423d..fff65f44 100644 --- a/projects/components/table/public_api.ts +++ b/projects/components/table/public_api.ts @@ -14,6 +14,7 @@ export { PsTableTopButtonSectionDirective, } from './src/directives/table.directives'; export { PsTableMemoryStateManager, PsTableStateManager, PsTableUrlStateManager } from './src/helper/state-manager'; +export { PsTableActionStoreBase, PsTableActionStore } from './src/helper/action-store'; export { IExtendedPsTableUpdateDataInfo, IPsTableAction, IPsTableSortDefinition, IPsTableUpdateDataInfo } from './src/models'; export { IPsTableSetting, PsTableSettingsService } from './src/services/table-settings.service'; export { PsTableComponent } from './src/table.component'; diff --git a/projects/components/table/src/data/table-data-source.ts b/projects/components/table/src/data/table-data-source.ts index 068fac2f..41117def 100644 --- a/projects/components/table/src/data/table-data-source.ts +++ b/projects/components/table/src/data/table-data-source.ts @@ -2,6 +2,7 @@ import { DataSource, SelectionModel } from '@angular/cdk/collections'; import { TrackByFunction } from '@angular/core'; import { BehaviorSubject, isObservable, NEVER, Observable, of, Subject, Subscription } from 'rxjs'; import { catchError, finalize, map, take, tap } from 'rxjs/operators'; +import { PsTableActionStore, PsTableActionStoreBase } from '../helper/action-store'; import { _isNumberValue } from '../helper/table.helper'; import { IExtendedPsTableUpdateDataInfo, IPsTableAction, IPsTableUpdateDataInfo, PsTableAction, PsTableActionScope } from '../models'; @@ -101,8 +102,8 @@ export class PsTableDataSource extends DataSource { public readonly moreMenuThreshold: number; - /** Action-Definition -> Action Store */ - private readonly _store = new WeakMap, PsTableAction>(); + /** Stores table actions.*/ + private readonly _store: PsTableActionStoreBase = new PsTableActionStore(); /** Stream that emits when a new data array is set on the data source. */ private readonly _updateDataTrigger$: Observable; @@ -313,25 +314,6 @@ export class PsTableDataSource extends DataSource { return data; } - /** - * Gets the existing PsTableAction object by the declaration from the store or creates a new one. - * - * Because actions can have async children, we cannot create for each declaration the coresponding PsTableAction at the beginning. - * This forces us to create the PsTableAction dynamicly. But we do not want to execute the same observables multiple times, so we cache them. - * Otherwise the observable would be executed for each row. - * - * @param declaration Action declaration information - * @returns Existing or new PsTableAction - */ - public getAction(declaration: IPsTableAction): PsTableAction { - if (!this._store.has(declaration)) { - const action = new PsTableAction(declaration, this); - this._store.set(declaration, action); - } - - return this._store.get(declaration); - } - public updateActions(): void { if (!this._actions) { return; @@ -343,13 +325,13 @@ export class PsTableDataSource extends DataSource { }; if (Array.isArray(this._actions) && this._actions.length) { - setActions(this._actions.map((x) => this.getAction(x))); + setActions(this._actions.map((x) => this._store.get(x))); } else if (isObservable(this._actions)) { this._loadActionsSubscription?.unsubscribe(); this._loadActionsSubscription = this._actions .pipe( take(1), - map((x) => x.map((y) => this.getAction(y))), + map((x) => x.map((y) => this._store.get(y))), finalize(() => this._internalDetectChanges.next()) ) .subscribe((actions) => setActions(actions)); diff --git a/projects/components/table/src/helper/action-store.spec.ts b/projects/components/table/src/helper/action-store.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/projects/components/table/src/helper/action-store.ts b/projects/components/table/src/helper/action-store.ts new file mode 100644 index 00000000..de70513d --- /dev/null +++ b/projects/components/table/src/helper/action-store.ts @@ -0,0 +1,28 @@ +import { IPsTableAction, PsTableAction } from '../../src/models'; + +export abstract class PsTableActionStoreBase { + abstract get(declaration: IPsTableAction): PsTableAction; +} + +export class PsTableActionStore extends PsTableActionStoreBase { + private readonly _store = new WeakMap, PsTableAction>(); + + /** + * Gets the existing PsTableAction object by the declaration (IPsTableAction) from the store or creates a new one. + * + * Because actions can have observables as children, we cannot create for each declaration the coresponding PsTableAction at the beginning. + * This forces us to create the PsTableAction dynamicly. But we do not want to execute the same observables multiple times, so we cache them. + * Otherwise the observable would be executed for each row. + * + * @param declaration Action declaration information + * @returns Existing or new PsTableAction + */ + public get(declaration: IPsTableAction): PsTableAction { + if (!this._store.has(declaration)) { + const action = new PsTableAction(declaration, this); + this._store.set(declaration, action); + } + + return this._store.get(declaration); + } +} diff --git a/projects/components/table/src/models.ts b/projects/components/table/src/models.ts index e3d246a6..481206ca 100644 --- a/projects/components/table/src/models.ts +++ b/projects/components/table/src/models.ts @@ -1,6 +1,6 @@ import { isObservable, Observable, of } from 'rxjs'; import { map, shareReplay, switchMap, tap } from 'rxjs/operators'; -import { PsTableDataSource } from './data/table-data-source'; +import { PsTableActionStoreBase } from './helper/action-store'; export interface IPsTableSortDefinition { prop: string; @@ -62,7 +62,7 @@ export class PsTableAction { } private _isLoading = false; - constructor(declaration: IPsTableAction, private dataSource: PsTableDataSource) { + constructor(declaration: IPsTableAction, store: PsTableActionStoreBase) { this.label = declaration.label; this.icon = declaration.icon; this.isSvgIcon = declaration.isSvgIcon === true; @@ -84,9 +84,9 @@ export class PsTableAction { switchMap(() => declaration.children as Observable[]>), tap(() => (this._isLoading = false)), shareReplay({ bufferSize: 1, refCount: false }), - map((x) => x.map((y) => this.dataSource.getAction(y) as PsTableAction) || []) + map((x) => x.map((y) => store.get(y) as PsTableAction) || []) ) - : of(declaration.children.map((x) => this.dataSource.getAction(x) as PsTableAction)); + : of(declaration.children.map((x) => store.get(x) as PsTableAction)); } else { this.children$ = of([]); } diff --git a/projects/components/table/src/subcomponents/table-actions.component.html b/projects/components/table/src/subcomponents/table-actions.component.html index 81bd920a..3941652b 100644 --- a/projects/components/table/src/subcomponents/table-actions.component.html +++ b/projects/components/table/src/subcomponents/table-actions.component.html @@ -37,7 +37,6 @@ #innerPanel [actions]="action.children$ | async | psTableActionsToRender: items" [items]="items" - [dataSource]="dataSource" [scope]="scope" > diff --git a/projects/components/table/src/subcomponents/table-actions.component.ts b/projects/components/table/src/subcomponents/table-actions.component.ts index 8542fc29..47b41ad4 100644 --- a/projects/components/table/src/subcomponents/table-actions.component.ts +++ b/projects/components/table/src/subcomponents/table-actions.component.ts @@ -4,7 +4,6 @@ import { IPsTableIntlTexts } from '@prosoft/components/core'; import { merge, Subscription } from 'rxjs'; import { tap } from 'rxjs/operators'; -import { PsTableDataSource } from '../data/table-data-source'; import { PsTableAction, PsTableActionScope } from '../models'; @Component({ @@ -29,7 +28,6 @@ export class PsTableActionsComponent implements OnDestroy { } } private _actions: PsTableAction[]; - @Input() public dataSource: PsTableDataSource<{ [key: string]: any }>; @Input() public items: unknown[]; @Input() public refreshable: boolean; @Input() public settingsEnabled: boolean; diff --git a/projects/components/table/src/subcomponents/table-data.component.html b/projects/components/table/src/subcomponents/table-data.component.html index 181df3f2..4c96e8bb 100644 --- a/projects/components/table/src/subcomponents/table-data.component.html +++ b/projects/components/table/src/subcomponents/table-data.component.html @@ -57,7 +57,6 @@ [refreshable]="refreshable" [settingsEnabled]="settingsEnabled" [intl]="intl" - [dataSource]="dataSource" [scope]="psTableActionScopes.list" (refreshData)="onRefreshDataClicked()" (showSettings)="onShowSettingsClicked()" @@ -70,7 +69,6 @@ diff --git a/projects/components/table/src/subcomponents/table-row-actions.component.html b/projects/components/table/src/subcomponents/table-row-actions.component.html index 5795a3fa..a3df9e3e 100644 --- a/projects/components/table/src/subcomponents/table-row-actions.component.html +++ b/projects/components/table/src/subcomponents/table-row-actions.component.html @@ -3,13 +3,7 @@ - + @@ -40,7 +34,6 @@ #iconActions [actions]="action.children" [items]="itemAsArray" - [dataSource]="dataSource" [scope]="psTableActionScopes.row" > diff --git a/projects/components/table/src/subcomponents/table-row-actions.component.ts b/projects/components/table/src/subcomponents/table-row-actions.component.ts index 4cc2e3d3..cd476105 100644 --- a/projects/components/table/src/subcomponents/table-row-actions.component.ts +++ b/projects/components/table/src/subcomponents/table-row-actions.component.ts @@ -1,6 +1,5 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges, TemplateRef } from '@angular/core'; -import { PsTableDataSource } from '../data/table-data-source'; import { PsTableAction, PsTableActionScope } from '../models'; @Component({ @@ -11,7 +10,6 @@ import { PsTableAction, PsTableActionScope } from '../models'; export class PsTableRowActionsComponent implements OnChanges { @Input() public actions: PsTableAction[]; @Input() public actionsTemplate: TemplateRef | null = null; - @Input() public dataSource: PsTableDataSource<{ [key: string]: any }>; @Input() public moreMenuThreshold: number; @Input() public item: any;