mirror of
https://github.com/bitwarden/browser
synced 2026-02-13 06:54:07 +00:00
Merge branch 'main' into ps/extension-refresh
This commit is contained in:
@@ -74,7 +74,7 @@ export class TableDataSource<T> extends DataSource<T> {
|
||||
}
|
||||
}
|
||||
|
||||
connect(): Observable<readonly T[]> {
|
||||
connect(): Observable<T[]> {
|
||||
if (!this._renderChangesSubscription) {
|
||||
this.updateChangeSubscription();
|
||||
}
|
||||
|
||||
20
libs/components/src/table/table-scroll.component.html
Normal file
20
libs/components/src/table/table-scroll.component.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<cdk-virtual-scroll-viewport
|
||||
scrollWindow
|
||||
[itemSize]="rowSize"
|
||||
[ngStyle]="{ paddingBottom: headerHeight + 'px' }"
|
||||
>
|
||||
<table [ngClass]="tableClass">
|
||||
<thead
|
||||
class="tw-border-0 tw-border-b-2 tw-border-solid tw-border-secondary-300 tw-font-bold tw-text-muted"
|
||||
>
|
||||
<tr>
|
||||
<ng-content select="[header]"></ng-content>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *cdkVirtualFor="let r of rows$; trackBy: trackBy" bitRow>
|
||||
<ng-container *ngTemplateOutlet="rowDef.template; context: { $implicit: r }"></ng-container>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
92
libs/components/src/table/table-scroll.component.ts
Normal file
92
libs/components/src/table/table-scroll.component.ts
Normal file
@@ -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
|
||||
* <ng-template bitRowDef let-row>
|
||||
* <td bitCell>{{ row.id }}</td>
|
||||
* </ng-template>
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector: "[bitRowDef]",
|
||||
standalone: true,
|
||||
})
|
||||
export class BitRowDef {
|
||||
constructor(public template: TemplateRef<any>) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<any> | 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="templateVariable.template; context: { $implicit: rows }"
|
||||
*ngTemplateOutlet="templateVariable.template; context: { $implicit: rows$ }"
|
||||
></ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -30,7 +30,7 @@ export class TableComponent implements OnDestroy, AfterContentChecked {
|
||||
|
||||
@ContentChild(TableBodyDirective) templateVariable: TableBodyDirective;
|
||||
|
||||
protected rows: Observable<readonly any[]>;
|
||||
protected rows$: Observable<any[]>;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
<cdk-virtual-scroll-viewport scrollWindow itemSize="47">
|
||||
<bit-table [dataSource]="dataSource">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell bitSortable="id" default>Id</th>
|
||||
<th bitCell bitSortable="name">Name</th>
|
||||
<th bitCell bitSortable="other" [fn]="sortFn">Other</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template let-rows$>
|
||||
<tr bitRow *cdkVirtualFor="let r of rows$">
|
||||
<td bitCell>{{ r.id }}</td>
|
||||
<td bitCell>{{ r.name }}</td>
|
||||
<td bitCell>{{ r.other }}</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<bit-table-scroll [dataSource]="dataSource" rowSize="47">
|
||||
<ng-container header>
|
||||
<th bitCell bitSortable="id" default>Id</th>
|
||||
<th bitCell bitSortable="name">Name</th>
|
||||
<th bitCell bitSortable="other" [fn]="sortFn">Other</th>
|
||||
</ng-container>
|
||||
<ng-template bitRowDef let-row>
|
||||
<td bitCell>{{ row.id }}</td>
|
||||
<td bitCell>{{ row.name }}</td>
|
||||
<td bitCell>{{ row.other }}</td>
|
||||
</ng-template>
|
||||
</bit-table-scroll>
|
||||
```
|
||||
|
||||
## Accessibility
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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: `
|
||||
<cdk-virtual-scroll-viewport scrollWindow itemSize="47">
|
||||
<bit-table [dataSource]="dataSource">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell bitSortable="id" default>Id</th>
|
||||
<th bitCell bitSortable="name">Name</th>
|
||||
<th bitCell bitSortable="other" [fn]="sortFn">Other</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body let-rows$>
|
||||
<tr bitRow *cdkVirtualFor="let r of rows$">
|
||||
<td bitCell>{{ r.id }}</td>
|
||||
<td bitCell>{{ r.name }}</td>
|
||||
<td bitCell>{{ r.other }}</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<bit-table-scroll [dataSource]="dataSource" [rowSize]="43">
|
||||
<ng-container header>
|
||||
<th bitCell bitSortable="id" default>Id</th>
|
||||
<th bitCell bitSortable="name">Name</th>
|
||||
<th bitCell bitSortable="other" [fn]="sortFn">Other</th>
|
||||
</ng-container>
|
||||
<ng-template bitRowDef let-row>
|
||||
<td bitCell>{{ row.id }}</td>
|
||||
<td bitCell>{{ row.name }}</td>
|
||||
<td bitCell>{{ row.other }}</td>
|
||||
</ng-template>
|
||||
</bit-table-scroll>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -151,22 +145,16 @@ export const Filterable: Story = {
|
||||
},
|
||||
template: `
|
||||
<input type="search" placeholder="Search" (input)="dataSource.filter = $event.target.value" />
|
||||
<cdk-virtual-scroll-viewport scrollWindow itemSize="47">
|
||||
<bit-table [dataSource]="dataSource">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell bitSortable="name" default>Name</th>
|
||||
<th bitCell bitSortable="value" width="120px">Value</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body let-rows$>
|
||||
<tr bitRow *cdkVirtualFor="let r of rows$">
|
||||
<td bitCell>{{ r.name }}</td>
|
||||
<td bitCell>{{ r.value }}</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<bit-table-scroll [dataSource]="dataSource" [rowSize]="43">
|
||||
<ng-container header>
|
||||
<th bitCell bitSortable="name" default>Name</th>
|
||||
<th bitCell bitSortable="value" width="120px">Value</th>
|
||||
</ng-container>
|
||||
<ng-template bitRowDef let-row>
|
||||
<td bitCell>{{ row.name }}</td>
|
||||
<td bitCell>{{ row.value }}</td>
|
||||
</ng-template>
|
||||
</bit-table-scroll>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user