UNPKG

@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
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; } @media 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> `; } }