diff --git a/libs/components/src/table/table-data-source.ts b/libs/components/src/table/table-data-source.ts index 36f5866763..6501c9bffb 100644 --- a/libs/components/src/table/table-data-source.ts +++ b/libs/components/src/table/table-data-source.ts @@ -74,7 +74,7 @@ export class TableDataSource extends DataSource { } } - connect(): Observable { + connect(): Observable { if (!this._renderChangesSubscription) { this.updateChangeSubscription(); } diff --git a/libs/components/src/table/table-scroll.component.html b/libs/components/src/table/table-scroll.component.html new file mode 100644 index 0000000000..26b06ee0e5 --- /dev/null +++ b/libs/components/src/table/table-scroll.component.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + +
+
diff --git a/libs/components/src/table/table-scroll.component.ts b/libs/components/src/table/table-scroll.component.ts new file mode 100644 index 0000000000..77647133bc --- /dev/null +++ b/libs/components/src/table/table-scroll.component.ts @@ -0,0 +1,92 @@ +import { + AfterContentChecked, + Component, + ContentChild, + Input, + OnDestroy, + TemplateRef, + Directive, + NgZone, + AfterViewInit, + ElementRef, + TrackByFunction, +} from "@angular/core"; + +import { TableComponent } from "./table.component"; + +/** + * Helper directive for defining the row template. + * + * ```html + * + * {{ row.id }} + * + * ``` + */ +@Directive({ + selector: "[bitRowDef]", + standalone: true, +}) +export class BitRowDef { + constructor(public template: TemplateRef) {} +} + +/** + * Scrollable table component. + * + * Utilizes virtual scrolling to render large datasets. + */ +@Component({ + selector: "bit-table-scroll", + templateUrl: "./table-scroll.component.html", + providers: [{ provide: TableComponent, useExisting: TableScrollComponent }], +}) +export class TableScrollComponent + extends TableComponent + implements AfterContentChecked, AfterViewInit, OnDestroy +{ + /** The size of the rows in the list (in pixels). */ + @Input({ required: true }) rowSize: number; + + /** Optional trackBy function. */ + @Input() trackBy: TrackByFunction | undefined; + + @ContentChild(BitRowDef) protected rowDef: BitRowDef; + + /** + * Height of the thead element (in pixels). + * + * Used to increase the table's total height to avoid items being cut off. + */ + protected headerHeight = 0; + + /** + * Observer for table header, applies padding on resize. + */ + private headerObserver: ResizeObserver; + + constructor( + private zone: NgZone, + private el: ElementRef, + ) { + super(); + } + + ngAfterViewInit(): void { + this.headerObserver = new ResizeObserver((entries) => { + this.zone.run(() => { + this.headerHeight = entries[0].contentRect.height; + }); + }); + + this.headerObserver.observe(this.el.nativeElement.querySelector("thead")); + } + + override ngOnDestroy(): void { + super.ngOnDestroy(); + + if (this.headerObserver) { + this.headerObserver.disconnect(); + } + } +} diff --git a/libs/components/src/table/table.component.html b/libs/components/src/table/table.component.html index 09b035826c..6615d0cb42 100644 --- a/libs/components/src/table/table.component.html +++ b/libs/components/src/table/table.component.html @@ -6,7 +6,7 @@ diff --git a/libs/components/src/table/table.component.ts b/libs/components/src/table/table.component.ts index b4d6d1931d..16f7a01b7c 100644 --- a/libs/components/src/table/table.component.ts +++ b/libs/components/src/table/table.component.ts @@ -30,7 +30,7 @@ export class TableComponent implements OnDestroy, AfterContentChecked { @ContentChild(TableBodyDirective) templateVariable: TableBodyDirective; - protected rows: Observable; + protected rows$: Observable; private _initialized = false; @@ -50,7 +50,7 @@ export class TableComponent implements OnDestroy, AfterContentChecked { this._initialized = true; const dataStream = this.dataSource.connect(); - this.rows = dataStream; + this.rows$ = dataStream; } } diff --git a/libs/components/src/table/table.mdx b/libs/components/src/table/table.mdx index f08691f764..3f28dd93b6 100644 --- a/libs/components/src/table/table.mdx +++ b/libs/components/src/table/table.mdx @@ -141,28 +141,28 @@ dataSource.filter = "search value"; ### Virtual Scrolling It's heavily adviced to use virtual scrolling if you expect the table to have any significant amount -of data. This is easily done by wrapping the table in the `cdk-virtual-scroll-viewport` component, -specify a `itemSize`, set `scrollWindow` to `true` and replace `*ngFor` with `*cdkVirtualFor`. +of data. This is done by using the `bit-table-scroll` component instead of the `bit-table` +component. This component behaves slightly different from the `bit-table` component. Instead of +using the `*ngFor` directive to render the rows, you provide a `bitRowDef` template that will be +used for rendering the rows. + +Due to limitations in the Angular Component Dev Kit you must provide an `rowSize` which corresponds +to the height of each row. If the height of the rows are not uniform, you should set an explicit row +height and align vertically. ```html - - - - - Id - Name - Other - - - - - {{ r.id }} - {{ r.name }} - {{ r.other }} - - - - + + + Id + Name + Other + + + {{ row.id }} + {{ row.name }} + {{ row.other }} + + ``` ## Accessibility diff --git a/libs/components/src/table/table.module.ts b/libs/components/src/table/table.module.ts index 753e4362e6..1f1b705c69 100644 --- a/libs/components/src/table/table.module.ts +++ b/libs/components/src/table/table.module.ts @@ -1,20 +1,31 @@ +import { ScrollingModule } from "@angular/cdk/scrolling"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { CellDirective } from "./cell.directive"; import { RowDirective } from "./row.directive"; import { SortableComponent } from "./sortable.component"; +import { BitRowDef, TableScrollComponent } from "./table-scroll.component"; import { TableBodyDirective, TableComponent } from "./table.component"; @NgModule({ - imports: [CommonModule], + imports: [CommonModule, ScrollingModule, BitRowDef], declarations: [ - TableComponent, CellDirective, RowDirective, SortableComponent, TableBodyDirective, + TableComponent, + TableScrollComponent, + ], + exports: [ + BitRowDef, + CellDirective, + RowDirective, + SortableComponent, + TableBodyDirective, + TableComponent, + TableScrollComponent, ], - exports: [TableComponent, CellDirective, RowDirective, SortableComponent, TableBodyDirective], }) export class TableModule {} diff --git a/libs/components/src/table/table.stories.ts b/libs/components/src/table/table.stories.ts index 8fa3f5559d..4ebc3045d1 100644 --- a/libs/components/src/table/table.stories.ts +++ b/libs/components/src/table/table.stories.ts @@ -1,4 +1,3 @@ -import { ScrollingModule } from "@angular/cdk/scrolling"; import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; import { countries } from "../form/countries"; @@ -10,7 +9,7 @@ export default { title: "Component Library/Table", decorators: [ moduleMetadata({ - imports: [TableModule, ScrollingModule], + imports: [TableModule], }), ], argTypes: { @@ -114,26 +113,21 @@ export const Scrollable: Story = { props: { dataSource: data2, sortFn: (a: any, b: any) => a.id - b.id, + trackBy: (index: number, item: any) => item.id, }, template: ` - - - - - Id - Name - Other - - - - - {{ r.id }} - {{ r.name }} - {{ r.other }} - - - - + + + Id + Name + Other + + + {{ row.id }} + {{ row.name }} + {{ row.other }} + + `, }), }; @@ -151,22 +145,16 @@ export const Filterable: Story = { }, template: ` - - - - - Name - Value - - - - - {{ r.name }} - {{ r.value }} - - - - + + + Name + Value + + + {{ row.name }} + {{ row.value }} + + `, }), };