From 5f3745131a2df11a2b82ade7efc8d56d7f79327d Mon Sep 17 00:00:00 2001 From: Maximilian Koeller Date: Thu, 26 Sep 2024 08:51:24 +0200 Subject: [PATCH] fix: improve types for row grouping --- .../components/body/body-cell.component.ts | 6 +-- .../body-group-header-template.directive.ts | 8 ++-- .../body/body-group-header.directive.ts | 8 ++-- .../body/body-row-wrapper.component.ts | 47 +++++++++++-------- .../lib/components/body/body-row.component.ts | 2 +- .../src/lib/components/body/body.component.ts | 37 ++++++++------- .../src/lib/components/datatable.component.ts | 2 +- .../row-detail-template.directive.ts | 8 ++-- .../row-detail/row-detail.directive.ts | 4 +- .../src/lib/types/public.types.ts | 31 +++++++++--- src/app/basic/row-grouping.component.ts | 8 ++-- 11 files changed, 96 insertions(+), 65 deletions(-) diff --git a/projects/ngx-datatable/src/lib/components/body/body-cell.component.ts b/projects/ngx-datatable/src/lib/components/body/body-cell.component.ts index 55e6e38ed..e12ce5795 100644 --- a/projects/ngx-datatable/src/lib/components/body/body-cell.component.ts +++ b/projects/ngx-datatable/src/lib/components/body/body-cell.component.ts @@ -166,14 +166,14 @@ export class DataTableBodyCellComponent return this._column; } - @Input() set row(row: RowOrGroup) { + @Input() set row(row: TRow) { this._row = row; this.cellContext.row = row; this.checkValueUpdates(); this.cd.markForCheck(); } - get row(): RowOrGroup { + get row(): TRow { return this._row; } @@ -298,7 +298,7 @@ export class DataTableBodyCellComponent private _isSelected: boolean; private _sorts: SortPropDir[]; private _column: TableColumn; - private _row: RowOrGroup; + private _row: TRow; private _group: TRow[]; private _rowHeight: number; private _rowIndex: number; diff --git a/projects/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts b/projects/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts index 485c80bf5..a7052c2d1 100644 --- a/projects/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts +++ b/projects/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts @@ -4,11 +4,11 @@ import { GroupContext } from '../../types/public.types'; @Directive({ selector: '[ngx-datatable-group-header-template]' }) -export class DatatableGroupHeaderTemplateDirective { - static ngTemplateContextGuard( - directive: DatatableGroupHeaderTemplateDirective, +export class DatatableGroupHeaderTemplateDirective { + static ngTemplateContextGuard( + directive: DatatableGroupHeaderTemplateDirective, context: unknown - ): context is GroupContext { + ): context is GroupContext { return true; } } diff --git a/projects/ngx-datatable/src/lib/components/body/body-group-header.directive.ts b/projects/ngx-datatable/src/lib/components/body/body-group-header.directive.ts index 056ee4bbc..6f05b4153 100644 --- a/projects/ngx-datatable/src/lib/components/body/body-group-header.directive.ts +++ b/projects/ngx-datatable/src/lib/components/body/body-group-header.directive.ts @@ -1,13 +1,13 @@ import { ContentChild, Directive, EventEmitter, Input, Output, TemplateRef } from '@angular/core'; import { DatatableGroupHeaderTemplateDirective } from './body-group-header-template.directive'; -import { GroupContext } from '../../types/public.types'; +import { Group, GroupContext, GroupToggleEvents } from '../../types/public.types'; @Directive({ selector: 'ngx-datatable-group-header' }) export class DatatableGroupHeaderDirective { /** * Row height is required when virtual scroll is enabled. */ - @Input() rowHeight: number | ((group?: any, index?: number) => number) = 0; + @Input() rowHeight: number | ((group?: Group, index?: number) => number) = 0; /** * Show checkbox at group header to select all rows of the group. @@ -27,12 +27,12 @@ export class DatatableGroupHeaderDirective { /** * Track toggling of group visibility */ - @Output() toggle: EventEmitter = new EventEmitter(); + @Output() toggle: EventEmitter> = new EventEmitter(); /** * Toggle the expansion of a group */ - toggleExpandGroup(group: any): void { + toggleExpandGroup(group: Group): void { this.toggle.emit({ type: 'group', value: group diff --git a/projects/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts b/projects/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts index 816d5c1c8..80d4e8da4 100644 --- a/projects/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts +++ b/projects/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts @@ -94,8 +94,7 @@ export class DataTableRowWrapperComponent implements DoCheck, OnInit @Input() set rowIndex(val: number) { this._rowIndex = val; - this.rowContext.rowIndex = val; - this.groupContext.rowIndex = val; + (this.rowContext ?? this.groupContext).rowIndex = val; this.cd.markForCheck(); } @@ -107,7 +106,7 @@ export class DataTableRowWrapperComponent implements DoCheck, OnInit @Input() set expanded(val: boolean) { this._expanded = val; - this.groupContext.expanded = val; + (this.groupContext ?? this.rowContext)!.expanded = val; this.rowContext.expanded = val; this.cd.markForCheck(); } @@ -116,8 +115,8 @@ export class DataTableRowWrapperComponent implements DoCheck, OnInit return this._expanded; } - groupContext: GroupContext; - rowContext: RowDetailContext; + groupContext?: GroupContext; + rowContext?: RowDetailContext; disable$: BehaviorSubject; private rowDiffer: KeyValueDiffer, any>; @@ -131,18 +130,21 @@ export class DataTableRowWrapperComponent implements DoCheck, OnInit differs: KeyValueDiffers, private iterableDiffers: IterableDiffers ) { - this.groupContext = { - group: this.row, - expanded: this.expanded, - rowIndex: this.rowIndex - }; - - this.rowContext = { - row: this.row, - expanded: this.expanded, - rowIndex: this.rowIndex, - disableRow$: this.disable$ - }; + // this component renders either a group header or a row. Never both. + if (this.isGroup(this.row)) { + this.groupContext = { + group: this.row, + expanded: this.expanded, + rowIndex: this.rowIndex + }; + } else { + this.rowContext = { + row: this.row, + expanded: this.expanded, + rowIndex: this.rowIndex, + disableRow$: this.disable$ + }; + } this.rowDiffer = differs.find({}).create(); this.selectedRowsDiffer = this.iterableDiffers.find(this.selected ?? []).create(); @@ -174,8 +176,11 @@ export class DataTableRowWrapperComponent implements DoCheck, OnInit } if (this.rowDiffer.diff(this.row)) { - this.rowContext.row = this.row; - this.groupContext.group = this.row; + if (this.isGroup(this.row)) { + this.groupContext.group = this.row; + } else { + this.rowContext.row = this.row; + } this.cd.markForCheck(); } // When groupheader is used with chechbox we use iterableDiffer @@ -222,4 +227,8 @@ export class DataTableRowWrapperComponent implements DoCheck, OnInit selected: this.selected }); } + + isGroup(row: RowOrGroup): row is Group { + return !!this.groupHeader; + } } diff --git a/projects/ngx-datatable/src/lib/components/body/body-row.component.ts b/projects/ngx-datatable/src/lib/components/body/body-row.component.ts index 306e9b163..eae0c2492 100644 --- a/projects/ngx-datatable/src/lib/components/body/body-row.component.ts +++ b/projects/ngx-datatable/src/lib/components/body/body-row.component.ts @@ -86,7 +86,7 @@ export class DataTableBodyRowComponent implements DoCheck, OnChanges @Input() expanded: boolean; @Input() rowClass?: (row: RowOrGroup) => string | Record; - @Input() row: RowOrGroup; + @Input() row: TRow; @Input() group: TRow[]; @Input() isSelected: boolean; @Input() rowIndex: number; diff --git a/projects/ngx-datatable/src/lib/components/body/body.component.ts b/projects/ngx-datatable/src/lib/components/body/body.component.ts index 9e0680391..1337d45ae 100644 --- a/projects/ngx-datatable/src/lib/components/body/body.component.ts +++ b/projects/ngx-datatable/src/lib/components/body/body.component.ts @@ -26,7 +26,11 @@ import { ColumnGroupWidth } from '../../types/internal.types'; import { DragEventData, Group, + GroupToggleEvent, + GroupToggleEvents, RowOrGroup, + RowToggleEvent, + RowToggleEvents, ScrollEvent, SelectionType, TreeStatus @@ -144,6 +148,7 @@ import { + this.toggleStateChange(type, value) - ); + this.listener = this.rowDetail.toggle.subscribe(event => this.toggleStateChange(event)); } if (this.groupHeader) { - this.listener = this.groupHeader.toggle.subscribe( - ({ type, value }: { type: string; value: any }) => { - // Remove default expansion state once user starts manual toggle. - this.groupExpansionDefault = false; - this.toggleStateChange(type, value); - } - ); + this.listener = this.groupHeader.toggle.subscribe(event => { + // Remove default expansion state once user starts manual toggle. + this.groupExpansionDefault = false; + this.toggleStateChange(event); + }); } } - private toggleStateChange(type: string, value: any) { - if (type === 'group' || type === 'row') { - this.toggleRowExpansion(value); + private toggleStateChange(event: RowToggleEvents | GroupToggleEvents) { + if (event.type === 'group' || event.type === 'row') { + this.toggleRowExpansion(event.value); } - if (type === 'all') { - this.toggleAllRows(value); + if (event.type === 'all') { + this.toggleAllRows(event.value); } // Refresh rows after toggle @@ -635,7 +636,7 @@ export class DataTableBodyComponent { + getDetailRowHeight = (row?: RowOrGroup, index?: number): number => { if (!this.rowDetail) { return 0; } @@ -827,7 +828,7 @@ export class DataTableBodyComponent): void { // Capture the row index of the first row that is visible on the viewport. const viewPortFirstRowIndex = this.getAdjustedViewPortIndex(); const rowExpandedIdx = this.getRowExpandedIdx(row, this.rowExpansions); diff --git a/projects/ngx-datatable/src/lib/components/datatable.component.ts b/projects/ngx-datatable/src/lib/components/datatable.component.ts index 951ef9291..79a429330 100644 --- a/projects/ngx-datatable/src/lib/components/datatable.component.ts +++ b/projects/ngx-datatable/src/lib/components/datatable.component.ts @@ -639,7 +639,7 @@ export class DatatableComponent * if described in your markup. */ @ContentChildren(DataTableColumnDirective) - columnTemplates!: QueryList; + columnTemplates!: QueryList; /** * Row Detail templates gathered from the ContentChild diff --git a/projects/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts b/projects/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts index 2a5907c3b..d0a66a366 100644 --- a/projects/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts +++ b/projects/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts @@ -4,11 +4,11 @@ import { RowDetailContext } from '../../types/public.types'; @Directive({ selector: '[ngx-datatable-row-detail-template]' }) -export class DatatableRowDetailTemplateDirective { - static ngTemplateContextGuard( - directive: DatatableRowDetailTemplateDirective, +export class DatatableRowDetailTemplateDirective { + static ngTemplateContextGuard( + directive: DatatableRowDetailTemplateDirective, context: unknown - ): context is RowDetailContext { + ): context is RowDetailContext { return true; } } diff --git a/projects/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts b/projects/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts index 00b980353..be068fa11 100644 --- a/projects/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts +++ b/projects/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts @@ -1,6 +1,6 @@ import { ContentChild, Directive, EventEmitter, Input, Output, TemplateRef } from '@angular/core'; import { DatatableRowDetailTemplateDirective } from './row-detail-template.directive'; -import { RowDetailContext } from '../../types/public.types'; +import { RowDetailContext, RowToggleEvents } from '../../types/public.types'; @Directive({ selector: 'ngx-datatable-row-detail' }) export class DatatableRowDetailDirective { @@ -23,7 +23,7 @@ export class DatatableRowDetailDirective { /** * Row detail row visbility was toggled. */ - @Output() toggle: EventEmitter = new EventEmitter(); + @Output() toggle: EventEmitter> = new EventEmitter(); /** * Toggle the expansion of the row diff --git a/projects/ngx-datatable/src/lib/types/public.types.ts b/projects/ngx-datatable/src/lib/types/public.types.ts index 0b5cef1e7..f5ce16dd9 100644 --- a/projects/ngx-datatable/src/lib/types/public.types.ts +++ b/projects/ngx-datatable/src/lib/types/public.types.ts @@ -54,16 +54,16 @@ export interface HeaderCellContext { selectFn: () => void; } -export interface GroupContext { - group: RowOrGroup; +export interface GroupContext { + group: Group; expanded: boolean; rowIndex: number; } -export interface CellContext { +export interface CellContext { onCheckboxChangeFn: (event: Event) => void; activateFn: (event: ActivateEvent) => void; - row: RowOrGroup; + row: TRow; group: TRow[]; value: any; column: TableColumn; @@ -100,8 +100,8 @@ export interface Group { /** Type for either a row or a group */ export type RowOrGroup = TRow | Group; -export interface RowDetailContext { - row: RowOrGroup; +export interface RowDetailContext { + row: TRow; expanded: boolean; rowIndex: number; disableRow$?: Observable; @@ -135,6 +135,25 @@ export interface ScrollEvent { offsetX: number; } +export interface GroupToggleEvent { + type: 'group'; + value: Group; +} + +export interface RowToggleEvent { + type: 'row'; + value: TRow; +} + +export interface AllToggleEvent { + type: 'all'; + value: boolean; +} + +export type GroupToggleEvents = GroupToggleEvent | AllToggleEvent; + +export type RowToggleEvents = RowToggleEvent | AllToggleEvent; + export enum SelectionType { single = 'single', multi = 'multi', diff --git a/src/app/basic/row-grouping.component.ts b/src/app/basic/row-grouping.component.ts index 165b43f53..c89c524b7 100644 --- a/src/app/basic/row-grouping.component.ts +++ b/src/app/basic/row-grouping.component.ts @@ -1,10 +1,12 @@ import { Component, ViewChild } from '@angular/core'; +import { GroupedEmployee } from '../data.model'; import { ColumnMode, DatatableComponent, + Group, + GroupToggleEvents, SelectionType } from 'projects/ngx-datatable/src/public-api'; -import { GroupedEmployee } from '../data.model'; @Component({ selector: 'row-grouping-demo', @@ -287,12 +289,12 @@ export class RowGroupingComponent { this.rows = [...this.rows]; } - toggleExpandGroup(group) { + toggleExpandGroup(group: Group) { console.log('Toggled Expand Group!', group); this.table.groupHeader.toggleExpandGroup(group); } - onDetailToggle(event) { + onDetailToggle(event: GroupToggleEvents) { console.log('Detail Toggled', event); } }