import './table.css'; import { Ut } from '../../utils/Ut'; import { $, El, fillData } from '../../utils/dom'; import { FormValidator } from '../../validator/FormValidator'; import { Rc } from '../../core/Rc'; import { Render } from '../../core/Render'; export class Table { static CLEAN = 0; static APPEND = 1; static PREPEND = 2; static UPDATE = 3; /** * Table, Constructor */ constructor(target, caller, opts) { this.target = target; this.caller = caller; this.vars = new FormData(); this.reqOpts = {}; this.vars.set('pg', 1); this.opts = { ... { layout : 'auto', filter : false, header : {}, label : '%rows results', sortable : 0 }, ... opts, ... opts.children }; this.rows = 0; this.setHeader = true; this.data = {}; if (!this.opts.header.cell || this.opts.display == 'none') { this.setHeader = false; } this.makeUiTable(); if (this.opts.filter) { // create filter form this.makeFilterForm(); } if (Ut.isSet(this.opts.toolbar_header)) { // create toolbar header this.makeToolbarHeader(); } if (this.opts.label) { // create info label this.makeInfoLabel(); } this.makeNewTable(); this.makePagination(); if (Ut.isSet(this.opts.toolbar_footer)) { // create toolbar footer this.makeToolbarFooter(); } this.setMode(Table.CLEAN); if ( Ut.isFn(caller) ) { // Data Mode this.makeRequest(); } else { this.setTableData(caller); } if (this.opts.sortable) { this.setSortable(); } this.setTableEvents(); Ut.trigger(this.opts.onInit, this); } setMode(mode) { this.mode = mode; if (mode == Table.CLEAN) { this.id = 0; } } setTableData(data) { this.data = data; this.rows = parseInt(this.data.count); this.updateInfoRows(); if (this.mode == Table.CLEAN) { this.table.dxHtml(''); if (this.rows) { this.makeTableHead(); } } this.makeTableRows(); this.updatePagination(); Ut.trigger(this.opts.onUpdate, this); } makeInfoLabel() { this.info = El('div', { className: 'sp-table-info' }); this.ui.append(this.info); } // Make UI container makeUiTable() { this.ui = El('div', { className: 'sp-table-ui' }); this.target.append(this.ui); } // Make filter form makeFilterForm() { let that = this; this.filterForm = El('form', { novalidate: true, className: 'sp-form filter-main' }); Render.makeUiElements(Ut.objVal(this.opts.filter).children, this.filterForm); this.ui.append(this.filterForm); this.filterForm.dxOn('changed clear', 'input[type="text"]', () => that.filterForm.dxTrigger('submit.form')); this.filterForm.dxOn('change', 'select, input[type="radio"], input[type="checkbox"]', () => that.filterForm.dxTrigger('submit.form')); new FormValidator(this.filterForm, { onInit: el => that.filterFormEl = el, onSubmit: function(vars) { that.vars = vars; that.vars.set('pg', 1); that.setMode(Table.CLEAN); that.makeRequest(); } }); fillData(that.filterFormEl, this.vars); } // Make header toolbar makeToolbarHeader() { this.toolbarHeader = El('div', { className: 'sp-toolbar toolbar-header' + (this.opts.toolbar_header.cls ? ' ' + this.opts.toolbar_header.cls : '') }); Render.makeUiElements(Ut.objVal(this.opts.toolbar_header).children, this.toolbarHeader); this.ui.append( this.toolbarHeader ); Ut.trigger(this.opts.toolbarHeader, this); } // Make Pagination Area makePagination() { this.paginationEl = this.ui.appendChild(El('div', { className: 'sp-pagination' })); this.paginationEl.dxOn('click', 'a', (e, t) => { this.vars.set('pg', t.dataset.id); this.setMode(Table.CLEAN); this.makeRequest(); }); this.paginationEl.dxOn('click', 'a.btn-before', (_, t) => { this.vars.set('before', this.data.before); this.vars.set('after', ''); this.setMode(Table.CLEAN); this.makeRequest(); }); this.paginationEl.dxOn('click', 'a.btn-after', function() { this.vars.set('before', ''); this.vars.set('after', this.data.after); this.setMode(Table.CLEAN); this.makeRequest(); }); } // Make footer toolbar makeToolbarFooter() { this.toolbarFooter = El('div', { className: 'sp-toolbar toolbar-footer' + (this.opts.toolbar_footer.cls ? ' ' + this.opts.toolbar_footer.cls : '') }); Render.makeUiElements(Ut.objVal(this.opts.toolbar_footer).children, this.toolbarFooter); this.ui.append(this.toolbarFooter); Ut.trigger(this.opts.toolbarFooter, this); } // Make new ui table makeNewTable() { this.table = El('div', { className: 'sp-table layout-' + this.opts.layout + (!this.setHeader ? ' no-header' : '') + (this.opts.cls ? ' ' + this.opts.cls : '')}); this.ui.append(this.table); } // Make table head makeTableHead() { if (!this.setHeader) { return; } const header = this.opts.header; const thead = El('div', { className: 'thead' + (header.cls ? ' ' + header.cls : '') }); let n = 1; for (; n < this.data.items[0].length; n++) { const cell = header.cell ? (header.cell[ n - 1 ] || {}) : {}; const th = El('div'); th.className = 'th'; if (cell.cls) { th.className += ` ${cell.cls}`; } if (cell.sortable) { const sortMode = this.vars.get('sort_mode'); if (this.vars.get('sort_by') === cell.sortable && (sortMode === 'asc' || sortMode === 'desc')) { th.className += ` sort-${sortMode}`; } th.dataset.sortable = cell.sortable; } th.textContent = cell.text || ''; thead.append(th); } this.table.prepend(thead); } // Make table rows makeTableRows() { let that = this; if (this.mode == Table.UPDATE) { let row = this.getRow(this.id); if (row) { row.dxHtml(''); let data = this.data.items[0]; let n = 1; for (; n < data.length; n++) { let cell = that.opts.header.cell ? (that.opts.header.cell[ n - 1 ] || {}) : {}; let column = El('div', { className: 't-col' }); row.append( El('div', { className: 'td' + (cell.cls ? ' ' + cell.cls : ''), 'data-label': cell.text }, column )); if (data[n]) { column.dxHtml(data[n]); } if (n == 1 && that.opts.sortable) { column.classList.add('flex', 'v-center'); column.prepend( El('i', { className: 'handle' }) ); } } } } else { this.data.items.forEach((data, i) => { let tbody = El('div', { className: 'tbody' + (that.opts.body && that.opts.body.cls ? ' ' + that.opts.body.cls : ''), 'data-id': data[0], 'data-pos': i }); let n = 1; for (; n < data.length; n++) { let cell = that.opts.header.cell ? (that.opts.header.cell[ n - 1 ] || {}) : {}; let column = El('div', { className: 't-col' }); tbody.append( El('div', { className: 'td' + (cell.cls ? ' ' + cell.cls : ''), 'data-label': cell.text }, column )); if (data[n]) { column.dxHtml(data[n]); } if (n == 1 && that.opts.sortable) { column.classList.add('flex', 'v-center'); column.prepend( El('i', { className: 'handle' }) ); } if (that.mode == Table.PREPEND) { that.table.prepend(tbody); } else { that.table.append(tbody); } } }); } } // Set info rows updateInfoRows() { this.setInfo(this.opts.label.replace(/%rows/g, this.rows)); } // XHR Request makeRequest() { let that = this; let vars = {}; if (this.mode == Table.UPDATE) { vars.id = this.id; } else { vars = this.vars; vars.set('id', 0); } this.reqOpts.vars = vars; this.caller(this.reqOpts).then(jr => { if (jr.code != Rc.DONE) { that.setInfo('No Data'); return; } that.setTableData(jr.data); }).catch(error => console.error(error)); } // Set ui sortable setSortable() { let that = this, isHandle = false, toDrag; let isBefore = function(a, b) { if (a.parentNode == b.parentNode) { for (let cur = a; cur; cur = cur.previousSibling) { if (cur === b) { return true; } } } return false; } this.table.dxOn('mousedown mouseup', 'i.handle', function(e) { let parent = this.dxParents('.tbody'); if (e.type == 'mousedown') { isHandle = true; parent.setAttribute('draggable', true); } else { isHandle = false; parent.removeAttribute('draggable'); } }); this.table.dxOn('dragenter', '.tbody', function(e) { if (!isHandle) { return; } let target = this.matches('.tbody') ? this : this.dxParents('.tbody'); if (isBefore( toDrag, target )) { target.before(toDrag); } else { if (target.nextElementSibling === null) { $('.sp-table').append( toDrag ); } else { target.nextElementSibling.before(toDrag); } } }); this.table.dxOn('dragstart', '.tbody', function(e) { if (!isHandle) { return; } toDrag = this; e.dataTransfer.setData('text/plain', null); e.dataTransfer.effectAllowed = 'move'; toDrag.style.opacity = 0.4; }); this.table.dxOn('dragend', '.tbody', function(e) { if (!isHandle) { return; } isHandle = false; let onSort = false; toDrag.style.opacity = ''; toDrag.removeAttribute('draggable'); let data = {}; that.table.dxFind('.tbody', (el, i) => { if (el.dataset.pos != i && !onSort) { onSort = true; } if (el.dataset.pos != i) { data[ el.dataset.id ] = i; el.dataset.pos = i; } }); if (onSort) { Ut.trigger(that.opts.onSort, that, data); onSort = false; } }); } // Set Table Events setTableEvents() { this.table.dxOn('click', '.th[data-sortable]', (_, t) => { this.vars.set('sort_by', t.dataset.sortable); if (!t.classList.contains('sort-asc')) { t.className = 'th sort-asc'; this.vars.set('sort_mode', 'asc'); this.update(); return; } t.className = 'th sort-desc'; this.vars.set('sort_mode', 'desc'); this.update(); });; } // Update pagination area updatePagination() { this.paginationEl.dxHtml(''); if (this.data.pagination && this.data.pagination.count > 1) { let pg_btns = [], pg_btn, j = 0; if (this.data.pagination.start > 1) { pg_btns[j] = [ 1, '<<', false ]; j = 1; } for (let i = this.data.pagination.start; i <= this.data.pagination.end; i++) { pg_btns[j] = [ i, i, (this.vars.get('pg') == i) ] j++; } if (this.data.pagination.start > 1 && this.data.pagination.page != this.data.pagination.end) { pg_btns[j] = [ this.data.pagination.count, '>>', false ]; } pg_btns.forEach((dat) => { pg_btn = El('a', { 'data-id': dat[0], className: (dat[2] ? 'disabled' : '')}, dat[1] ); this.paginationEl.append(pg_btn); }); } else if (this.data.before || this.data.after) { // update UI if (this.data.before) { this.paginationEl.append( El('a', { 'data-id': this.data.before, className: 'btn-before' }, '<') ); } if (this.data.after) { this.paginationEl.append( El('a', { 'data-id': this.data.after, className: 'btn-after' }, '>') ); } } } // Get All Rows getRows(id) { return this.table.dxFind('.tbody'); } // Get Row By ID getRow(id) { return this.table.dxFind('.tbody[data-id="'+ id +'"]'); } // Update Row Data updateRow(id, data) { this.setMode(Table.UPDATE); this.id = id; if (data) { this.setTableData(data); } else { this.makeRequest(); } } // Update All Rows update(data) { this.setMode(Table.CLEAN); if (data) { this.setTableData(data); } else { this.makeRequest(); } } // Delete Row deleteRow(id) { let row = this.getRow(id); if (row) { this.rows--; this.updateInfoRows(); row.remove(); if (!this.rows) { let pg = this.vars.get('pg'); if (pg > 1) { this.vars.set('pg', --pg); } this.update(); } Ut.trigger(this.opts.onUpdate, this); } } // Add Row At The End of the table appendRow(data) { this.setMode(this.table.children.length ? Table.APPEND : Table.CLEAN); if (data) { this.setTableData(data); } else { this.makeRequest(); } } // Add Row At Begining of the table prependRow(data) { this.setMode(this.table.children.length ? Table.APPEND : Table.CLEAN); if (data) { this.setTableData(data); } else { this.makeRequest(); } } // Set Info Table setInfo(text) { this.info.textContent = text; }; // Set Toolbar Header setHeaderToolbar(content) { this.toolbarHeader.dxHtml(content); } // Set Toolbar Footer setFooterToolbar(content) { this.toolbarFooter.dxHtml(content); } }