@digital-blueprint/lunchlottery-app
Version:
[GitHub Repository](https://github.com/digital-blueprint/lunchlottery-app) | [npmjs package](https://www.npmjs.com/package/@digital-blueprint/lunchlottery-app) | [Unpkg CDN](https://unpkg.com/browse/@digital-blueprint/lunchlottery-app/)
775 lines (675 loc) • 26.8 kB
JavaScript
import {createInstance} from './i18n.js';
import {html, css, unsafeCSS} from 'lit';
import {ScopedElementsMixin, LangMixin, getIconSVGURL} from '@dbp-toolkit/common';
import * as commonStyles from '@dbp-toolkit/common/styles';
import {TabulatorFull as Tabulator} from 'tabulator-tables';
import * as commonUtils from '@dbp-toolkit/common/utils';
import * as tabulatorStyles from '@dbp-toolkit/tabulator-table/src/tabulator-table-styles';
import {name as pkgName} from '@dbp-toolkit/tabulator-table/package.json';
import {classMap} from 'lit/directives/class-map.js';
import DBPLitElement from '@dbp-toolkit/common/dbp-lit-element';
import {downloadExcel, generatePDFDownload} from './utils.js';
import {sendNotification} from '@dbp-toolkit/common';
export class TabulatorTable extends LangMixin(ScopedElementsMixin(DBPLitElement), createInstance) {
constructor() {
super();
// Tabulator table id
this.identifier = 'table';
// Web Component id
this.id = '';
/** @type {import('tabulator-tables').Options} */
this.options = {
layout: 'fitColumns',
autoColumns: true,
};
this.data = [];
this.paginationEnabled = false;
this.paginationSize = 10;
this.stickyHeaderEnabled = false;
this.selectRowsEnabled = false;
this.rowSelected = false;
this.selectedRows = null;
this.tableReady = false;
this.initialization = true;
this.collapseEnabled = false;
this.isCollapsible = false;
}
static get properties() {
return {
...super.properties,
id: {type: String, reflect: true},
identifier: {type: String, attribute: 'identifier'},
options: {type: Object, attribute: 'options'},
data: {type: Array, attribute: 'data'},
paginationNoLangsEnabled: {type: Boolean, attribute: 'pagination-no-langs-enabled'},
paginationEnabled: {type: Boolean, attribute: 'pagination-enabled'},
paginationSize: {type: Number, attribute: 'pagination-size'},
stickyHeaderEnabled: {type: Boolean, attribute: 'sticky-header'},
rowSelected: {type: Boolean},
selectedRows: {type: Array},
selectRowsEnabled: {type: Boolean, attribute: 'select-rows-enabled'},
collapseEnabled: {type: Boolean, attribute: 'collapse-enabled'},
expanded: {type: Boolean},
isCollapsible: {type: Boolean, attribute: false},
};
}
update(changedProperties) {
super.update(changedProperties);
changedProperties.forEach((oldValue, propName) => {
if (propName === 'lang') {
if (this.tabulatorTable) {
this.tabulatorTable.setLocale(this.lang);
}
} else if (
propName === 'options' &&
this.options !== null &&
!this.tableReady &&
!this.initialization
) {
this.buildTable();
}
});
}
connectedCallback() {
super.connectedCallback();
this.updateComplete.then(() => {
this.initialization = false;
});
}
disconnectedCallback() {
if (this.tabulatorTable) {
this.tabulatorTable.off('tableBuilt');
this.tabulatorTable.off('rowClick');
this.tabulatorTable.off('columnVisibilityChanged');
this.tabulatorTable.off('pageLoaded');
}
super.disconnectedCallback();
}
buildTable() {
if (this.collapseEnabled) {
// this.options['layout'] = 'fitDataFill';
this.options['responsiveLayout'] = 'collapse';
}
if (this.paginationNoLangsEnabled) {
let paginationElement = this._('.tabulator-paginator');
if (paginationElement) {
while (paginationElement.firstChild) {
paginationElement.removeChild(paginationElement.firstChild);
}
}
//this.options['autoColumns'] = true;
this.options['pagination'] = true;
this.options['paginationSize'] = this.paginationSize;
this.options['paginationSizeSelector'] = true;
this.options['footerElement'] = '';
this.options['paginationElement'] = paginationElement;
}
if (this.paginationEnabled) {
let paginationElement = this._('.tabulator-paginator');
if (paginationElement) {
while (paginationElement.firstChild) {
paginationElement.removeChild(paginationElement.firstChild);
}
}
this.options['pagination'] = true;
this.options['paginationSize'] = this.paginationSize;
this.options['paginationSizeSelector'] = true;
this.options['paginationElement'] = paginationElement;
this.options['langs']['en']['pagination'] = {
page_size: 'Page size',
page_size_title: 'Page size',
first: '<span class="mobile-hidden"> << </span>',
first_title: 'First Page',
last: '<span class="mobile-hidden"> >> </span>',
last_title: 'Last Page',
prev: '<span class="mobile-hidden"> < </span>',
prev_title: 'Prev Page',
next: '<span class="mobile-hidden"> > </span>',
next_title: 'Next Page',
};
this.options['langs']['de']['pagination'] = {
page_size: 'Einträge pro Seite',
page_size_title: 'Einträge pro Seite',
first: '<span class="mobile-hidden"> << </span>',
first_title: 'Erste Seite',
last: '<span class="mobile-hidden"> >> </span>',
last_title: 'Letzte Seite',
prev: '<span class="mobile-hidden"> < </span>',
prev_title: 'Vorherige Seite',
next: '<span class="mobile-hidden"> > </span>',
next_title: 'Nächste Seite',
};
}
if (this.selectRowsEnabled) {
this.options['selectableRows'] = true;
}
// Set this.data if data is provided in the options
if (this.options.data) {
this.data = this.options.data;
}
if (this._(`#${this.identifier}`)) {
/** @type {import('tabulator-tables').Tabulator} */
this.tabulatorTable = new Tabulator(this._('#' + this.identifier), this.options);
}
console.log('TABULATOR TABLE INITIALIZED', this.tabulatorTable);
this.tabulatorTable.on('tableBuilt', this.tableBuildFunctions.bind(this));
this.tabulatorTable.on('rowClick', this.rowClickFunction.bind(this));
this.tabulatorTable.on('rowSelectionChanged', (data, rows, selected, deselected) => {
const allSelectedRows = this.tabulatorTable.getSelectedRows();
const collapseEvent = new CustomEvent(
'dbp-tabulator-table-row-selection-changed-event',
{
detail: {
selected: selected,
deselected: deselected,
allselected: allSelectedRows,
rows: rows,
data: data,
},
bubbles: true,
composed: true,
},
);
this.dispatchEvent(collapseEvent);
});
this.tabulatorTable.on('columnVisibilityChanged', (column) => {
const columnDefinition = column.getDefinition();
const columnVisibility = column.isVisible();
if (columnDefinition.formatter === 'responsiveCollapse') {
if (columnVisibility === true) {
this.isCollapsible = true;
} else {
this.isCollapsible = false;
}
const collapseEvent = new CustomEvent('dbp-tabulator-table-collapsible-event', {
detail: {
tableId: this.identifier,
isCollapsible: this.isCollapsible,
},
bubbles: true,
composed: true,
});
this.dispatchEvent(collapseEvent);
}
});
/**
* Pagination event pageLoaded
* Whenever a page has been loaded, the pageLoaded event is called, passing the current page number as an argument.
*/
this.tabulatorTable.on('pageLoaded', (pageno) => {
const pageLoadedEvent = new CustomEvent('dbp-tabulator-table-page-loaded-event', {
detail: {
tableId: this.identifier,
pageSize: pageno,
},
bubbles: true,
composed: true,
});
this.dispatchEvent(pageLoadedEvent);
});
this.tabulatorTable.on('renderComplete', () => {
const renderCompleteEvent = new CustomEvent(
'dbp-tabulator-table-render-complete-event',
{
detail: {
tableId: this.identifier,
},
bubbles: true,
composed: true,
},
);
this.dispatchEvent(renderCompleteEvent);
});
this.tableReady = true;
}
tableBuildFunctions() {
if (!this.tabulatorTable || !this.tabulatorTable.initialized) return;
this.tabulatorTable.setLocale(this.lang);
if (Array.isArray(this.data) && this.data.length > 0) {
this.tabulatorTable.setData(this.data);
}
/**
* Change cursor to pointer on hover if rows are selectable
*/
if (this.selectRowsEnabled) {
this.tabulatorTable.on('rowMouseOver', function (e, row) {
this.rowManager.element.classList.add('pointer-mouse');
});
}
// Handle pagination size changes
if (this.paginationEnabled) {
const paginationSizeDropdown = this._('#custom-pagination .tabulator-page-size');
const paginationSize = parseInt(
localStorage.getItem(`tabulator-${this.identifier}-pagination-size`),
);
if (paginationSize) {
this.paginationSize = paginationSize;
this.tabulatorTable.setPageSize(this.paginationSize);
}
paginationSizeDropdown.addEventListener('change', (event) => {
if (event.target.value) {
localStorage.setItem(
`tabulator-${this.identifier}-pagination-size`,
event.target.value,
);
}
});
}
const tableBuiltEvent = new CustomEvent('dbp-tabulator-table-built', {
detail: {
id: this.identifier,
tabulator: this.tabulatorTable,
},
bubbles: true,
composed: true,
});
this.dispatchEvent(tableBuiltEvent);
}
rowClickFunction(e, row) {
if (!this._('#select_all') || !this.tabulatorTable) return;
const check =
this.tabulatorTable.getSelectedRows().length ===
this.tabulatorTable.getRows('display').length;
/** @type {HTMLInputElement} */ (this._('#select_all')).checked = check;
if (this.tabulatorTable.getSelectedRows().length === 0) this.rowSelected = false;
else this.rowSelected = true;
}
deleteRow(row) {
if (!this.tabulatorTable) return;
this.tabulatorTable.deleteRow(row);
}
/**
* Select all rows from tabulator table
*
*/
selectAllRows() {
if (!this.tabulatorTable) return;
let allSelected = this.checkAllSelected();
if (!allSelected) {
this.tabulatorTable.getRows().forEach((row) => row.select());
}
}
/**
* Select all visible rows from tabulator table
*
*/
selectAllVisibleRows() {
if (!this.tabulatorTable) return;
this.tabulatorTable.getRows('visible').forEach((row) => row.select());
}
/**
* Deselect all rows from tabulator table
*
*/
deselectAllRows() {
if (!this.tabulatorTable) return;
let noneSelected = this.checkNoneSelected();
if (!noneSelected) {
this.tabulatorTable.getSelectedRows().forEach((row) => row.deselect());
}
}
checkAllSelected() {
if (this.tabulatorTable) {
let maxSelected = this.tabulatorTable.getRows('display').length;
let selected = this.tabulatorTable.getSelectedRows().length;
if (selected === maxSelected) {
return true;
}
}
return false;
}
checkNoneSelected() {
if (this.tabulatorTable) {
let selected = this.tabulatorTable.getSelectedRows().length;
if (selected === 0) {
return true;
}
}
return false;
}
setData(data) {
if (!this.tabulatorTable) return;
this.data = data;
this.tabulatorTable.setData(this.data);
}
clearData() {
if (!this.tabulatorTable) return;
this.tabulatorTable.clearData();
}
addData(data) {
if (!this.tabulatorTable) return;
this.tabulatorTable.addData(data, false);
}
setFilter(listOfFilters) {
if (!this.tabulatorTable) return;
if (listOfFilters.length === 0) this.tabulatorTable.clearFilter(false);
else this.tabulatorTable.setFilter(listOfFilters);
}
clearFilter() {
if (!this.tabulatorTable) return;
this.tabulatorTable.clearFilter(false);
}
getSelectedRows() {
if (!this.tabulatorTable) return;
this.selectedRows = this.tabulatorTable.getSelectedRows();
return this.selectedRows;
}
deleteSelectedRows() {
if (!this.tabulatorTable) return;
this.getSelectedRows();
this.tabulatorTable.deleteRow(this.selectedRows);
}
getColumns() {
if (!this.tabulatorTable) return;
return this.tabulatorTable.getColumns();
}
setColumns(newColumns) {
if (!this.tabulatorTable) return;
this.tabulatorTable.setColumns(newColumns);
}
getColumnDefinitions() {
if (!this.tabulatorTable) return;
return this.tabulatorTable.getColumnDefinitions();
}
getColumnsFields() {
if (!this.tabulatorTable) return;
let columns = this.tabulatorTable.getColumns();
let columns_titles = [];
for (let col of columns) {
columns_titles.push(col.getField());
}
return columns_titles;
}
getRows() {
if (!this.tabulatorTable) return;
let rows = this.tabulatorTable.getRows();
return rows;
}
getData() {
if (!this.tabulatorTable) return;
let data = this.tabulatorTable.getData();
return data;
}
updateRow(row, newData) {
if (!this.tabulatorTable) return;
row.update(newData);
}
getPage() {
if (!this.tabulatorTable) return;
let currentPage = this.tabulatorTable.getPage();
return currentPage;
}
getLang() {
if (!this.tabulatorTable) return;
let currentLang = this.tabulatorTable.getLang();
return currentLang;
}
setPage(currentPage) {
if (!this.tabulatorTable) return;
this.tabulatorTable.setPage(currentPage);
}
expandAll() {
if (!this.tabulatorTable) return;
this.tabulatorTable.getRows('visible').forEach((row) => {
let config = row._row.modules.responsiveLayout;
config.open = true;
const item = /** @type {HTMLElement} */ (row.getElement().lastChild);
if (item.classList.contains('tabulator-responsive-collapse')) {
item.style.display = 'block';
}
row.getElement()
.getElementsByClassName('tabulator-responsive-collapse-toggle')[0]
.classList.add('open');
});
const that = this;
setTimeout(function () {
that.tabulatorTable.redraw();
}, 0);
}
collapseAll() {
this.tabulatorTable.getRows('visible').forEach((row) => {
let config = row._row.modules.responsiveLayout;
config.open = false;
const item = /** @type {HTMLElement} */ (row.getElement().lastChild);
if (item.classList.contains('tabulator-responsive-collapse')) {
item.style.display = 'none';
}
row.getElement()
.getElementsByClassName('tabulator-responsive-collapse-toggle')[0]
.classList.remove('open');
});
const that = this;
setTimeout(function () {
that.tabulatorTable.redraw();
}, 0);
}
async download(type, dataName) {
if (!this.tabulatorTable) {
return;
}
const active_rows = this.tabulatorTable.getRows('active');
if (active_rows.length === 0) return;
const selected_rows = this.tabulatorTable.getSelectedRows();
const hasSelection = selected_rows.length > 0;
const rows = hasSelection ? selected_rows : active_rows;
const data = hasSelection
? selected_rows.map((row) => row.getData())
: this.tabulatorTable.getData();
const downloadMode = hasSelection ? 'selected' : 'all';
let hasError = false;
try {
switch (type) {
case 'csv':
case 'json':
case 'html':
this.tabulatorTable.download(type, dataName + '.' + type, {}, downloadMode);
break;
case 'xlsx':
await downloadExcel(rows, dataName);
break;
case 'pdf':
await generatePDFDownload(this.tabulatorTable, data, dataName);
break;
}
} catch (error) {
hasError = true;
console.error('Download failed:', error);
sendNotification({
summary: this._i18n.t('tabulator-table.error-title'),
body: this._i18n.t('tabulator-table.download-error-message'),
type: 'danger',
timeout: 0,
});
}
if (!hasError) {
sendNotification({
summary: this._i18n.t('tabulator-table.success-title'),
body: this._i18n.t('tabulator-table.download-success-message'),
type: 'success',
timeout: 5,
});
}
}
static get styles() {
// language=css
return css`
${commonStyles.getThemeCSS()}
${commonStyles.getGeneralCSS()}
${commonStyles.getRadioAndCheckboxCss()}
${tabulatorStyles.getTabulatorStyles()}
.tabulator .tabulator-header .tabulator-col .tabulator-col-title {
padding-top: 4px;
padding-bottom: 4px;
font-weight: normal;
font-size: 1rem;
}
.tabulator .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title {
padding-top: 4px;
padding-bottom: 4px;
font-weight: normal;
font-size: 1rem;
}
.tabulator .tabulator-header .tabulator-header-contents .tabulator-headers {
min-height: 37px;
}
.tabulator .tabulator-tableholder :hover {
cursor: default;
}
.tabulator .tabulator-tableholder.pointer-mouse :hover {
cursor: pointer;
}
.tabulator .tabulator-footer .tabulator-paginator .tabulator-page[disabled] {
opacity: 0.4;
}
.tabulator .tabulator-footer {
background-color: var(--dbp-background);
color: var(--dbp-content);
border-top: none;
}
.tabulator .tabulator-footer .tabulator-footer-contents {
flex-direction: column;
}
.tabulator .tabulator-footer .tabulator-paginator {
flex-direction: row;
display: flex;
align-items: center;
margin-top: 10px;
}
.tabulator .tabulator-footer .tabulator-paginator > label {
padding-right: 10px;
}
.tabulator .tabulator-footer .tabulator-paginator label {
color: var(--dbp-content);
font-weight: 400;
}
.tabulator .tabulator-footer .tabulator-paginator .tabulator-page-size {
box-sizing: border-box;
color: var(--dbp-content);
background-color: var(--dbp-background);
border: var(--dbp-border);
border-radius: var(--dbp-border-radius);
padding: calc(0.5em - 1px) 1.7em calc(0.5em - 1px) 0.75em;
cursor: pointer;
background-position-x: calc(100% - 0.4rem);
background-size: auto 45%;
min-height: 40px;
}
.tabulator .tabulator-footer .tabulator-paginator .tabulator-page {
opacity: unset;
border-radius: var(--dbp-border-radius);
cursor: pointer;
padding: calc(0.375em - 1px) 0.75em;
text-align: center;
white-space: nowrap;
font-size: inherit;
font-weight: bolder;
font-family: inherit;
transition:
all 0.15s ease 0s,
color 0.15s ease 0s;
background: var(--dbp-secondary-surface);
color: var(--dbp-on-secondary-surface);
border-color: var(--dbp-secondary-surface-border-color);
box-sizing: border-box;
min-height: 40px;
}
.tabulator .tabulator-footer .tabulator-paginator .tabulator-page.active {
background: var(--dbp-on-secondary-surface);
color: var(--dbp-secondary-surface);
}
.filename {
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
white-space: nowrap;
}
only screen and (orientation: portrait) and (max-width: 768px) {
.mobile-hidden {
display: none;
}
#custom-pagination,
.tabulator-footer {
position: sticky;
bottom: 0;
z-index: 10;
}
.tabulator {
overflow: visible;
}
.tabulator .tabulator-footer .tabulator-paginator .tabulator-page {
border: none;
}
.tabulator
.tabulator-footer
.tabulator-footer-contents
.tabulator-paginator
.tabulator-pages {
display: none;
}
.tabulator .tabulator-footer .tabulator-paginator {
text-align: center;
}
.tabulator .tabulator-footer .tabulator-paginator label {
display: none;
}
.tabulator .tabulator-footer .tabulator-paginator .tabulator-page {
border: none;
}
.tabulator .tabulator-footer .tabulator-paginator .tabulator-page-size {
padding-right: 1.5em;
background-size: auto 40%;
}
button[data-page='prev']::after,
button[data-page='next']::after,
button[data-page='first']::after,
button[data-page='last']::after {
content: '\\00a0\\00a0\\00a0\\00a0\\00a0\\00a0\\00a0';
background-color: var(--dbp-content);
mask-repeat: no-repeat;
mask-position: center center;
padding: 0 0 0.25%;
mask-size: 1.4rem !important;
}
button[data-page='first']::after {
content: '\\00a0\\00a0\\00a0\\00a0\\00a0\\00a0\\00a0';
mask-image: url('${unsafeCSS(getIconSVGURL('angle-double-left'))}');
}
button[data-page='prev']::after {
mask-image: url('${unsafeCSS(getIconSVGURL('chevron-left'))}');
}
button[data-page='next']::after {
mask-image: url('${unsafeCSS(getIconSVGURL('chevron-right'))}');
}
button[data-page='last']::after {
content: '\\00a0\\00a0\\00a0\\00a0\\00a0\\00a0\\00a0';
mask-image: url('${unsafeCSS(getIconSVGURL('angle-double-right'))}');
}
}
`;
}
render() {
const tabulatorCss = commonUtils.getAssetURL(
pkgName,
'tabulator-tables/css/tabulator.min.css',
);
return html`
<div class="wrapper">
<link rel="stylesheet" href="${tabulatorCss}" />
<div class="table-wrapper">
<div
id=${this.identifier}
class="${classMap({'sticky-header': this.stickyHeaderEnabled})}"></div>
<div
class="tabulator ${classMap({hidden: !this.paginationEnabled})}"
id="custom-pagination">
<div class="tabulator-footer">
<div class="tabulator-footer-contents">
<span class="tabulator-paginator"></span>
</div>
</div>
</div>
</div>
</div>
`;
}
}