UNPKG

carbon-components-angular

Version:
929 lines 96.2 kB
import { Component, Input, Output, EventEmitter, HostBinding } from "@angular/core"; import { Subscription, fromEvent } from "rxjs"; import { TableModel } from "./table-model.class"; import { TableHeaderItem } from "./table-header-item.class"; import { TableItem } from "./table-item.class"; import { getFocusElementList, tabbableSelectorIgnoreTabIndex } from "carbon-components-angular/common"; import { merge } from "carbon-components-angular/utils"; import { DataGridInteractionModel } from "./data-grid-interaction-model.class"; import { TableDomAdapter } from "./table-adapter.class"; import * as i0 from "@angular/core"; import * as i1 from "carbon-components-angular/i18n"; import * as i2 from "@angular/common"; import * as i3 from "./table.directive"; import * as i4 from "./head/table-head.component"; import * as i5 from "./body/table-body.component"; /** * 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 class Table { /** * Creates an instance of Table. */ constructor(elementRef, applicationRef, i18n) { this.elementRef = elementRef; this.applicationRef = applicationRef; this.i18n = i18n; /** * Size of the table rows. */ this.size = "md"; /** * Set to `true` for a loading table. */ this.skeleton = false; /** * 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. */ this.sortable = true; this.noBorder = true; /** * Set to `true` to show expansion toggle when table consists of row expansions */ this.showExpandAllToggle = false; /** * Controls whether to show the selection checkboxes column or not. */ this.showSelectionColumn = true; /** * Controls whether to enable multiple or single row selection. */ this.enableSingleSelect = false; /** * Distance (in px) from the bottom that view has to reach before * `scrollLoad` event is emitted. */ this.scrollLoadDistance = 0; /** * Set to `false` to remove table rows (zebra) stripes. */ this.striped = true; /** * When true, rows that set `TableItem.hasAILabelDecorator` on the decorator column receive AI row styling. */ this.withRowAILabels = false; /** * Allows table content to scroll horizontally */ this.tableContent = true; /** * Set to `true` to stick the header to the top of the table */ this.stickyHeader = false; /** * 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); * } * } * ``` */ this.sort = new EventEmitter(); /** * Emits if all rows are selected. * * @param model */ this.selectAll = new EventEmitter(); /** * Emits if all rows are deselected. * * @param model */ this.deselectAll = new EventEmitter(); /** * Emits if a single row is selected. * * @param ({model: this.model, selectedRowIndex: index}) */ this.selectRow = new EventEmitter(); /** * Emits if a single row is deselected. * * @param ({model: this.model, deselectedRowIndex: index}) */ this.deselectRow = new EventEmitter(); /** * Emits if a row item excluding expandButtons, checkboxes, or radios is clicked. */ this.rowClick = new EventEmitter(); /** * Emits when table requires more data to be loaded. */ this.scrollLoad = new EventEmitter(); /** * Controls if all checkboxes are viewed as selected. */ this.selectAllCheckbox = false; /** * Controls the indeterminate state of the header checkbox. */ this.selectAllCheckboxSomeSelected = false; this.isColumnDragging = false; this.columnDraggedHoverIndex = -1; this.columnDraggedPosition = ""; this._isDataGrid = false; // flag to prevent getters/setters from querying the view before it's fully instantiated this.isViewReady = false; this.subscriptions = new Subscription(); this._expandButtonAriaLabel = this.i18n.getOverridable("TABLE.EXPAND_BUTTON"); this._sortDescendingLabel = this.i18n.getOverridable("TABLE.SORT_DESCENDING"); this._sortAscendingLabel = this.i18n.getOverridable("TABLE.SORT_ASCENDING"); this._checkboxHeaderLabel = this.i18n.getOverridable("TABLE.CHECKBOX_HEADER"); this._checkboxRowLabel = this.i18n.getOverridable("TABLE.CHECKBOX_ROW"); this._endOfDataText = this.i18n.getOverridable("TABLE.END_OF_DATA"); this._scrollTopText = this.i18n.getOverridable("TABLE.SCROLL_TOP"); this._filterTitle = this.i18n.getOverridable("TABLE.FILTER"); } /** * 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, columnCount) { const model = new TableModel(); let header = new Array(); let data = new Array(); let row = new Array(); for (let i = 0; i < columnCount; i++) { header.push(new TableHeaderItem()); row.push(new TableItem()); } for (let i = 0; i < rowCount - 1; i++) { data.push(row); } model.header = header; model.data = data; return model; } static setTabIndex(element, index) { const focusElementList = getFocusElementList(element, tabbableSelectorIgnoreTabIndex); if (element.firstElementChild && element.firstElementChild.classList.contains("cds--table-sort") && focusElementList.length > 1) { focusElementList[1].tabIndex = index; } else if (focusElementList.length > 0) { focusElementList[0].tabIndex = index; } else { element.tabIndex = index; } } static focus(element) { const focusElementList = getFocusElementList(element, tabbableSelectorIgnoreTabIndex); if ((element.firstElementChild?.classList.contains("cds--table-sort") && focusElementList.length > 1) || focusElementList.length > 0) { focusElementList[0].focus(); } else { element.focus(); } } /** * `TableModel` with data the table is to display. */ set model(m) { if (this._model) { this.subscriptions.unsubscribe(); // Need to create a new subscription instance here because unsubscribing prevents any new subscriptions // from being added for some reason. When a new model is set, none of the subscriptions would exist. this.subscriptions = new Subscription(); } this._model = m; const rowsChange = this._model.rowsSelectedChange.subscribe(() => this.updateSelectAllCheckbox()); const dataChange = this._model.dataChange.subscribe(() => { if (this.isDataGrid) { this.resetTabIndex(); } this.updateSelectAllCheckbox(); }); this.subscriptions.add(rowsChange); this.subscriptions.add(dataChange); if (this.isDataGrid) { const expandedChange = this._model.rowsExpandedChange.subscribe(() => { // Allows the expanded row to have a focus state when it exists in the DOM setTimeout(() => { const expandedRows = this.elementRef.nativeElement.querySelectorAll(".cds--expandable-row:not(.cds--parent-row)"); Array.from(expandedRows).forEach(row => { if (row.firstElementChild.tabIndex === undefined || row.firstElementChild.tabIndex !== -1) { row.firstElementChild.tabIndex = -1; } }); }); }); this.subscriptions.add(expandedChange); } } get model() { return this._model; } /** * Set to `true` for a data grid with keyboard interactions. */ set isDataGrid(value) { this._isDataGrid = value; if (this.isViewReady) { if (value) { this.enableDataGridInteractions(); } else { this.disableDataGridInteractions(); } } } get isDataGrid() { return this._isDataGrid; } /** * @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. * */ // @Input() columnsResizable = false; /** * @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. * */ // @Input() columnsDraggable = false; set expandButtonAriaLabel(value) { this._expandButtonAriaLabel.override(value); } get expandButtonAriaLabel() { return this._expandButtonAriaLabel.value; } set sortDescendingLabel(value) { this._sortDescendingLabel.override(value); } get sortDescendingLabel() { return this._sortDescendingLabel.value; } set sortAscendingLabel(value) { this._sortAscendingLabel.override(value); } get sortAscendingLabel() { return this._sortAscendingLabel.value; } /** * 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) { const valueWithDefaults = merge(this.i18n.getMultiple("TABLE"), value); this._filterTitle.override(valueWithDefaults.FILTER); this._endOfDataText.override(valueWithDefaults.END_OF_DATA); this._scrollTopText.override(valueWithDefaults.SCROLL_TOP); this._checkboxHeaderLabel.override(valueWithDefaults.CHECKBOX_HEADER); this._checkboxRowLabel.override(valueWithDefaults.CHECKBOX_ROW); } get noData() { return !this.model.data || this.model.data.length === 0 || this.model.data.length === 1 && this.model.data[0].length === 0; } ngOnInit() { // Manually trigger check to see if all checkboxes are selected // This is since subscription is made AFTER checkboxes are selected this.updateSelectAllCheckbox(); } ngAfterViewInit() { this.isViewReady = true; if (this.isDataGrid) { this.enableDataGridInteractions(); } } ngOnDestroy() { this.subscriptions.unsubscribe(); if (this.positionSubscription) { this.positionSubscription.unsubscribe(); } } enableDataGridInteractions() { // if we have an `interactioModel` we've already enabled datagrid if (this.interactionModel) { return; } const table = this.elementRef.nativeElement.querySelector("table"); const tableAdapter = new TableDomAdapter(table); const keydownEventStream = fromEvent(table, "keydown"); const clickEventStream = fromEvent(table, "click"); this.interactionModel = new DataGridInteractionModel(keydownEventStream, clickEventStream, tableAdapter); this.positionSubscription = this.interactionModel.position.subscribe(event => { const [currentRow, currentColumn] = event.current; const [previousRow, previousColumn] = event.previous; const currentElement = tableAdapter.getCell(currentRow, currentColumn); Table.setTabIndex(currentElement, 0); // if the model has just initialized don't focus or reset anything if (previousRow === -1 || previousColumn === -1) { return; } // Make the previous cell unfocusable (if it's not the current) if (previousRow !== currentRow || previousColumn !== currentColumn) { const previousElement = tableAdapter.getCell(previousRow, previousColumn); Table.setTabIndex(previousElement, -1); } Table.focus(currentElement); }); // call this after assigning `this.interactionModel` since it depends on it this.resetTabIndex(); } disableDataGridInteractions() { // unsubscribe first so we don't cause the focus to fly around if (this.positionSubscription) { this.positionSubscription.unsubscribe(); } // undo tab indexing (also resets the model) this.resetTabIndex(0); // null out the model ref this.interactionModel = null; } onSelectAll() { this.model.selectAll(true); this.selectAll.emit(this.model); } onDeselectAll() { this.model.selectAll(false); this.deselectAll.emit(this.model); } onSelectRow(event) { // check for the existence of the selectedRowIndex property if (Object.keys(event).includes("selectedRowIndex")) { if (this.enableSingleSelect) { this.model.selectAll(false); } this.model.selectRow(event.selectedRowIndex, true); this.selectRow.emit(event); } else { this.model.selectRow(event.deselectedRowIndex, false); this.deselectRow.emit(event); } } onRowClick(index) { this.rowClick.emit(index); } updateSelectAllCheckbox() { const selectedRowsCount = this.model.selectedRowsCount(); if (selectedRowsCount <= 0) { // reset select all checkbox if nothing selected this.selectAllCheckbox = false; this.selectAllCheckboxSomeSelected = false; } else if (selectedRowsCount < this.model.data.length) { this.selectAllCheckbox = true; this.selectAllCheckboxSomeSelected = true; } else { this.selectAllCheckbox = true; this.selectAllCheckboxSomeSelected = false; } } resetTabIndex(newTabIndex = -1) { // ensure the view is ready for the reset before we preform the actual reset setTimeout(() => { // reset all the tabIndexes we can find const focusElementList = getFocusElementList(this.elementRef.nativeElement, tabbableSelectorIgnoreTabIndex); if (focusElementList) { focusElementList.forEach(tabbable => { tabbable.tabIndex = newTabIndex; }); } // reset interaction model positions and tabIndexes if (this.interactionModel) { this.interactionModel.resetTabIndexes(newTabIndex); } }); } columnResizeStart(event, column) { this.columnResizeWidth = parseInt(column.style.width, 10); this.columnResizeMouseX = event.clientX; event.preventDefault(); this.mouseMoveSubscription = fromEvent(document.body, "mousemove").subscribe(event => { this.columnResizeProgress(event, column); }); this.mouseUpSubscription = fromEvent(document.body, "mouseup").subscribe(event => { this.columnResizeEnd(event, column); }); } columnResizeProgress(event, column) { const move = event.clientX - this.columnResizeMouseX; column.style.width = `${this.columnResizeWidth + move}px`; } columnResizeEnd(event, column) { this.mouseMoveSubscription.unsubscribe(); this.mouseUpSubscription.unsubscribe(); } /** * Triggered when the user scrolls on the `<tbody>` element. * Emits the `scrollLoad` event. */ onScroll(event) { const distanceFromBottom = event.target.scrollHeight - event.target.clientHeight - event.target.scrollTop; if (distanceFromBottom <= this.scrollLoadDistance) { this.scrollLoad.emit(this.model); } else { this.model.isEnd = false; } } columnDragStart(event, columnIndex) { this.isColumnDragging = true; this.columnDraggedHoverIndex = columnIndex; event.dataTransfer.setData("columnIndex", JSON.stringify(columnIndex)); } columnDragEnd(event, columnIndex) { this.isColumnDragging = false; this.columnDraggedHoverIndex = -1; } columnDragEnter(event, position, columnIndex) { this.columnDraggedPosition = position; this.columnDraggedHoverIndex = columnIndex; } columnDragLeave(event, position, columnIndex) { this.columnDraggedPosition = ""; } columnDragover(event, position, columnIndex) { this.columnDraggedHoverIndex = columnIndex; this.columnDraggedPosition = position; // needed to tell browser to allow dropping event.preventDefault(); } columnDrop(event, position, columnIndex) { this.isColumnDragging = false; this.columnDraggedHoverIndex = -1; this.columnDraggedPosition = ""; this.model.moveColumn(parseInt(event.dataTransfer.getData("columnIndex"), 10), columnIndex + (position === "right" ? 1 : 0)); } doSort(index) { if (this.sort.observers.length === 0) { // no sort provided so do the simple sort this.model.cycleSortState(index); this.model.sort(index); } this.sort.emit(index); } /** * Triggered when the user scrolls on the `<tbody>` element. * Emits the `scrollLoad` event. */ scrollToTop(event) { event.target.parentElement.parentElement.parentElement.parentElement.children[1].scrollTop = 0; this.model.isEnd = false; } getSelectionLabelValue(row) { if (!this.selectionLabelColumn) { return { value: this.i18n.get().TABLE.ROW }; } return { value: row[this.selectionLabelColumn].data }; } getExpandButtonAriaLabel() { return this._expandButtonAriaLabel.subject; } getSortDescendingLabel() { return this._sortDescendingLabel.subject; } getSortAscendingLabel() { return this._sortAscendingLabel.subject; } getCheckboxHeaderLabel() { return this._checkboxHeaderLabel.subject; } getCheckboxRowLabel() { return this._checkboxRowLabel.subject; } getEndOfDataText() { return this._endOfDataText.subject; } getScrollTopText() { return this._scrollTopText.subject; } getFilterTitle() { return this._filterTitle.subject; } } Table.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Table, deps: [{ token: i0.ElementRef }, { token: i0.ApplicationRef }, { token: i1.I18n }], target: i0.ɵɵFactoryTarget.Component }); Table.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: Table, selector: "cds-table, ibm-table", inputs: { 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", withRowAILabels: "withRowAILabels", stickyHeader: "stickyHeader", footerTemplate: "footerTemplate", selectionLabelColumn: "selectionLabelColumn" }, outputs: { sort: "sort", selectAll: "selectAll", deselectAll: "deselectAll", selectRow: "selectRow", deselectRow: "deselectRow", rowClick: "rowClick", scrollLoad: "scrollLoad" }, host: { properties: { "class.cds--data-table-content": "this.tableContent", "class.cds--data-table_inner-container": "this.stickyHeader" } }, ngImport: i0, template: ` <table cdsTable [sortable]="sortable" [noBorder]="noBorder" [ngClass]="{'cds--data-table--sticky-header': stickyHeader}" [size]="size" [striped]="striped" [skeleton]="skeleton" [attr.aria-labelledby]="ariaLabelledby" [attr.aria-describedby]="ariaDescribedby"> <thead cdsTableHead [sortable]="sortable" (deselectAll)="onDeselectAll()" (selectAll)="onSelectAll()" (expandAllRows)="model.expandAllRows(true)" (collapseAllRows)="model.expandAllRows(false)" (sort)="doSort($event)" [checkboxHeaderLabel]="getCheckboxHeaderLabel()" [filterTitle]="getFilterTitle()" [model]="model" [selectAllCheckbox]="selectAllCheckbox" [selectAllCheckboxSomeSelected]="selectAllCheckboxSomeSelected" [showSelectionColumn]="showSelectionColumn" [enableSingleSelect]="enableSingleSelect" [showExpandAllToggle]="showExpandAllToggle" [skeleton]="skeleton" [sortAscendingLabel]="sortAscendingLabel" [sortDescendingLabel]="sortDescendingLabel" [stickyHeader]="stickyHeader" [withRowAILabels]="withRowAILabels"> </thead> <tbody cdsTableBody (deselectRow)="onSelectRow($event)" (scroll)="onScroll($event)" (selectRow)="onSelectRow($event)" [checkboxRowLabel]="getCheckboxRowLabel()" [enableSingleSelect]="enableSingleSelect" (rowClick)="onRowClick($event)" [expandButtonAriaLabel]="expandButtonAriaLabel" [model]="model" [size]="size" [ngStyle]="{'overflow-y': 'scroll'}" [selectionLabelColumn]="selectionLabelColumn" [showSelectionColumn]="showSelectionColumn" [skeleton]="skeleton" [withRowAILabels]="withRowAILabels" *ngIf="!noData; else noDataTemplate"> </tbody> <ng-template #noDataTemplate><ng-content></ng-content></ng-template> <tfoot> <ng-template [ngTemplateOutlet]="footerTemplate"> </ng-template> <tr *ngIf="this.model.isLoading"> <td class="table_loading-indicator"> <div class="cds--loading cds--loading--small"> <svg class="cds--loading__svg" viewBox="-75 -75 150 150"> <circle class="cds--loading__stroke" cx="0" cy="0" r="37.5" /> </svg> </div> </td> </tr> <tr *ngIf="this.model.isEnd"> <td class="table_end-indicator"> <h5>{{getEndOfDataText() | async}}</h5> <button (click)="scrollToTop($event)" class="btn--secondary-sm"> {{getScrollTopText() | async}} </button> </td> </tr> </tfoot> </table> `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i3.TableDirective, selector: "[cdsTable], [ibmTable]", inputs: ["sortable", "noBorder", "striped", "skeleton", "size"] }, { kind: "component", type: i4.TableHead, selector: "[cdsTableHead], [ibmTableHead]", inputs: ["model", "showSelectionColumn", "enableSingleSelect", "selectAllCheckboxSomeSelected", "selectAllCheckbox", "skeleton", "stickyHeader", "showExpandAllToggle", "sortable", "withRowAILabels", "checkboxHeaderLabel", "sortDescendingLabel", "sortAscendingLabel", "filterTitle"], outputs: ["sort", "selectAll", "deselectAll", "expandAllRows", "collapseAllRows"] }, { kind: "component", type: i5.TableBody, selector: "[cdsTableBody], [ibmTableBody]", inputs: ["model", "enableSingleSelect", "expandButtonAriaLabel", "checkboxRowLabel", "showSelectionColumn", "size", "selectionLabelColumn", "skeleton", "withRowAILabels"], outputs: ["selectRow", "deselectRow", "rowClick"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Table, decorators: [{ type: Component, args: [{ selector: "cds-table, ibm-table", template: ` <table cdsTable [sortable]="sortable" [noBorder]="noBorder" [ngClass]="{'cds--data-table--sticky-header': stickyHeader}" [size]="size" [striped]="striped" [skeleton]="skeleton" [attr.aria-labelledby]="ariaLabelledby" [attr.aria-describedby]="ariaDescribedby"> <thead cdsTableHead [sortable]="sortable" (deselectAll)="onDeselectAll()" (selectAll)="onSelectAll()" (expandAllRows)="model.expandAllRows(true)" (collapseAllRows)="model.expandAllRows(false)" (sort)="doSort($event)" [checkboxHeaderLabel]="getCheckboxHeaderLabel()" [filterTitle]="getFilterTitle()" [model]="model" [selectAllCheckbox]="selectAllCheckbox" [selectAllCheckboxSomeSelected]="selectAllCheckboxSomeSelected" [showSelectionColumn]="showSelectionColumn" [enableSingleSelect]="enableSingleSelect" [showExpandAllToggle]="showExpandAllToggle" [skeleton]="skeleton" [sortAscendingLabel]="sortAscendingLabel" [sortDescendingLabel]="sortDescendingLabel" [stickyHeader]="stickyHeader" [withRowAILabels]="withRowAILabels"> </thead> <tbody cdsTableBody (deselectRow)="onSelectRow($event)" (scroll)="onScroll($event)" (selectRow)="onSelectRow($event)" [checkboxRowLabel]="getCheckboxRowLabel()" [enableSingleSelect]="enableSingleSelect" (rowClick)="onRowClick($event)" [expandButtonAriaLabel]="expandButtonAriaLabel" [model]="model" [size]="size" [ngStyle]="{'overflow-y': 'scroll'}" [selectionLabelColumn]="selectionLabelColumn" [showSelectionColumn]="showSelectionColumn" [skeleton]="skeleton" [withRowAILabels]="withRowAILabels" *ngIf="!noData; else noDataTemplate"> </tbody> <ng-template #noDataTemplate><ng-content></ng-content></ng-template> <tfoot> <ng-template [ngTemplateOutlet]="footerTemplate"> </ng-template> <tr *ngIf="this.model.isLoading"> <td class="table_loading-indicator"> <div class="cds--loading cds--loading--small"> <svg class="cds--loading__svg" viewBox="-75 -75 150 150"> <circle class="cds--loading__stroke" cx="0" cy="0" r="37.5" /> </svg> </div> </td> </tr> <tr *ngIf="this.model.isEnd"> <td class="table_end-indicator"> <h5>{{getEndOfDataText() | async}}</h5> <button (click)="scrollToTop($event)" class="btn--secondary-sm"> {{getScrollTopText() | async}} </button> </td> </tr> </tfoot> </table> `, styles: [":host{display:block}\n"] }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ApplicationRef }, { type: i1.I18n }]; }, propDecorators: { ariaLabelledby: [{ type: Input }], ariaDescribedby: [{ type: Input }], model: [{ type: Input }], size: [{ type: Input }], skeleton: [{ type: Input }], isDataGrid: [{ type: Input }], sortable: [{ type: Input }], noBorder: [{ type: Input }], showExpandAllToggle: [{ type: Input }], showSelectionColumn: [{ type: Input }], enableSingleSelect: [{ type: Input }], scrollLoadDistance: [{ type: Input }], expandButtonAriaLabel: [{ type: Input }], sortDescendingLabel: [{ type: Input }], sortAscendingLabel: [{ type: Input }], translations: [{ type: Input }], striped: [{ type: Input }], withRowAILabels: [{ type: Input }], tableContent: [{ type: HostBinding, args: ["class.cds--data-table-content"] }], stickyHeader: [{ type: HostBinding, args: ["class.cds--data-table_inner-container"] }, { type: Input }], footerTemplate: [{ type: Input }], selectionLabelColumn: [{ type: Input }], sort: [{ type: Output }], selectAll: [{ type: Output }], deselectAll: [{ type: Output }], selectRow: [{ type: Output }], deselectRow: [{ type: Output }], rowClick: [{ type: Output }], scrollLoad: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFibGUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3RhYmxlL3RhYmxlLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ04sU0FBUyxFQUVULEtBQUssRUFFTCxNQUFNLEVBQ04sWUFBWSxFQUtaLFdBQVcsRUFDWCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsWUFBWSxFQUFFLFNBQVMsRUFBYyxNQUFNLE1BQU0sQ0FBQztBQUUzRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzVELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUUvQyxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsOEJBQThCLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUV2RyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDeEQsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDL0UsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHVCQUF1QixDQUFDOzs7Ozs7O0FBR3hEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnSkc7QUFxRkgsTUFBTSxPQUFPLEtBQUs7SUFrWmpCOztPQUVHO0lBQ0gsWUFDVyxVQUFzQixFQUN0QixjQUE4QixFQUM5QixJQUFVO1FBRlYsZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQUN0QixtQkFBYyxHQUFkLGNBQWMsQ0FBZ0I7UUFDOUIsU0FBSSxHQUFKLElBQUksQ0FBTTtRQTlTckI7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