Files
david/src/widgets/table/Table.js
T
2026-07-06 19:23:12 +03:00

700 lines
12 KiB
JavaScript

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' }, '&lt') );
}
if (this.data.after) {
this.paginationEl.append( El('a', { 'data-id': this.data.after, className: 'btn-after' }, '&gt;') );
}
}
}
// 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);
}
}