carbon-components-angular
Version:
Next generation components
469 lines (465 loc) • 17.3 kB
TypeScript
/**
*
* carbon-angular v0.0.0 | table.component.d.ts
*
* Copyright 2014, 2025 IBM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ApplicationRef, OnInit, EventEmitter, ElementRef, AfterViewInit, TemplateRef, OnDestroy } from "@angular/core";
import { Subscription, Observable } from "rxjs";
import { TableModel } from "./table-model.class";
import { TableItem } from "./table-item.class";
import { I18n, Overridable } from "carbon-components-angular/i18n";
import { DataGridInteractionModel } from "./data-grid-interaction-model.class";
import { TableRowSize } from "./table.types";
import * as i0 from "@angular/core";
/**
* Build your table with this component by extending things that differ from default.
*
* [See demo](../../?path=/story/components-table--basic)
*
* Instead of the usual write-your-own-html approach you had with `<table>`,
* carbon table uses model-view-controller approach.
*
* Here, you create a view (with built-in controller) and provide it a model.
* Changes you make to the model are reflected in the view. Provide same model you use
* in the table to the `<cds-pagination>` components.
* They provide a different view over the same data.
*
* ## Basic usage
*
* ```html
* <cds-table [model]="model"></cds-table>
* ```
*
* ```typescript
* public model = new TableModel();
*
* this.model.data = [
* [new TableItem({data: "asdf"}), new TableItem({data: "qwer"})],
* [new TableItem({data: "csdf"}), new TableItem({data: "zwer"})],
* [new TableItem({data: "bsdf"}), new TableItem({data: "swer"})],
* [new TableItem({data: "csdf"}), new TableItem({data: "twer"})]
* ];
* ```
*
* ## Customization
*
* If you have custom data in your table, you need a way to display it. You can do that
* by providing a template to `TableItem`.
*
* ```html
* <ng-template #customTableItemTemplate let-data="data">
* <a [routerLink]="data.link">{{data.name}} {{data.surname}}</a>
* </ng-template>
* ```
*
* ```typescript
* customTableItemTemplate: TemplateRef<any>;
*
* this.customModel.data = [
* [new TableItem({data: "asdf"}), new TableItem({data: {name: "Lessy", link: "/table"}, template: this.customTableItemTemplate})],
* [new TableItem({data: "csdf"}), new TableItem({data: "swer"})],
* [new TableItem({data: "bsdf"}), new TableItem({data: {name: "Alice", surname: "Bob"}, template: this.customTableItemTemplate})],
* [new TableItem({data: "csdf"}), new TableItem({data: "twer"})],
* ];
* ```
*
* ### Sorting and filtering
*
* In case you need custom sorting and/or filtering you should subclass `TableHeaderItem`
* and override needed functions.
*
* ```typescript
* class FilterableHeaderItem extends TableHeaderItem {
* // custom filter function
* filter(item: TableItem): boolean {
* if (typeof item.data === "string" && item.data.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0 ||
* item.data.name && item.data.name.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0 ||
* item.data.surname && item.data.surname.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0) {
* return false;
* }
* return true;
* }
*
* set filterCount(n) {}
* get filterCount() {
* return (this.filterData && this.filterData.data && this.filterData.data.length > 0) ? 1 : 0;
* }
*
* // used for custom sorting
* compare(one: TableItem, two: TableItem) {
* const stringOne = (one.data.name || one.data.surname || one.data).toLowerCase();
* const stringTwo = (two.data.name || two.data.surname || two.data).toLowerCase();
*
* if (stringOne > stringTwo) {
* return 1;
* } else if (stringOne < stringTwo) {
* return -1;
* } else {
* return 0;
* }
* }
* }
* ```
*
* If you want to do your sorting on the backend or query for sorted data as a result of user
* clicking the table header, check table [`sort`](#sort) output documentation
*
* See `TableHeaderItem` class for more information.
*
* ## No data template
*
* When table has no data to show, it can show a message you provide it instead.
*
* ```html
* <cds-table [model]="model">No data.</cds-table>
* ```
*
* ... will show `No data.` message, but you can get creative and provide any template you want
* to replace table's default `tbody`.
*
* ## Use pagination as table footer
*
* ```html
* <cds-pagination [model]="model" (selectPage)="selectPage($event)"></cds-pagination>
* ```
*
* `selectPage()` function should fetch the data from backend, create new `data`, apply it to `model.data`,
* and update `model.currentPage`.
*
* If the data your server returns is a two dimensional array of objects, it would look something like this:
*
* ```typescript
* selectPage(page) {
* this.getPage(page).then((data: Array<Array<any>>) => {
* // set the data and update page
* this.model.data = this.prepareData(data);
* this.model.currentPage = page;
* });
* }
*
* protected prepareData(data: Array<Array<any>>) {
* // create new data from the service data
* let newData = [];
* data.forEach(dataRow => {
* let row = [];
* dataRow.forEach(dataElement => {
* row.push(new TableItem({
* data: dataElement,
* template: typeof dataElement === "string" ? undefined : this.paginationTableItemTemplate
* // your template can handle all the data types so you don't have to conditionally set it
* // you can also set different templates for different columns based on index
* }));
* });
* newData.push(row);
* });
* return newData;
* }
* ```
*/
export declare class Table implements OnInit, AfterViewInit, OnDestroy {
protected elementRef: ElementRef;
protected applicationRef: ApplicationRef;
protected i18n: I18n;
/**
* Creates a skeleton model with a row and column count specified by the user
*
* Example:
*
* ```typescript
* this.model = Table.skeletonModel(5, 5);
* ```
*/
static skeletonModel(rowCount: number, columnCount: number): TableModel;
static setTabIndex(element: HTMLElement, index: -1 | 0): void;
static focus(element: HTMLElement): void;
/**
* Id of the table header title element
*/
ariaLabelledby: string;
/**
* Id of the table header description element
*/
ariaDescribedby: string;
/**
* `TableModel` with data the table is to display.
*/
set model(m: TableModel);
get model(): TableModel;
/**
* Size of the table rows.
*/
size: TableRowSize;
/**
* Set to `true` for a loading table.
*/
skeleton: boolean;
/**
* Set to `true` for a data grid with keyboard interactions.
*/
set isDataGrid(value: boolean);
/**
* Setting sortable to false will disable all headers including headers which are sortable. Is is
* possible to set the sortable state on the header item to disable/enable sorting for only some headers.
*/
sortable: boolean;
noBorder: boolean;
/**
* Set to `true` to show expansion toggle when table consists of row expansions
*/
showExpandAllToggle: boolean;
get isDataGrid(): boolean;
/**
* Controls whether to show the selection checkboxes column or not.
*/
showSelectionColumn: boolean;
/**
* Controls whether to enable multiple or single row selection.
*/
enableSingleSelect: boolean;
/**
* Distance (in px) from the bottom that view has to reach before
* `scrollLoad` event is emitted.
*/
scrollLoadDistance: number;
/**
* @todo - Enable column resize when Carbon officially supports feature
* Set to `true` to enable users to resize columns.
*
* Works for columns with width set in pixels.
*
*/
/**
* @todo - Enable columns drag & drop when Carbon officially supports feature
* Set to `true` to enable users to drag and drop columns.
*
* Changing the column order in table changes table model. Be aware of it when you add additional data
* to the model.
*
*/
set expandButtonAriaLabel(value: string | Observable<string>);
get expandButtonAriaLabel(): string | Observable<string>;
set sortDescendingLabel(value: string | Observable<string>);
get sortDescendingLabel(): string | Observable<string>;
set sortAscendingLabel(value: string | Observable<string>);
get sortAscendingLabel(): string | Observable<string>;
/**
* Expects an object that contains some or all of:
* ```
* {
* "FILTER": "Filter",
* "END_OF_DATA": "You've reached the end of your content",
* "SCROLL_TOP": "Scroll to top",
* "CHECKBOX_HEADER": "Select all rows",
* "CHECKBOX_ROW": "Select row"
* }
* ```
*/
set translations(value: any);
/**
* Set to `false` to remove table rows (zebra) stripes.
*/
striped: boolean;
/**
* Allows table content to scroll horizontally
*/
tableContent: boolean;
/**
* Set to `true` to stick the header to the top of the table
*/
stickyHeader: boolean;
/**
* Set footer template to customize what is displayed in the tfoot section of the table
*/
footerTemplate: TemplateRef<any>;
/**
* Used to populate the row selection checkbox label with a useful value if set.
*
* Example:
* ```
* <cds-table [selectionLabelColumn]="0"></cds-table>
* <!-- results in aria-label="Select first column value"
* (where "first column value" is the value of the first column in the row -->
* ```
*/
selectionLabelColumn: number;
/**
* Emits an index of the column that wants to be sorted.
*
* If no observers are provided (default), table will attempt to do a simple sort of the data loaded
* into the model.
*
* If an observer is provided, table will not attempt any sorting of its own and it is up to the observer
* to sort the table. This is what you typically want if you're using a backend query to get the sorted
* data or want to sort data across multiple pages.
*
* Usage:
*
* ```typescript
* @Component({
* selector: "app-table",
* template: `
* <cds-table
* [model]="model"
* (sort)="simpleSort($event)">
* No data.
* </cds-table>
* `
* })
* export class TableApp implements OnInit, OnChanges {
* @Input() model = new TableModel();
*
* ngOnInit() {
* this.model.header = [
* new TableHeaderItem({ data: "Name" }),
* new TableHeaderItem({ data: "hwer" })
* ];
*
* this.model.data = [
* [new TableItem({ data: "Name 1" }), new TableItem({ data: "qwer" })],
* [new TableItem({ data: "Name 3" }), new TableItem({ data: "zwer" })],
* [new TableItem({ data: "Name 2" }), new TableItem({ data: "swer" })],
* [new TableItem({ data: "Name 4" }), new TableItem({data: "twer"})],
* [new TableItem({ data: "Name 5" }), new TableItem({data: "twer"})],
* [new TableItem({ data: "Name 6" }), new TableItem({data: "twer"})]
* ];
* }
*
* simpleSort(index: number) {
* // this function does a simple sort, which is the default for the table and if that's
* // all you want, you don't need to do this.
*
* // here you can query your backend and update the model.data based on the result
* if (this.model.header[index].sorted) {
* // if already sorted flip sorting direction
* this.model.header[index].ascending = this.model.header[index].descending;
* }
* this.model.sort(index);
* }
* }
* ```
*/
sort: EventEmitter<number>;
/**
* Emits if all rows are selected.
*
* @param model
*/
selectAll: EventEmitter<Object>;
/**
* Emits if all rows are deselected.
*
* @param model
*/
deselectAll: EventEmitter<Object>;
/**
* Emits if a single row is selected.
*
* @param ({model: this.model, selectedRowIndex: index})
*/
selectRow: EventEmitter<Object>;
/**
* Emits if a single row is deselected.
*
* @param ({model: this.model, deselectedRowIndex: index})
*/
deselectRow: EventEmitter<Object>;
/**
* Emits if a row item excluding expandButtons, checkboxes, or radios is clicked.
*/
rowClick: EventEmitter<number>;
/**
* Emits when table requires more data to be loaded.
*/
scrollLoad: EventEmitter<TableModel>;
/**
* Controls if all checkboxes are viewed as selected.
*/
selectAllCheckbox: boolean;
/**
* Controls the indeterminate state of the header checkbox.
*/
selectAllCheckboxSomeSelected: boolean;
get noData(): boolean;
isColumnDragging: boolean;
columnDraggedHoverIndex: number;
columnDraggedPosition: string;
protected _model: TableModel;
protected _isDataGrid: boolean;
protected isViewReady: boolean;
protected subscriptions: Subscription;
protected positionSubscription: Subscription;
protected interactionModel: DataGridInteractionModel;
protected interactionPositionSubscription: Subscription;
protected _expandButtonAriaLabel: Overridable;
protected _sortDescendingLabel: Overridable;
protected _sortAscendingLabel: Overridable;
protected _checkboxHeaderLabel: Overridable;
protected _checkboxRowLabel: Overridable;
protected _endOfDataText: Overridable;
protected _scrollTopText: Overridable;
protected _filterTitle: Overridable;
protected columnResizeWidth: number;
protected columnResizeMouseX: number;
protected mouseMoveSubscription: Subscription;
protected mouseUpSubscription: Subscription;
/**
* Creates an instance of Table.
*/
constructor(elementRef: ElementRef, applicationRef: ApplicationRef, i18n: I18n);
ngOnInit(): void;
ngAfterViewInit(): void;
ngOnDestroy(): void;
enableDataGridInteractions(): void;
disableDataGridInteractions(): void;
onSelectAll(): void;
onDeselectAll(): void;
onSelectRow(event: any): void;
onRowClick(index: number): void;
updateSelectAllCheckbox(): void;
resetTabIndex(newTabIndex?: number): void;
columnResizeStart(event: any, column: any): void;
columnResizeProgress(event: any, column: any): void;
columnResizeEnd(event: any, column: any): void;
/**
* Triggered when the user scrolls on the `<tbody>` element.
* Emits the `scrollLoad` event.
*/
onScroll(event: any): void;
columnDragStart(event: any, columnIndex: any): void;
columnDragEnd(event: any, columnIndex: any): void;
columnDragEnter(event: any, position: any, columnIndex: any): void;
columnDragLeave(event: any, position: any, columnIndex: any): void;
columnDragover(event: any, position: any, columnIndex: any): void;
columnDrop(event: any, position: any, columnIndex: any): void;
doSort(index: number): void;
/**
* Triggered when the user scrolls on the `<tbody>` element.
* Emits the `scrollLoad` event.
*/
scrollToTop(event: any): void;
getSelectionLabelValue(row: TableItem[]): {
value: any;
};
getExpandButtonAriaLabel(): Observable<string>;
getSortDescendingLabel(): Observable<string>;
getSortAscendingLabel(): Observable<string>;
getCheckboxHeaderLabel(): Observable<string>;
getCheckboxRowLabel(): Observable<string>;
getEndOfDataText(): Observable<string>;
getScrollTopText(): Observable<string>;
getFilterTitle(): Observable<string>;
static ɵfac: i0.ɵɵFactoryDeclaration<Table, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<Table, "cds-table, ibm-table", never, { "ariaLabelledby": "ariaLabelledby"; "ariaDescribedby": "ariaDescribedby"; "model": "model"; "size": "size"; "skeleton": "skeleton"; "isDataGrid": "isDataGrid"; "sortable": "sortable"; "noBorder": "noBorder"; "showExpandAllToggle": "showExpandAllToggle"; "showSelectionColumn": "showSelectionColumn"; "enableSingleSelect": "enableSingleSelect"; "scrollLoadDistance": "scrollLoadDistance"; "expandButtonAriaLabel": "expandButtonAriaLabel"; "sortDescendingLabel": "sortDescendingLabel"; "sortAscendingLabel": "sortAscendingLabel"; "translations": "translations"; "striped": "striped"; "stickyHeader": "stickyHeader"; "footerTemplate": "footerTemplate"; "selectionLabelColumn": "selectionLabelColumn"; }, { "sort": "sort"; "selectAll": "selectAll"; "deselectAll": "deselectAll"; "selectRow": "selectRow"; "deselectRow": "deselectRow"; "rowClick": "rowClick"; "scrollLoad": "scrollLoad"; }, never, ["*"], false>;
}