import '@material/mwc-linear-progress'; import '@material/mwc-list/mwc-list-item'; import '../../WebComponents/Select'; import type {LinearProgress as MWCLinearProgress} from '@material/mwc-linear-progress'; import type {Cash} from 'cash-dom'; import type { Children, Vnode, VnodeDOM } from 'mithril'; import Component from '../Component'; import Mdi from '../Mdi'; import TableColumn from './TableColumn'; import TableFooter from './TableFooter'; import TableRow, {type TableRowAttributes} from './TableRow'; declare global { namespace JSX { interface IntrinsicElements { DataTable: DataTable } } } type PaginationAction = 'first' | 'next' | 'previous' | 'last'; export type Attributes = { 'rows-per-page'?: number, 'default-rows-per-page'?: number, 'aria-label'?: string, checkable?: boolean, paginated?: boolean }; export default class DataTable extends Component { rows: Children[] = []; columns: Children[]; footer: Children[]; rowsPerPage = { options: [10, 25, 50, 75, 100], currentStart: 0, value: 10, currentEnd: 10 }; onbeforeupdate(vnode: VnodeDOM) { super.onbeforeupdate(vnode); const children = (vnode.children as Children[]).flat(); this.rows = this.tableRows(children); this.columns = this.filterElements(children, TableColumn); this.footer = this.filterElements(children, TableFooter); const rowsPerPage = this.attrs.get('rows-per-page'); if (rowsPerPage) { this.rowsPerPage.options = rowsPerPage .split(',') .map((value: string) => Number.parseInt(value, 10)); } // @ts-ignore (Waiting proper fix from collect.jsd devs) let defaultRowsPerPage: number = Number.parseInt(this.attrs.get('default-rows-per-page', '10') as string, 10); if (Number.isInteger(defaultRowsPerPage)) { if (!this.rowsPerPage.options.includes(defaultRowsPerPage)) { [defaultRowsPerPage] = this.rowsPerPage.options; } this.rowsPerPage.value = defaultRowsPerPage; } if (this.rowsPerPage.currentStart === 0) { this.rowsPerPage.currentEnd = this.rowsPerPage.value >= this.rows.length ? this.rows.length : defaultRowsPerPage; } } onupdate(vnode: VnodeDOM) { super.onupdate(vnode); const rows: Cash = $(this.element).find('tbody tr'); rows.hide(); // eslint-disable-next-line no-plusplus for ( let index = this.rowsPerPage.currentStart; index < this.rowsPerPage.currentEnd; index += 1 ) { rows.eq(index).show(); } if (this.rowsPerPage.currentStart === 0) { this.paginate('first'); } $(this.element) .find('thead th.mdc-data-table__header-cell--with-sort') .on('click', this.onColumnClicked.bind(this)); $(this.element) .find('.mdc-data-table__pagination-rows-per-page-select') .on('selected', this.onPaginationSelected.bind(this)); $(this.element) .find('.mdc-data-table__pagination-button') .on('click', this.onPaginationButtonClicked.bind(this)); } view() { return (
{this.attrs.has('checkable') && } {this.columns} {this.rows} {this.footer}
{this.attrs.has('paginated') && (
{__('Righe per pagina')}
{this.rowsPerPage.options.map((rowsPerPage) => ( {rowsPerPage} ))}
{__(':start-:chunk di :total', { start: this.rowsPerPage.currentStart + 1, chunk: this.rowsPerPage.currentEnd, total: this.rows.length })}
)}
); } tableRows(children: Children[]): Children[] { let rows = this.filterElements(children, TableRow); if (this.attrs.has('checkable')) { rows = rows.map((row: Children) => { if (!row) { return ''; } const rowNode = row as Vnode; return ( {rowNode.children} ); }); } return rows; } filterElements( elements: Children[], tag: typeof TableRow | typeof TableColumn | typeof TableFooter | string ): Children[] { const filtered = []; for (const element of elements) { if ((element as Vnode).tag === tag) { filtered.push(element); } } return filtered; } getProgress(): Element & Partial | null { return this.element.querySelector('.mdc-data-table__progress-indicator mwc-linear-progress'); } showProgress() { this.manageProgress(true); } hideProgress() { this.manageProgress(false); } onColumnClicked(event: Event) { this.showProgress(); const column: Cash = $(event.target as Element).closest('th'); const ascendingClass = 'mdc-data-table__header-cell--sorted'; // Clean previously sorted info and arrows const columns = $(this.element).find('thead th'); columns.removeClass(ascendingClass); columns.off('click').on('click', this.onColumnClicked.bind(this)); // Add ony one header to sort column.addClass(ascendingClass); // Do sorting this.sortTable(column, false); // Set/remove callbacks column.off('click'); column.find('mwc-icon-button-toggle').on('click', () => { this.sortTable(column); }); } sortTable(column: Cash, toggleClass = true) { const cells = $(this.element) .find(`tr td:nth-child(${column.index() + 1})`) .get(); // Handle button class if (toggleClass) { column.toggleClass('mdc-data-table__header-cell--sorted-descending'); } const isNumeric = column.attr('type') === 'numeric'; const isDescending = column.hasClass( 'mdc-data-table__header-cell--sorted-descending' ); cells.sort((a: HTMLElement, b: HTMLElement) => { let aValue: string | number = a.textContent as string; let bValue: string | number = b.textContent as string; if (isNumeric) { aValue = Number.parseFloat(aValue); bValue = Number.parseFloat(bValue); } if (!isDescending) { const temporary = aValue; aValue = bValue; bValue = temporary; } if (typeof aValue === 'string' && typeof bValue === 'string') { return aValue.localeCompare(bValue); } return aValue < bValue ? -1 : (aValue > bValue ? 1 : 0); }); for (const cell of cells) { const row = $(cell).parent(); row.appendTo(row.parent()); } this.hideProgress(); } onPaginationSelected(event: Event & {detail: {index: number}}) { this.rowsPerPage.value = Number.parseInt( $(event.target as Element) .find('mwc-list-item') .eq(event.detail.index) .val() as string, 10 ); this.rowsPerPage.currentStart = 0; this.rowsPerPage.currentEnd = this.rowsPerPage.value; m.redraw(); } onPaginationButtonClicked(event: Event) { const button: Cash = $(event.target as Element); this.paginate(button.data('page') as PaginationAction); m.redraw(); } paginate(action: PaginationAction) { const increments = { first: -this.rowsPerPage.currentStart, next: this.rowsPerPage.value, previous: -this.rowsPerPage.value, last: this.rows.length - this.rowsPerPage.currentStart }; const increment = increments[action]; if (action !== 'first') { this.rowsPerPage.currentStart += increment; } if (action !== 'last') { this.rowsPerPage.currentEnd += increment; } const paginationButtons = this.element.querySelectorAll( '.mdc-data-table__pagination-button' ); const disabled = { first: this.rowsPerPage.currentStart === 0, previous: this.rowsPerPage.currentStart === 0, next: this.rowsPerPage.currentEnd >= this.rows.length, last: this.rowsPerPage.currentEnd >= this.rows.length }; for (const button of paginationButtons) { const buttonElement = $(button); const buttonAction = buttonElement.data('page') as PaginationAction; buttonElement.prop('disabled', disabled[buttonAction]); } } private manageProgress(show: boolean) { $(this.element).toggleClass('mdc-data-table--in-progress'); const progress = this.getProgress(); if (progress) { (progress as MWCLinearProgress)[show ? 'open' : 'close'](); } } }