diff --git a/projects/angular/clarity.api.md b/projects/angular/clarity.api.md index b8074d944f..8719dc6ff9 100644 --- a/projects/angular/clarity.api.md +++ b/projects/angular/clarity.api.md @@ -1725,7 +1725,7 @@ export class ClrDatagridRow implements AfterContentInit, AfterViewInit // (undocumented) expandAnimation: ClrExpandableAnimation; // (undocumented) - expandAnimationTrigger: boolean; + expandAnimationTrigger: boolean | null; // (undocumented) get expanded(): boolean | string; set expanded(value: boolean | string); @@ -4972,10 +4972,14 @@ export class ÇlrClrPopoverOpenCloseButton implements OnDestroy { export class ÇlrDatagridCellRenderer implements OnDestroy { constructor(el: ElementRef, renderer: Renderer2, organizer: DatagridRenderOrganizer); // (undocumented) - set columnState(columnState: BehaviorSubject); - // (undocumented) ngOnDestroy(): void; // (undocumented) + resetState(state: ColumnState): void; + // (undocumented) + setHidden(state: ColumnState): void; + // (undocumented) + setWidth(state: ColumnState): void; + // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration<ÇlrDatagridCellRenderer, "clr-dg-cell", never, {}, {}, never, never, false, never>; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration<ÇlrDatagridCellRenderer, never>; @@ -5004,6 +5008,10 @@ export class ÇlrDatagridHeaderRenderer implements OnDestroy { // (undocumented) setColumnState(index: number): void; // (undocumented) + setHidden(state: ColumnState): void; + // (undocumented) + setWidth(state: ColumnState): void; + // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration<ÇlrDatagridHeaderRenderer, "clr-dg-column", never, {}, { "resizeEmitter": "clrDgColumnResize"; }, never, never, false, never>; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration<ÇlrDatagridHeaderRenderer, never>; @@ -5011,7 +5019,7 @@ export class ÇlrDatagridHeaderRenderer implements OnDestroy { // @public (undocumented) export class ÇlrDatagridMainRenderer implements AfterContentInit, AfterViewInit, AfterViewChecked, OnDestroy { - constructor(organizer: DatagridRenderOrganizer, items: Items, page: Page, domAdapter: DomAdapter, el: ElementRef, renderer: Renderer2, detailService: DetailService, tableSizeService: TableSizeService, columnsService: ColumnsService, ngZone: NgZone); + constructor(organizer: DatagridRenderOrganizer, items: Items, page: Page, domAdapter: DomAdapter, el: ElementRef, renderer: Renderer2, detailService: DetailService, tableSizeService: TableSizeService, columnsService: ColumnsService, ngZone: NgZone, keyNavigation: KeyNavigationGridController); // (undocumented) ngAfterContentInit(): void; // (undocumented) @@ -5032,11 +5040,13 @@ export class ÇlrDatagridMainRenderer implements AfterContentInit, AfterViewInit export class ÇlrDatagridRowRenderer implements AfterContentInit, OnDestroy { constructor(columnsService: ColumnsService); // (undocumented) + cells: QueryList<ÇlrDatagridCellRenderer>; + // (undocumented) ngAfterContentInit(): void; // (undocumented) ngOnDestroy(): void; // (undocumented) - setColumnState(): void; + setCellsState(): void; // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration<ÇlrDatagridRowRenderer, "clr-dg-row, clr-dg-row-detail", never, {}, {}, ["cells"], never, false, never>; // (undocumented) diff --git a/projects/angular/src/data/datagrid/_datagrid.clarity.scss b/projects/angular/src/data/datagrid/_datagrid.clarity.scss index 2361c8109d..f638d247a5 100644 --- a/projects/angular/src/data/datagrid/_datagrid.clarity.scss +++ b/projects/angular/src/data/datagrid/_datagrid.clarity.scss @@ -1044,6 +1044,10 @@ flex: 0 0 auto !important; min-width: 0 !important; display: block !important; + visibility: hidden !important; + position: absolute !important; + top: 0; + left: 0; } /** diff --git a/projects/angular/src/data/datagrid/datagrid-row.ts b/projects/angular/src/data/datagrid/datagrid-row.ts index 17aa46e8e9..eb45966728 100644 --- a/projects/angular/src/data/datagrid/datagrid-row.ts +++ b/projects/angular/src/data/datagrid/datagrid-row.ts @@ -67,7 +67,7 @@ export class ClrDatagridRow implements AfterContentInit, AfterViewInit expandableId: string; replaced: boolean; displayCells = false; - expandAnimationTrigger = false; + expandAnimationTrigger: boolean | null = null; /* reference to the enum so that template can access */ SELECTION_TYPE = SelectionType; diff --git a/projects/angular/src/data/datagrid/datagrid.ts b/projects/angular/src/data/datagrid/datagrid.ts index 175aec92d8..315bab6785 100644 --- a/projects/angular/src/data/datagrid/datagrid.ts +++ b/projects/angular/src/data/datagrid/datagrid.ts @@ -303,8 +303,6 @@ export class ClrDatagrid implements AfterContentInit, AfterViewInit, On this.datagridTable.nativeElement.focus(); } }), - // Reinitialize arrow key navigation on hide/unhide columns - combineLatest(this.columnsService.columns).subscribe(() => this.keyNavigation?.resetKeyGrid()), // A subscription that listens for displayMode changes on the datagrid this.displayMode.view.subscribe(viewChange => { // Remove any projected columns from the projectedDisplayColumns container diff --git a/projects/angular/src/data/datagrid/interfaces/column-state.interface.ts b/projects/angular/src/data/datagrid/interfaces/column-state.interface.ts index d13f00e822..c69989c057 100644 --- a/projects/angular/src/data/datagrid/interfaces/column-state.interface.ts +++ b/projects/angular/src/data/datagrid/interfaces/column-state.interface.ts @@ -9,6 +9,7 @@ import { TemplateRef } from '@angular/core'; import { DatagridColumnChanges } from '../enums/column-changes.enum'; export interface ColumnState { + columnIndex?: number; changes?: DatagridColumnChanges[]; // This is an array of change types to update width?: number; // This is the width calculated for the column strictWidth?: number; // This is the strict width if defined in styles/css diff --git a/projects/angular/src/data/datagrid/render/cell-renderer.spec.ts b/projects/angular/src/data/datagrid/render/cell-renderer.spec.ts index e8e17bef3a..86f19f3e94 100644 --- a/projects/angular/src/data/datagrid/render/cell-renderer.spec.ts +++ b/projects/angular/src/data/datagrid/render/cell-renderer.spec.ts @@ -5,12 +5,10 @@ */ import { Component } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; import { DatagridColumnChanges } from '../enums/column-changes.enum'; import { DatagridRenderStep } from '../enums/render-step.enum'; import { TestContext } from '../helpers.spec'; -import { ColumnState } from '../interfaces/column-state.interface'; import { DatagridCellRenderer } from './cell-renderer'; import { HIDDEN_COLUMN_CLASS, STRICT_WIDTH_CLASS } from './constants'; import { DatagridRenderOrganizer } from './render-organizer'; @@ -25,38 +23,35 @@ export default function (): void { describe('DatagridCellRenderer directive', function () { let context: TestContext; let organizer: MockDatagridRenderOrganizer; - let stateSub: BehaviorSubject; beforeEach(function () { context = this.create(DatagridCellRenderer, SimpleTest, [MOCK_ORGANIZER_PROVIDER]); organizer = context.getClarityProvider(DatagridRenderOrganizer) as MockDatagridRenderOrganizer; - stateSub = new BehaviorSubject({}); - context.clarityDirective.columnState = stateSub; }); it('sets proper width and class for strict width cells', function () { - stateSub.next({ changes: [DatagridColumnChanges.WIDTH], width: 42, strictWidth: 42 }); + context.clarityDirective.setWidth({ changes: [DatagridColumnChanges.WIDTH], width: 42, strictWidth: 42 }); expect(context.clarityElement.style.width).toBe('42px'); expect(context.clarityElement.classList).toContain(STRICT_WIDTH_CLASS); }); it('sets proper hidden class for hidden cell', function () { - stateSub.next({ changes: [DatagridColumnChanges.HIDDEN], hidden: true }); + context.clarityDirective.setHidden({ changes: [DatagridColumnChanges.HIDDEN], hidden: true }); expect(context.clarityElement.classList).toContain(HIDDEN_COLUMN_CLASS); - stateSub.next({ changes: [DatagridColumnChanges.HIDDEN], hidden: false }); + context.clarityDirective.setHidden({ changes: [DatagridColumnChanges.HIDDEN], hidden: false }); expect(context.clarityElement.classList).not.toContain(HIDDEN_COLUMN_CLASS); }); it('makes the cell non-flexible if and only if the width is strict', function () { - stateSub.next({ changes: [DatagridColumnChanges.WIDTH], width: 42, strictWidth: 42 }); + context.clarityDirective.setWidth({ changes: [DatagridColumnChanges.WIDTH], width: 42, strictWidth: 42 }); expect(context.clarityElement.style.width).toBe('42px'); expect(context.clarityElement.classList).toContain(STRICT_WIDTH_CLASS); - stateSub.next({ changes: [DatagridColumnChanges.WIDTH], width: 42, strictWidth: 0 }); + context.clarityDirective.setWidth({ changes: [DatagridColumnChanges.WIDTH], width: 42, strictWidth: 0 }); expect(context.clarityElement.classList).not.toContain(STRICT_WIDTH_CLASS); }); it('resets the cell to default width when notified', function () { - stateSub.next({ changes: [DatagridColumnChanges.WIDTH], width: 42, strictWidth: 42 }); + context.clarityDirective.setWidth({ changes: [DatagridColumnChanges.WIDTH], width: 42, strictWidth: 42 }); expect(context.clarityElement.style.width).toBe('42px'); expect(context.clarityElement.classList).toContain(STRICT_WIDTH_CLASS); organizer.updateRenderStep.next(DatagridRenderStep.CLEAR_WIDTHS); @@ -68,23 +63,21 @@ export default function (): void { expect(context.clarityElement.style.width).toBe(''); expect(context.clarityElement.classList).not.toContain(STRICT_WIDTH_CLASS); expect(context.clarityElement.classList).not.toContain(HIDDEN_COLUMN_CLASS); - stateSub.next({ + context.clarityDirective.resetState({ changes: [DatagridColumnChanges.WIDTH, DatagridColumnChanges.HIDDEN], width: 84, strictWidth: 0, hidden: true, }); - context.clarityDirective.columnState = stateSub; expect(context.clarityElement.style.width).toBe('84px'); expect(context.clarityElement.classList).not.toContain(STRICT_WIDTH_CLASS); expect(context.clarityElement.classList).toContain(HIDDEN_COLUMN_CLASS); - stateSub.next({ + context.clarityDirective.resetState({ changes: [DatagridColumnChanges.HIDDEN, DatagridColumnChanges.WIDTH], width: 42, strictWidth: 42, hidden: false, }); - context.clarityDirective.columnState = stateSub; expect(context.clarityElement.style.width).toBe('42px'); expect(context.clarityElement.classList).toContain(STRICT_WIDTH_CLASS); expect(context.clarityElement.classList).not.toContain(HIDDEN_COLUMN_CLASS); diff --git a/projects/angular/src/data/datagrid/render/cell-renderer.ts b/projects/angular/src/data/datagrid/render/cell-renderer.ts index d850d9d0fd..adf18c2acf 100644 --- a/projects/angular/src/data/datagrid/render/cell-renderer.ts +++ b/projects/angular/src/data/datagrid/render/cell-renderer.ts @@ -5,9 +5,9 @@ */ import { Directive, ElementRef, OnDestroy, Renderer2 } from '@angular/core'; -import { BehaviorSubject, Subscription } from 'rxjs'; +import { Subscription } from 'rxjs'; -import { ALL_COLUMN_CHANGES, DatagridColumnChanges } from '../enums/column-changes.enum'; +import { ALL_COLUMN_CHANGES } from '../enums/column-changes.enum'; import { DatagridRenderStep } from '../enums/render-step.enum'; import { ColumnState } from '../interfaces/column-state.interface'; import { HIDDEN_COLUMN_CLASS, STRICT_WIDTH_CLASS } from './constants'; @@ -17,7 +17,6 @@ import { DatagridRenderOrganizer } from './render-organizer'; selector: 'clr-dg-cell', }) export class DatagridCellRenderer implements OnDestroy { - private runAllChanges: DatagridColumnChanges[]; private stateSubscription: Subscription; private subscriptions: Subscription[] = []; @@ -27,16 +26,6 @@ export class DatagridCellRenderer implements OnDestroy { ); } - // @TODO(JEREMY) Work out how to dedupe some of this code between header and cell renderers - set columnState(columnState: BehaviorSubject) { - if (this.stateSubscription) { - this.stateSubscription.unsubscribe(); - } - - this.runAllChanges = ALL_COLUMN_CHANGES; - this.stateSubscription = columnState.subscribe(state => this.stateChanges(state)); - } - ngOnDestroy() { this.subscriptions.forEach(sub => sub.unsubscribe()); if (this.stateSubscription) { @@ -44,33 +33,13 @@ export class DatagridCellRenderer implements OnDestroy { } } - private stateChanges(state: ColumnState) { - if (this.runAllChanges) { - state.changes = this.runAllChanges; - delete this.runAllChanges; - } - if (state.changes && state.changes.length) { - state.changes.forEach(change => { - switch (change) { - case DatagridColumnChanges.WIDTH: - this.setWidth(state); - break; - case DatagridColumnChanges.HIDDEN: - this.setHidden(state); - break; - default: - break; - } - }); - } - } - - private clearWidth() { - this.renderer.removeClass(this.el.nativeElement, STRICT_WIDTH_CLASS); - this.renderer.setStyle(this.el.nativeElement, 'width', null); + resetState(state: ColumnState) { + state.changes = ALL_COLUMN_CHANGES; + this.setWidth(state); + this.setHidden(state); } - private setWidth(state: ColumnState) { + setWidth(state: ColumnState) { if (state.strictWidth) { this.renderer.addClass(this.el.nativeElement, STRICT_WIDTH_CLASS); } else { @@ -79,11 +48,16 @@ export class DatagridCellRenderer implements OnDestroy { this.renderer.setStyle(this.el.nativeElement, 'width', state.width + 'px'); } - private setHidden(state: ColumnState) { + setHidden(state: ColumnState) { if (state.hidden) { this.renderer.addClass(this.el.nativeElement, HIDDEN_COLUMN_CLASS); } else { this.renderer.removeClass(this.el.nativeElement, HIDDEN_COLUMN_CLASS); } } + + private clearWidth() { + this.renderer.removeClass(this.el.nativeElement, STRICT_WIDTH_CLASS); + this.renderer.setStyle(this.el.nativeElement, 'width', null); + } } diff --git a/projects/angular/src/data/datagrid/render/header-renderer.spec.ts b/projects/angular/src/data/datagrid/render/header-renderer.spec.ts index f40c68d6d1..732c073b97 100644 --- a/projects/angular/src/data/datagrid/render/header-renderer.spec.ts +++ b/projects/angular/src/data/datagrid/render/header-renderer.spec.ts @@ -101,12 +101,12 @@ export default function (): void { }); it('can set the width of a column', function () { - stateSub.next({ changes: [DatagridColumnChanges.WIDTH], width: 123 }); + context.clarityDirective.setWidth({ changes: [DatagridColumnChanges.WIDTH], width: 123 }); expect(context.clarityElement.style.width).toBe('123px'); }); it('resets the header to default width when notified', function () { - stateSub.next({ changes: [DatagridColumnChanges.WIDTH], width: 123 }); + context.clarityDirective.setWidth({ changes: [DatagridColumnChanges.WIDTH], width: 123 }); expect(context.clarityElement.style.width).toBe('123px'); organizer.updateRenderStep.next(DatagridRenderStep.CLEAR_WIDTHS); expect(context.clarityElement.style.width).toBeFalsy(); @@ -128,19 +128,19 @@ export default function (): void { it('does not set the width when the user declared a strict one', function () { domAdapter._scrollWidth = 123; - stateSub.next({ changes: [DatagridColumnChanges.WIDTH], width: 123, strictWidth: 24 }); + context.clarityDirective.setWidth({ changes: [DatagridColumnChanges.WIDTH], width: 123, strictWidth: 24 }); expect(context.clarityElement.classList).toContain(STRICT_WIDTH_CLASS); expect(context.clarityElement.style.width).toBeFalsy(); - stateSub.next({ changes: [DatagridColumnChanges.WIDTH], width: 123, strictWidth: 0 }); + context.clarityDirective.setWidth({ changes: [DatagridColumnChanges.WIDTH], width: 123, strictWidth: 0 }); expect(context.clarityElement.style.width).toBe('123px'); expect(context.clarityElement.classList).not.toContain(STRICT_WIDTH_CLASS); }); it('sets proper hidden class for hidden cell', function () { - stateSub.next({ changes: [DatagridColumnChanges.HIDDEN], hidden: true }); + context.clarityDirective.setHidden({ changes: [DatagridColumnChanges.HIDDEN], hidden: true }); expect(context.clarityElement.classList).toContain(HIDDEN_COLUMN_CLASS); - stateSub.next({ changes: [DatagridColumnChanges.HIDDEN], hidden: false }); + context.clarityDirective.setHidden({ changes: [DatagridColumnChanges.HIDDEN], hidden: false }); expect(context.clarityElement.classList).not.toContain(HIDDEN_COLUMN_CLASS); }); }); diff --git a/projects/angular/src/data/datagrid/render/header-renderer.ts b/projects/angular/src/data/datagrid/render/header-renderer.ts index 53d50235d5..dfcb55a020 100644 --- a/projects/angular/src/data/datagrid/render/header-renderer.ts +++ b/projects/angular/src/data/datagrid/render/header-renderer.ts @@ -8,7 +8,6 @@ import { Directive, ElementRef, EventEmitter, Inject, OnDestroy, Output, Rendere import { BehaviorSubject, Subscription } from 'rxjs'; import { DomAdapter } from '../../../utils/dom-adapter/dom-adapter'; -import { DatagridColumnChanges } from '../enums/column-changes.enum'; import { DatagridRenderStep } from '../enums/render-step.enum'; import { ColumnState } from '../interfaces/column-state.interface'; import { ColumnResizerService } from '../providers/column-resizer.service'; @@ -43,8 +42,6 @@ export class DatagridHeaderRenderer implements OnDestroy { this.subscriptions.push( this.organizer.filterRenderSteps(DatagridRenderStep.CLEAR_WIDTHS).subscribe(() => this.clearWidth()) ); - - this.subscriptions.push(columnState.subscribe(state => this.stateChanges(state))); } ngOnDestroy() { @@ -63,20 +60,29 @@ export class DatagridHeaderRenderer implements OnDestroy { this.columnsService.columns[index] = this.columnState; } - private stateChanges(state: ColumnState) { - if (state.changes && state.changes.length) { - state.changes.forEach(change => { - switch (change) { - case DatagridColumnChanges.WIDTH: - this.setWidth(state); - break; - case DatagridColumnChanges.HIDDEN: - this.setHidden(state); - break; - default: - break; - } - }); + setWidth(state: ColumnState) { + if (state.strictWidth) { + if (this.columnResizerService.resizedBy) { + this.resizeEmitter.emit(state.width); + this.renderer.setStyle(this.el.nativeElement, 'width', state.width + 'px'); + this.widthSet = false; + } + // Don't set width if there is a user-defined one. Just add the strict width class. + this.renderer.addClass(this.el.nativeElement, STRICT_WIDTH_CLASS); + this.autoSet = false; + } else { + this.renderer.removeClass(this.el.nativeElement, STRICT_WIDTH_CLASS); + this.renderer.setStyle(this.el.nativeElement, 'width', state.width + 'px'); + this.widthSet = true; + this.autoSet = true; + } + } + + setHidden(state: ColumnState) { + if (state.hidden) { + this.renderer.addClass(this.el.nativeElement, HIDDEN_COLUMN_CLASS); + } else { + this.renderer.removeClass(this.el.nativeElement, HIDDEN_COLUMN_CLASS); } } @@ -107,30 +113,4 @@ export class DatagridHeaderRenderer implements OnDestroy { } return width; } - - private setWidth(state: ColumnState) { - if (state.strictWidth) { - if (this.columnResizerService.resizedBy) { - this.resizeEmitter.emit(state.width); - this.renderer.setStyle(this.el.nativeElement, 'width', state.width + 'px'); - this.widthSet = false; - } - // Don't set width if there is a user-defined one. Just add the strict width class. - this.renderer.addClass(this.el.nativeElement, STRICT_WIDTH_CLASS); - this.autoSet = false; - } else { - this.renderer.removeClass(this.el.nativeElement, STRICT_WIDTH_CLASS); - this.renderer.setStyle(this.el.nativeElement, 'width', state.width + 'px'); - this.widthSet = true; - this.autoSet = true; - } - } - - private setHidden(state: ColumnState) { - if (state.hidden) { - this.renderer.addClass(this.el.nativeElement, HIDDEN_COLUMN_CLASS); - } else { - this.renderer.removeClass(this.el.nativeElement, HIDDEN_COLUMN_CLASS); - } - } } diff --git a/projects/angular/src/data/datagrid/render/main-renderer.spec.ts b/projects/angular/src/data/datagrid/render/main-renderer.spec.ts index 40a13540c4..d30d9ef3fa 100644 --- a/projects/angular/src/data/datagrid/render/main-renderer.spec.ts +++ b/projects/angular/src/data/datagrid/render/main-renderer.spec.ts @@ -101,7 +101,7 @@ export default function (): void { beforeEach(function () { resizeSpy = spyOn(DatagridRenderOrganizer.prototype, 'resize'); - rowsSpy = spyOn(DatagridRowRenderer.prototype, 'setColumnState'); + rowsSpy = spyOn(DatagridRowRenderer.prototype, 'setCellsState'); context = this.create(DatagridMainRenderer, DynamicTest); }); diff --git a/projects/angular/src/data/datagrid/render/main-renderer.ts b/projects/angular/src/data/datagrid/render/main-renderer.ts index 47fae71271..44e0b8cca5 100644 --- a/projects/angular/src/data/datagrid/render/main-renderer.ts +++ b/projects/angular/src/data/datagrid/render/main-renderer.ts @@ -18,7 +18,7 @@ import { QueryList, Renderer2, } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { merge, Subscription } from 'rxjs'; import { DomAdapter } from '../../../utils/dom-adapter/dom-adapter'; import { DatagridColumnChanges } from '../enums/column-changes.enum'; @@ -29,6 +29,7 @@ import { DetailService } from '../providers/detail.service'; import { Items } from '../providers/items'; import { Page } from '../providers/page'; import { TableSizeService } from '../providers/table-size.service'; +import { KeyNavigationGridController } from '../utils/key-navigation-grid.controller'; import { DatagridHeaderRenderer } from './header-renderer'; import { NoopDomAdapter } from './noop-dom-adapter'; import { DatagridRenderOrganizer } from './render-organizer'; @@ -75,7 +76,8 @@ export class DatagridMainRenderer implements AfterContentInit, AfterViewInit, Af private detailService: DetailService, private tableSizeService: TableSizeService, private columnsService: ColumnsService, - private ngZone: NgZone + private ngZone: NgZone, + private keyNavigation: KeyNavigationGridController ) { this.subscriptions.push( this.organizer @@ -106,6 +108,7 @@ export class DatagridMainRenderer implements AfterContentInit, AfterViewInit, Af this.stabilizeColumns(); }) ); + this.listenForColumnChanges(); } // Initialize and set Table width for horizontal scrolling here. @@ -151,7 +154,7 @@ export class DatagridMainRenderer implements AfterContentInit, AfterViewInit, Af private setupColumns() { this.headers.forEach((header, index) => header.setColumnState(index)); this.columnsService.columns.splice(this.headers.length); // Trim any old columns - this.rows.forEach(row => row.setColumnState()); + this.rows.forEach(row => row.setCellsState()); } private shouldComputeHeight(): boolean { @@ -174,9 +177,8 @@ export class DatagridMainRenderer implements AfterContentInit, AfterViewInit, Af * Refer: http://stackoverflow.com/questions/24396205/flex-grow-not-working-in-internet-explorer-11-0 */ private computeDatagridHeight() { - // IE doesn't return correct value for getComputedStyle(element).getPropertyValue("height") - const value: number = this.domAdapter.clientRect(this.el.nativeElement).height; - this.renderer.setStyle(this.el.nativeElement, 'height', value + 'px'); + const height = window.getComputedStyle(this.el.nativeElement).height; + this.renderer.setStyle(this.el.nativeElement, 'height', height); this._heightSet = true; } @@ -190,6 +192,9 @@ export class DatagridMainRenderer implements AfterContentInit, AfterViewInit, Af */ private computeHeadersWidth() { const nbColumns: number = this.headers.length; + const headerWidths = this.headers.map(header => { + return header.getColumnWidthState(); + }); let allStrict = true; this.headers.forEach((header, index) => { // On the last header column check whether all columns have strict widths. @@ -198,7 +203,7 @@ export class DatagridMainRenderer implements AfterContentInit, AfterViewInit, Af // gap in the Datagrid. const state: ColumnStateDiff = { changes: [DatagridColumnChanges.WIDTH], - ...header.getColumnWidthState(), + ...headerWidths[index], }; if (!state.strictWidth) { @@ -213,6 +218,47 @@ export class DatagridMainRenderer implements AfterContentInit, AfterViewInit, Af }); } + private columnStateChanged(state) { + const columnIndex = state.columnIndex; + if (state.changes && state.changes.length) { + state.changes.forEach(change => { + switch (change) { + case DatagridColumnChanges.WIDTH: + this.headers.get(columnIndex).setWidth(state); + this.rows.forEach(row => { + if (row.cells && row.cells.length) { + row.cells.get(columnIndex).setWidth(state); + } + }); + break; + case DatagridColumnChanges.HIDDEN: + this.headers.get(columnIndex).setHidden(state); + this.rows.forEach(row => { + if (row.cells && row.cells.length) { + row.cells.get(columnIndex).setHidden(state); + } + }); + this.keyNavigation.resetKeyGrid(); + break; + default: + break; + } + }); + } + } + + private listenForColumnChanges() { + this.columnsService.columns.forEach((column, index) => { + this.columnsService.emitStateChange(column, { changes: [], columnIndex: index } as ColumnStateDiff); + }); + /* + Merges all column subject so we can track them at once + and receive only the changed column as result + */ + const columnChanges = merge(...this.columnsService.columns); + this.subscriptions.push(columnChanges.subscribe(change => this.columnStateChanged(change))); + } + /** * Triggers a whole re-rendring cycle to set column sizes, if needed. */ diff --git a/projects/angular/src/data/datagrid/render/render-organizer.ts b/projects/angular/src/data/datagrid/render/render-organizer.ts index 5e4bdb1b56..117071cd01 100644 --- a/projects/angular/src/data/datagrid/render/render-organizer.ts +++ b/projects/angular/src/data/datagrid/render/render-organizer.ts @@ -30,7 +30,7 @@ export class DatagridRenderOrganizer { this._renderStep.next(DatagridRenderStep.CLEAR_WIDTHS); } this._renderStep.next(DatagridRenderStep.COMPUTE_COLUMN_WIDTHS); - this._renderStep.next(DatagridRenderStep.ALIGN_COLUMNS); + this._renderStep.next(DatagridRenderStep.ALIGN_COLUMNS); // NOT USED this.alreadySized = true; this._renderStep.next(DatagridRenderStep.CALCULATE_MODE_OFF); } diff --git a/projects/angular/src/data/datagrid/render/row-renderer.spec.ts b/projects/angular/src/data/datagrid/render/row-renderer.spec.ts index cc571c9de8..48cfb65076 100644 --- a/projects/angular/src/data/datagrid/render/row-renderer.spec.ts +++ b/projects/angular/src/data/datagrid/render/row-renderer.spec.ts @@ -41,8 +41,8 @@ export default function (): void { columnsService.columns[0] = new BehaviorSubject({ width: 41 }); columnsService.columns[1] = new BehaviorSubject({ width: 23 }); - columnStateSpy = spyOnProperty(DatagridCellRenderer.prototype, 'columnState', 'set').and.callThrough(); - context.clarityDirective.setColumnState(); + columnStateSpy = spyOn(DatagridCellRenderer.prototype, 'resetState').and.callThrough(); + context.clarityDirective.setCellsState(); context.detectChanges(); }); @@ -61,7 +61,7 @@ export default function (): void { it('sets the widths of the cells when created after the widths have been computed', function () { columnsService.columns[0].next({ width: 42, strictWidth: 0, changes: [DatagridColumnChanges.WIDTH] }); columnsService.columns[1].next({ width: 24, strictWidth: 24, changes: [DatagridColumnChanges.WIDTH] }); - context.detectChanges(); + context.clarityDirective.setCellsState(); cells = context.fixture.debugElement.queryAll(By.directive(DatagridCellRenderer)); expect(cells.length).toBe(2); expect(cells[0].nativeElement.style.width).toEqual('42px'); diff --git a/projects/angular/src/data/datagrid/render/row-renderer.ts b/projects/angular/src/data/datagrid/render/row-renderer.ts index 856776c605..fb16ed4304 100644 --- a/projects/angular/src/data/datagrid/render/row-renderer.ts +++ b/projects/angular/src/data/datagrid/render/row-renderer.ts @@ -14,17 +14,17 @@ import { DatagridCellRenderer } from './cell-renderer'; selector: 'clr-dg-row, clr-dg-row-detail', }) export class DatagridRowRenderer implements AfterContentInit, OnDestroy { - @ContentChildren(DatagridCellRenderer) private cells: QueryList; + @ContentChildren(DatagridCellRenderer) cells: QueryList; private subscriptions: Subscription[] = []; constructor(private columnsService: ColumnsService) {} ngAfterContentInit() { - this.setColumnState(); // case #3 and #4 + this.setCellsState(); // case #3 and #4 this.subscriptions.push( this.cells.changes.subscribe(() => { - this.setColumnState(); // case #2 + this.setCellsState(); // case #2 // Note on case #2: In the case of dynamic columns, when one column (header/cell together) gets deleted, // this.cells.changes emits before this.columnsService.columns gets updated in MainRenderer // when this.headers.changes emits as well. So that means there will be n+1 column state providers @@ -39,7 +39,7 @@ export class DatagridRowRenderer implements AfterContentInit, OnDestroy { this.subscriptions.forEach(sub => sub.unsubscribe()); } - setColumnState() { + setCellsState() { // This method runs in four cases: // 1. When the initial rows appear on the first page. // In this case, the method will be called in DatagridMainRenderer. @@ -52,7 +52,7 @@ export class DatagridRowRenderer implements AfterContentInit, OnDestroy { if (this.cells.length === this.columnsService.columns.length) { this.cells.forEach((cell, index) => { if (this.columnsService.columns[index]) { - cell.columnState = this.columnsService.columns[index]; + cell.resetState(this.columnsService.columns[index].value); } }); } diff --git a/projects/angular/src/utils/animations/expandable-animation/expandable-animation.ts b/projects/angular/src/utils/animations/expandable-animation/expandable-animation.ts index b47c7570ca..3d40f7ce94 100644 --- a/projects/angular/src/utils/animations/expandable-animation/expandable-animation.ts +++ b/projects/angular/src/utils/animations/expandable-animation/expandable-animation.ts @@ -43,16 +43,20 @@ export class ClrExpandableAnimation { @HostListener('@expandAnimation.start') animationStart() { - this.renderer.setStyle(this.element.nativeElement, 'overflow', 'hidden'); + if (this.clrExpandTrigger !== null) { + this.renderer.setStyle(this.element.nativeElement, 'overflow', 'hidden'); + } } @HostListener('@expandAnimation.done') animationDone() { - this.renderer.removeStyle(this.element.nativeElement, 'overflow'); + if (this.clrExpandTrigger !== null) { + this.renderer.removeStyle(this.element.nativeElement, 'overflow'); - // A "safe" auto-update of the height ensuring basic OOTB user experience . - // Prone to small jumps in initial animation height if data was changed in the meantime, window was resized, etc. - // For optimal behavior call manually updateStartHeight() from the parent component before initiating the update. - this.updateStartHeight(); + // A "safe" auto-update of the height ensuring basic OOTB user experience . + // Prone to small jumps in initial animation height if data was changed in the meantime, window was resized, etc. + // For optimal behavior call manually updateStartHeight() from the parent component before initiating the update. + this.updateStartHeight(); + } } updateStartHeight() { diff --git a/projects/angular/src/utils/dom-adapter/dom-adapter.ts b/projects/angular/src/utils/dom-adapter/dom-adapter.ts index 6a4c9439ab..ecec83b140 100644 --- a/projects/angular/src/utils/dom-adapter/dom-adapter.ts +++ b/projects/angular/src/utils/dom-adapter/dom-adapter.ts @@ -14,10 +14,16 @@ import { Injectable } from '@angular/core'; @Injectable() export class DomAdapter { + /* + We clone the element and take its measurements from outside the grid + so we don't trigger reflow for the whole datagrid. + */ userDefinedWidth(element: HTMLElement): number { - element.classList.add('datagrid-cell-width-zero'); - const userDefinedWidth = this.clientRect(element).width; - element.classList.remove('datagrid-cell-width-zero'); + const clonedElement = element.cloneNode(true) as HTMLElement; + clonedElement.classList.add('datagrid-cell-width-zero'); + document.body.appendChild(clonedElement); + const userDefinedWidth = this.clientRect(clonedElement).width; + clonedElement.remove(); return userDefinedWidth; }