2021-08-26 20:09:28 +02:00
|
|
|
import '@material/mwc-linear-progress';
|
|
|
|
import '@material/mwc-list/mwc-list-item';
|
2021-11-30 13:18:47 +01:00
|
|
|
import '../../WebComponents/Select';
|
2021-09-07 13:28:20 +02:00
|
|
|
|
2021-11-23 15:34:34 +01:00
|
|
|
import {type Cash} from 'cash-dom/dist/cash';
|
2021-11-22 19:22:54 +01:00
|
|
|
import {
|
|
|
|
type Children,
|
|
|
|
type Vnode
|
|
|
|
} from 'mithril';
|
2021-12-14 10:27:19 +01:00
|
|
|
import PropTypes from 'prop-types';
|
2021-11-22 19:22:54 +01:00
|
|
|
|
2021-09-29 15:32:31 +02:00
|
|
|
import Component from '../Component.jsx';
|
|
|
|
import Mdi from '../Mdi.jsx';
|
2021-11-22 19:22:54 +01:00
|
|
|
import TableColumn from './TableColumn.jsx';
|
|
|
|
import TableFooter from './TableFooter.jsx';
|
|
|
|
import TableRow from './TableRow.jsx';
|
2021-08-26 20:09:28 +02:00
|
|
|
|
|
|
|
export default class DataTable extends Component {
|
2021-12-14 10:27:19 +01:00
|
|
|
static propTypes = {
|
|
|
|
'rows-per-page': PropTypes.number,
|
|
|
|
'default-rows-per-page': PropTypes.number,
|
|
|
|
'aria-label': PropTypes.string,
|
|
|
|
checkable: PropTypes.bool,
|
|
|
|
paginated: PropTypes.bool
|
|
|
|
};
|
|
|
|
|
2021-11-30 19:51:33 +01:00
|
|
|
rows: Cash[] = [];
|
2021-11-30 16:44:53 +01:00
|
|
|
columns: Children[];
|
|
|
|
footer: Children[];
|
|
|
|
|
|
|
|
rowsPerPage = {
|
|
|
|
options: [10, 25, 50, 75, 100],
|
2021-11-30 19:51:33 +01:00
|
|
|
currentStart: 0,
|
|
|
|
value: 10,
|
|
|
|
currentEnd: 10
|
2021-11-30 16:44:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
onbeforeupdate(vnode) {
|
|
|
|
super.onbeforeupdate(vnode);
|
|
|
|
|
|
|
|
const children = (vnode.children: Children[]).flat();
|
2021-11-30 15:12:48 +01:00
|
|
|
|
2021-11-30 16:44:53 +01:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
let defaultRowsPerPage = this.attrs.get('default-rows-per-page');
|
|
|
|
if (Number.isInteger(defaultRowsPerPage)) {
|
|
|
|
defaultRowsPerPage = Number.parseInt(defaultRowsPerPage, 10);
|
2021-11-30 19:51:33 +01:00
|
|
|
|
2021-11-30 16:44:53 +01:00
|
|
|
if (!this.rowsPerPage.options.includes(defaultRowsPerPage)) {
|
2021-11-30 19:51:33 +01:00
|
|
|
[defaultRowsPerPage] = this.rowsPerPage.options;
|
2021-11-30 16:44:53 +01:00
|
|
|
}
|
2021-11-30 19:51:33 +01:00
|
|
|
this.rowsPerPage.value = defaultRowsPerPage;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.rowsPerPage.currentStart === 0) {
|
|
|
|
this.rowsPerPage.currentEnd = this.rowsPerPage.value >= this.rows.length ? this.rows.length
|
|
|
|
: defaultRowsPerPage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onupdate(vnode) {
|
|
|
|
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++) {
|
|
|
|
rows.eq(index).show();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.rowsPerPage.currentStart === 0) {
|
|
|
|
this.paginate('first');
|
2021-11-30 15:12:48 +01:00
|
|
|
}
|
2021-12-03 15:24:06 +01:00
|
|
|
|
|
|
|
$(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));
|
2021-11-30 16:44:53 +01:00
|
|
|
}
|
2021-11-30 15:12:48 +01:00
|
|
|
|
2021-11-30 16:44:53 +01:00
|
|
|
view(vnode) {
|
2021-09-07 13:28:20 +02:00
|
|
|
return <div className="mdc-data-table" {...this.attrs.all()}>
|
2021-08-26 20:09:28 +02:00
|
|
|
<div className="mdc-data-table__table-container">
|
2021-11-22 19:22:54 +01:00
|
|
|
<table className="mdc-data-table__table" aria-label={this.attrs.get('aria-label')}>
|
|
|
|
<thead>
|
2021-11-30 16:44:53 +01:00
|
|
|
<tr className="mdc-data-table__header-row">
|
|
|
|
{this.attrs.has('checkable') && <TableColumn type="checkbox"/>}
|
|
|
|
{this.columns}
|
|
|
|
</tr>
|
2021-11-22 19:22:54 +01:00
|
|
|
</thead>
|
|
|
|
|
2021-11-23 21:30:03 +01:00
|
|
|
<tbody className="mdc-data-table__content">
|
2021-11-30 19:51:33 +01:00
|
|
|
{this.rows}
|
2021-11-22 19:22:54 +01:00
|
|
|
</tbody>
|
|
|
|
|
2021-11-30 16:44:53 +01:00
|
|
|
{this.footer}
|
2021-08-26 20:09:28 +02:00
|
|
|
</table>
|
|
|
|
|
2021-11-22 19:22:54 +01:00
|
|
|
{this.attrs.has('paginated') && <div className="mdc-data-table__pagination">
|
2021-08-26 20:09:28 +02:00
|
|
|
<div className="mdc-data-table__pagination-trailing">
|
|
|
|
<div className="mdc-data-table__pagination-rows-per-page">
|
|
|
|
<div className="mdc-data-table__pagination-rows-per-page-label">
|
2021-11-22 19:22:54 +01:00
|
|
|
{__('Righe per pagina')}
|
2021-08-26 20:09:28 +02:00
|
|
|
</div>
|
|
|
|
|
2021-11-30 13:18:47 +01:00
|
|
|
<material-select
|
|
|
|
outlined
|
|
|
|
className="mdc-data-table__pagination-rows-per-page-select"
|
|
|
|
fixedMenuPosition
|
|
|
|
style="--mdc-select-width: 112px; --mdc-select-height: 36px; --mdc-menu-item-height: 36px;"
|
|
|
|
>
|
2021-11-30 16:44:53 +01:00
|
|
|
{this.rowsPerPage.options.map(
|
2021-11-30 16:53:22 +01:00
|
|
|
rowsPerPage => (
|
2021-11-30 19:51:33 +01:00
|
|
|
<mwc-list-item
|
|
|
|
key={rowsPerPage}
|
|
|
|
value={rowsPerPage}
|
|
|
|
selected={this.rowsPerPage.value === rowsPerPage}
|
|
|
|
>
|
|
|
|
{rowsPerPage}
|
|
|
|
</mwc-list-item>
|
2021-11-30 16:53:22 +01:00
|
|
|
)
|
2021-11-23 21:27:06 +01:00
|
|
|
)}
|
2021-11-30 13:18:47 +01:00
|
|
|
</material-select>
|
2021-08-26 20:09:28 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="mdc-data-table__pagination-navigation">
|
|
|
|
<div className="mdc-data-table__pagination-total">
|
2021-11-30 19:51:33 +01:00
|
|
|
{__(':start-:chunk di :total', {
|
|
|
|
start: this.rowsPerPage.currentStart + 1,
|
|
|
|
chunk: this.rowsPerPage.currentEnd,
|
|
|
|
total: this.rows.length
|
2021-11-30 16:44:53 +01:00
|
|
|
})}
|
2021-08-26 20:09:28 +02:00
|
|
|
</div>
|
2021-11-30 16:44:53 +01:00
|
|
|
<mwc-icon-button className="mdc-data-table__pagination-button" data-page="first"
|
|
|
|
disabled>
|
2021-11-23 21:31:59 +01:00
|
|
|
<Mdi icon="page-first"/>
|
2021-08-26 20:09:28 +02:00
|
|
|
</mwc-icon-button>
|
2021-11-30 19:51:33 +01:00
|
|
|
<mwc-icon-button className="mdc-data-table__pagination-button" data-page="previous"
|
2021-11-30 16:44:53 +01:00
|
|
|
disabled>
|
2021-11-23 21:31:59 +01:00
|
|
|
<Mdi icon="chevron-left"/>
|
2021-08-26 20:09:28 +02:00
|
|
|
</mwc-icon-button>
|
|
|
|
<mwc-icon-button className="mdc-data-table__pagination-button" data-page="next">
|
2021-11-23 21:31:59 +01:00
|
|
|
<Mdi icon="chevron-right"/>
|
2021-08-26 20:09:28 +02:00
|
|
|
</mwc-icon-button>
|
|
|
|
<mwc-icon-button className="mdc-data-table__pagination-button" data-page="last">
|
2021-11-23 21:31:59 +01:00
|
|
|
<Mdi icon="page-last"/>
|
2021-08-26 20:09:28 +02:00
|
|
|
</mwc-icon-button>
|
|
|
|
</div>
|
|
|
|
</div>
|
2021-11-22 19:22:54 +01:00
|
|
|
</div>}
|
2021-08-26 20:09:28 +02:00
|
|
|
|
|
|
|
<div className="mdc-data-table__progress-indicator">
|
|
|
|
<div className="mdc-data-table__scrim"/>
|
|
|
|
<mwc-linear-progress className="mdc-data-table__linear-progress" indeterminate/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
}
|
2021-11-22 19:22:54 +01:00
|
|
|
|
2021-11-30 16:44:53 +01:00
|
|
|
tableRows(children: Children[]): Children[] {
|
|
|
|
let rows = this.filterElements(children, TableRow);
|
2021-11-22 19:22:54 +01:00
|
|
|
|
|
|
|
if (this.attrs.has('checkable')) {
|
|
|
|
rows = rows.map((row: Vnode) => (
|
2021-11-30 16:44:53 +01:00
|
|
|
<TableRow key={row.attrs.key} checkable {...row.attrs}>
|
|
|
|
{row.children}
|
|
|
|
</TableRow>
|
2021-11-22 19:22:54 +01:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
return rows;
|
|
|
|
}
|
|
|
|
|
2021-11-30 14:47:35 +01:00
|
|
|
filterElements(elements: Children[], tag: Component | string): Children[] {
|
2021-11-22 19:22:54 +01:00
|
|
|
const filtered = [];
|
|
|
|
|
|
|
|
for (const element: Vnode of elements) {
|
|
|
|
if (element.tag === tag) {
|
|
|
|
filtered.push(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return filtered;
|
|
|
|
}
|
2021-11-23 15:34:34 +01:00
|
|
|
|
|
|
|
oncreate(vnode) {
|
|
|
|
super.oncreate(vnode);
|
|
|
|
}
|
|
|
|
|
|
|
|
showProgress() {
|
2021-11-30 13:26:51 +01:00
|
|
|
$(this.element)
|
|
|
|
.addClass('mdc-data-table--in-progress')
|
|
|
|
.find('.mdc-data-table__progress-indicator mwc-linear-progress')
|
|
|
|
.get(0)
|
|
|
|
.open();
|
2021-11-23 15:34:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
hideProgress() {
|
2021-11-30 13:26:51 +01:00
|
|
|
$(this.element)
|
|
|
|
.removeClass('mdc-data-table--in-progress')
|
|
|
|
.find('.mdc-data-table__progress-indicator mwc-linear-progress')
|
|
|
|
.get(0)
|
|
|
|
.open();
|
2021-11-23 15:34:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
onColumnClicked(event: Event) {
|
|
|
|
this.showProgress();
|
|
|
|
|
2021-11-30 16:44:53 +01:00
|
|
|
const column: Cash = $(event.target)
|
|
|
|
.closest('th');
|
2021-11-23 15:34:34 +01:00
|
|
|
const ascendingClass = 'mdc-data-table__header-cell--sorted';
|
|
|
|
|
|
|
|
// Clean previously sorted info and arrows
|
2021-11-30 16:44:53 +01:00
|
|
|
const columns = $(this.element)
|
|
|
|
.find('thead th');
|
2021-11-23 15:34:34 +01:00
|
|
|
columns.removeClass(ascendingClass);
|
2021-12-03 16:14:04 +01:00
|
|
|
columns.off('click').on('click', this.onColumnClicked.bind(this));
|
2021-11-23 15:34:34 +01:00
|
|
|
|
|
|
|
// Add ony one header to sort
|
|
|
|
column.addClass(ascendingClass);
|
|
|
|
|
|
|
|
// Do sorting
|
2021-12-03 16:14:04 +01:00
|
|
|
this.sortTable(column.attr('id'), false);
|
|
|
|
|
|
|
|
// Set/remove callbacks
|
|
|
|
column.off('click');
|
|
|
|
column.find('mwc-icon-button-toggle').on('click', () => {
|
|
|
|
this.sortTable(column.attr('id'));
|
|
|
|
});
|
2021-11-23 15:34:34 +01:00
|
|
|
}
|
|
|
|
|
2021-12-03 16:14:04 +01:00
|
|
|
sortTable(columnId: number, toggleClass = true) {
|
|
|
|
const column: Cash = $(`#${columnId}`);
|
|
|
|
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) => {
|
2021-11-23 15:34:34 +01:00
|
|
|
let aValue = a.textContent;
|
|
|
|
let bValue = b.textContent;
|
|
|
|
|
|
|
|
if (isNumeric) {
|
|
|
|
aValue = Number.parseFloat(aValue);
|
|
|
|
bValue = Number.parseFloat(bValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isDescending) {
|
|
|
|
const temporary = aValue;
|
|
|
|
aValue = bValue;
|
|
|
|
bValue = temporary;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof aValue === 'string') {
|
|
|
|
return aValue.localeCompare(bValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
return aValue < bValue ? -1 : (aValue > bValue ? 1 : 0);
|
|
|
|
});
|
|
|
|
|
2021-12-03 16:14:04 +01:00
|
|
|
for (const cell of cells) {
|
2021-11-30 16:44:53 +01:00
|
|
|
const row = $(cell)
|
|
|
|
.parent();
|
2021-11-23 15:34:34 +01:00
|
|
|
row.appendTo(row.parent());
|
|
|
|
}
|
2021-12-03 15:24:06 +01:00
|
|
|
|
|
|
|
this.hideProgress();
|
2021-11-23 15:34:34 +01:00
|
|
|
}
|
2021-11-30 19:51:33 +01:00
|
|
|
|
|
|
|
onPaginationSelected(event: Event) {
|
|
|
|
this.rowsPerPage.value = $(event.target).find('mwc-list-item').eq(event.detail.index).val();
|
|
|
|
this.rowsPerPage.currentStart = 0;
|
|
|
|
this.rowsPerPage.currentEnd = this.rowsPerPage.value;
|
|
|
|
m.redraw();
|
|
|
|
}
|
|
|
|
|
|
|
|
onPaginationButtonClicked(event: Event) {
|
|
|
|
const button: Cash = $(event.target);
|
|
|
|
this.paginate(button.data('page'));
|
|
|
|
m.redraw();
|
|
|
|
}
|
|
|
|
|
|
|
|
paginate(action: 'first' | 'next' | 'previous' | 'last') {
|
|
|
|
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: Cash = $(this.element).find('.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');
|
|
|
|
buttonElement.prop('disabled', disabled[buttonAction]);
|
|
|
|
}
|
|
|
|
}
|
2021-08-26 20:09:28 +02:00
|
|
|
}
|