class TableHeader {
constructor(columns) {
this.columns = columns;
}
render() {
const tr = document.createElement('tr');
const colgroup = document.createElement('colgroup');
this.columns.forEach((column) => {
const th = document.createElement('th');
const col = document.createElement('col');
th.setAttribute('data-column', column.column);
col.setAttribute('data-column', column.column);
th.innerHTML = column.label;
tr.appendChild(th);
colgroup.appendChild(col);
});
const thead = document.createElement('thead');
thead.appendChild(tr);
return {colgroup : colgroup, thead : thead };
}
}
class ActionSet {
arrowDown = '';
constructor(actions, element = 'button', value = null) {
this.actions = actions;
this.element = element;
this.value = value;
}
render() {
if (this.element == 'button') {
return this.renderButton();
}
if (this.element == 'span') {
return this.renderSpan();
}
}
renderButton() {
this.actionButton = document.createElement('a');
this.actionButton.classList.add('btn', 'btn-outline-secondary');
this.actionButton.innerHTML = ` ${this.actions[0].label}`;
this.actionButton.href = this.actions[0].route.replace('{{id}}', this.value);
if (this.actions.length == 1) {
return { 'button': this.actionButton, 'toggle': null, 'list': null };
}
this.actionToggle = document.createElement('button');
this.actionToggle.classList.add('btn', 'btn-outline-secondary', 'dropdown-toggle-split');
this.actionToggle.innerHTML = this.arrowDown;
this.actionToggle.setAttribute('data-bs-toggle', 'dropdown');
this.actionList = document.createElement('ul');
this.actionList.classList.add('dropdown-menu');
this.actions.forEach((action) => {
const actionHolder = document.createElement('li');
const actionLink = document.createElement('a');
actionLink.classList.add('dropdown-item');
actionLink.href = action.route.replace('{{id}}', this.value);
actionLink.innerHTML = ` ${action.label}`;
actionHolder.appendChild(actionLink);
this.actionList.appendChild(actionHolder);
});
return {
'button': this.actionButton,
'toggle': this.actionToggle,
'list': this.actionList
};
}
renderSpan() {
this.actionButton = document.createElement('div');
this.actionToggle = document.createElement('span');
this.actionToggle.setAttribute('data-bs-toggle', 'dropdown');
this.actionToggle.innerHTML = `#${this.value} ${this.arrowDown}`;
this.actionList = document.createElement('ul');
this.actionList.classList.add('dropdown-menu');
this.actions.forEach((action) => {
const actionHolder = document.createElement('li');
if(action.type == 'divider'){
const actionDivider = document.createElement('hr');
actionDivider.classList.add('dropdown-divider');
this.actionList.appendChild(actionDivider);
return;
}
const actionLink = document.createElement('a');
actionLink.classList.add('dropdown-item');
actionLink.href = action.route.replace('{{id}}', this.value);
actionLink.innerHTML = ` ${action.label}`;
actionHolder.appendChild(actionLink);
this.actionList.appendChild(actionHolder);
});
this.actionButton.appendChild(this.actionToggle);
this.actionButton.appendChild(this.actionList);
return this.actionButton;
}
renderLabel(label, icon){
if(icon){
return ` ${label}`
}
return label
}
}
class TableBody {
constructor(rows, columns, rowActions, options) {
this.rows = rows;
this.columns = columns;
this.rowActions = rowActions;
this.options = options;
}
render() {
if(this.options.selectable){
return this.renderSelectable();
}
return this.renderDefaultTable();
}
renderDefaultTable(){
const tbody = document.createElement('tbody');
this.rows.forEach((row) => {
const tr = document.createElement('tr');
this.columns.forEach((column) => {
const td = document.createElement('td');
td.setAttribute('data-name', column.column);
td.setAttribute('data-value', row[column.column]);
if (column.column == 'id') {
tr.setAttribute('data-value', row[column.column]);
const idColumn = new ActionSet(this.rowActions, 'span', row[column.column]);
td.appendChild(idColumn.render());
} else {
td.innerHTML = row[column.column];
}
tr.appendChild(td);
});
tbody.appendChild(tr);
});
return tbody;
}
renderSelectable(){
const tbody = document.createElement('tbody');
this.rows.forEach((row) => {
const tr = document.createElement('tr');
this.columns.forEach((column) => {
const td = document.createElement('td');
td.setAttribute('data-name', column.column);
td.setAttribute('data-value', row[column.column]);
if (column.column == 'id') {
tr.setAttribute('data-value', row[column.column]);
const idColumn = document.createElement('button');
let payload = {'element': row, ...this.options.selectable.callbackParameters};
idColumn.addEventListener('click', this.options.selectable.select.bind(null, payload));
idColumn.classList.add('btn', 'btn-danger', 'btn-block');
idColumn.style = "padding: 0.1rem 0.5em;";
idColumn.innerHTML = 'Selecionar';
td.appendChild(idColumn);
} else {
td.innerHTML = row[column.column];
}
tr.appendChild(td);
});
tbody.appendChild(tr);
});
return tbody;
}
}
class Buttons {
constructor(currentPage, totalPages, maxButtons, onPageChange, replicateChanges) {
this.currentPage = currentPage;
this.totalPages = totalPages;
this.maxButtons = maxButtons;
this.onPageChange = onPageChange;
this.replicateChanges = replicateChanges;
}
render() {
console.log('adssaddas');
this.buttonHolder = document.createElement('div');
this.buttonHolder.classList.add('col-12', 'col-md-9');
this.paginationButtons = document.createElement('ul');
this.paginationButtons.classList.add('pagination', 'pagination-sm');
this.prevButton = document.createElement('li');
this.prevButton.classList.add('page-link');
this.prevButton.textContent = 'Previous';
this.prevButton.disabled = true;
this.prevButton.addEventListener('click', () => {
if (this.currentPage > 1) {
this.onPageChange(this.currentPage - 1);
this.updateButtons(this.currentPage - 1);
this.replicateChanges(this.currentPage);
}
});
this.paginationButtons.appendChild(this.prevButton);
this.pageButtons = Array();
for (let i = 1; i <= this.totalPages; i++) {
const pageButton = document.createElement('li');
pageButton.classList.add('page-item');
const pageButtonLink = document.createElement('a');
pageButtonLink.classList.add('page-link');
pageButtonLink.textContent = i;
if (!(this.currentPage === i ||
i > (this.currentPage + this.maxButtons / 2) ||
i < (this.currentPage - this.maxButtons / 2))) {
pageButton.classList.add('hidden');
}
if(this.currentPage === i){
pageButton.classList.add('active');
}
pageButtonLink.addEventListener('click', () => {
this.onPageChange(i);
this.updateButtons(i);
this.replicateChanges(this.currentPage);
});
pageButton.appendChild(pageButtonLink);
this.pageButtons.push(pageButton);
this.paginationButtons.appendChild(pageButton);
}
this.nextButton = document.createElement('li');
this.nextButton.classList.add('page-link');
this.nextButton.textContent = 'Next';
this.nextButton.disabled = this.totalPages === 0 || this.currentPage === this.totalPages;
this.nextButton.addEventListener('click', () => {
if (this.currentPage < this.totalPages) {
this.onPageChange(this.currentPage + 1);
this.updateButtons(this.currentPage + 1);
this.replicateChanges(this.currentPage);
}
});
this.paginationButtons.appendChild(this.nextButton);
this.buttonHolder.appendChild(this.paginationButtons);
return this.buttonHolder;
}
updateButtons(page, perPage) {
if (page) {
this.currentPage = parseInt(page);
}
if (this.prevButton) {
this.prevButton.disabled = this.currentPage === 1;
}
if (this.nextButton) {
this.nextButton.disabled = this.totalPages === 0 || this.currentPage === this.totalPages;
}
if (this.pageButtons) {
this.pageButtons.forEach((button, index) => {
button.classList.remove('active');
if (this.currentPage === index + 1 ||
index > (this.currentPage + this.maxButtons / 2) ||
index < (this.currentPage - this.maxButtons / 2)) {
button.classList.add('hidden');
}else {
button.classList.remove('hidden');
}
if (index + 1 == this.currentPage) {
button.classList.add('active');
button.classList.remove('hidden');
} else {
button.removeAttribute('value');
}
});
}
}
}
class Label {
constructor(actualPage, totalPages, recordsCount, totalRecords) {
this.actualPage = actualPage;
this.totalPages = totalPages;
this.recordsCount = recordsCount;
this.totalRecords = totalRecords;
}
render(){
this.paginationLabel = document.createElement('div');
this.paginationLabel.classList.add('col-12');
this.pageLabel = document.createElement('span');
this.pageLabel.innerText = `Page: ${this.actualPage} of ${this.totalPages} | `;
this.recordLabel = document.createElement('span');
this.recordLabel.innerText = `${this.recordsCount} of ${this.totalRecords} records shown`;
this.paginationLabel.appendChild(this.pageLabel);
this.paginationLabel.appendChild(this.recordLabel);
return this.paginationLabel;
}
rerender() {
this.pageLabel.innerText = `Page: ${this.actualPage} of ${this.totalPages} | `;
this.recordLabel.innerText = `${this.recordsCount} of ${this.totalRecords} records shown`;
}
updateLabel(page, perPage, totalPages){
this.actualPage = page;
this.perPage = perPage;
this.rerender();
}
}
class Search {
constructor(currentPage, totalPages, onPageChange, replicateChanges){
this.currentPage = currentPage;
this.totalPages = totalPages;
this.onPageChange = onPageChange;
this.replicateChanges = replicateChanges;
}
render(){
const paginationHolder = document.createElement('div');
paginationHolder.classList.add('col-12', 'col-md-3');
this.paginationSearch = document.createElement('div');
this.paginationSearch.classList.add('search', 'input-group','col-md-3', 'col-sm-12');
this.textualNumber = document.createElement('input');
this.textualNumber.classList.add('form-control', 'form-control-sm');
this.textualNumber.setAttribute('type', 'number');
this.textualNumber.setAttribute('min', 1);
this.textualNumber.setAttribute('max', this.totalPages);
this.textualNumber.value = this.currentPage;
this.textualNumber.addEventListener('blur', (event) => {
let page = event.target.value;
if(page > this.totalPages){
page = this.totalPages;
this.textualNumber.value = this.totalPages;
}
if(page < 1){
page = 1;
this.textualNumber.value = page;
}
this.replicateChanges(page);
this.onPageChange(page);
});
this.textualNumber.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
console.log('reberberbrte');
}
});
//this.paginationSearch.appendChild(this.textualNumber);
this.append(this.textualNumber);
this.selectNumber = document.createElement('select');
this.selectNumber.classList.add('form-select', 'form-select-sm');
for(let i = 1; i < this.totalPages; i++){
const selectOption = document.createElement('option');
selectOption.innerText = i;
selectOption.setAttribute('value', i);
this.selectNumber.appendChild(selectOption);
}
this.selectNumber.addEventListener('change', (event) => {
this.replicateChanges(event.target.value);
this.onPageChange(event.target.value);
});
//this.paginationSearch.appendChild(this.selectNumber);
this.append(this.selectNumber);
const recordsPerPage = document.createElement('select');
recordsPerPage.classList.add('form-select', 'form-select-sm');
for(let i = 1; i < 10; i++){
const selectOption = document.createElement('option');
selectOption.innerText = i * 25;
selectOption.setAttribute('value', i * 25);
recordsPerPage.appendChild(selectOption);
}
recordsPerPage.addEventListener('change', (event) => {
//console.log(event.target.value)
//this.replicateChanges(event.target.value);
});
//this.paginationSearch.appendChild(recordsPerPage);
this.append(recordsPerPage);
paginationHolder.appendChild(this.paginationSearch);
return paginationHolder;
}
updateSearch(pageNumber, perPage) {
this.selectNumber.value = pageNumber;
this.textualNumber.value = pageNumber;
}
searchChange(){
}
append(element){
let container = document.createElement('div');
container.classList.add('col-4');
container.appendChild(element);
this.paginationSearch.appendChild(container);
}
}
class Pagination {
constructor(onPageChange, rowCount, perPage) {
this.currentPage = 1;
this.maxButtons = 10;
this.onPageChange = onPageChange;
this.rowCount = rowCount;
this.perPage = perPage;
this.totalPages = Math.ceil(rowCount / perPage);
}
render() {
this.paginationContainer = document.createElement('div');
this.paginationContainer.classList.add('pagination', 'row');
if(this.totalPages <= 1){
return this.paginationContainer;
}
this.labels = new Label(this.currentPage, this.totalPages, 10, 1000);
this.buttons = new Buttons(this.currentPage, this.totalPages, 10, this.onPageChange, (page) => {
this.labels.updateLabel(page, this.perPage);
this.search.updateSearch(page, this.perPage);
});
this.search = new Search(this.currentPage, this.totalPages, this.onPageChange, (page, size) => {
this.labels.updateLabel(page, this.perPage);
this.buttons.updateButtons(page, this.perPage);
this.search.updateSearch(page, this.perPage);
});
this.paginationContainer.appendChild(this.labels.render());
this.paginationContainer.appendChild(this.buttons.render());
this.paginationContainer.appendChild(this.search.render());
return this.paginationContainer;
}
changePageSize(onPageChange, rowCount, perPage) {
this.currentPage = 1;
this.maxButtons = 10;
this.onPageChange = onPageChange;
this.rowCount = rowCount;
this.perPage = perPage;
this.totalPages = Math.ceil(rowCount / perPage);
return this.paginationContainer;
}
}
class ListButton{
constructor(icon, actions){
this.icon = icon;
this.actions = actions;
}
render() {
this.actionButton = document.createElement('button');
this.actionButton.classList.add('btn', 'btn-outline-secondary');
this.actionButton.innerHTML = ``;
return this.actionButton;
}
}
class SearchBar {
constructor(onSearch) {
this.onSearch = onSearch;
}
render() {
this.searchBarContainer = document.createElement('div');
this.searchBarContainer.classList.add('search-bar');
this.inputElement = document.createElement('input');
this.inputElement.classList.add('form-control');
this.inputElement.setAttribute('type', 'text');
this.inputElement.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
this.searchText = this.inputElement.value;
this.onSearch(this.searchText);
}
});
this.searchBarContainer.appendChild(this.inputElement);
return this.searchBarContainer;
}
getSearch(){
return this.inputElement.value;
}
}
class ToolBar {
constructor(onSearch, actions, options) {
this.onSearch = onSearch;
this.actions = actions;
this.options = options;
}
render() {
this.toolbar = document.createElement('div');
this.toolbar.classList.add('input-group', 'mb-3');
if (typeof this.actions !== 'undefined' && typeof this.options.selectable === 'undefined' && this.actions.length > 0) {
this.actionSet = new ActionSet(this.actions, 'button');
let { button, toggle, list } = this.actionSet.render();
if (button) {
this.toolbar.appendChild(button);
}
if (toggle) {
this.toolbar.appendChild(toggle);
}
if (list) {
this.toolbar.append(list);
}
}
this.searchBar = new SearchBar(this.onSearch);
this.toolbar.appendChild(this.searchBar.render());
this.toolbar.appendChild(new ListButton('th', '').render());
return this.toolbar;
}
getSearch() {
return this.searchBar.getSearch();
}
}
class DataTable {
constructor(tableElement, options = {}) {
if (typeof tableElement === 'string') {
this.tableElement = document.getElementById(tableElement);
} else {
this.tableElement = tableElement;
}
this.page = 1;
this.options = options;
this.perPage = 25;
this.getData();
}
render(data) {
const { actions, rowActions, columns, rows, total } = data;
this.table = document.createElement('table');
this.table.classList.add('table', 'table-responsive');
this.searchBar = new ToolBar((searchText) => {
this.getData(searchText);
}, actions, this.options);
this.pagination = new Pagination((page, perPage) => {
this.page = page--;
//this.perPage = perPage;
this.getData();
}, total, this.perPage);
this.headerElement = new TableHeader(columns);
const tableHeader = this.headerElement.render();
this.table.appendChild(tableHeader.colgroup);
this.table.appendChild(tableHeader.thead);
this.bodyElement = new TableBody(rows, columns, rowActions, this.options);
this.table.appendChild(this.bodyElement.render());
// #ToDo Convert table to div
if (this.tableElement.tagName === 'TABLE') {
const divContainer = document.createElement('div');
while (this.tableElement.firstChild) {
divContainer.appendChild(this.tableElement.firstChild);
}
this.tableElement.parentNode.replaceChild(divContainer, this.tableElement);
}
this.tableElement.appendChild(this.searchBar.render());
this.tableElement.appendChild(this.pagination.render());
this.tableElement.appendChild(this.table);
}
rerender(data) {
const { columns, rows, rowActions, total } = data;
if (!this.table) {
this.render(data);
return;
}
const content = new TableBody(rows, columns, rowActions, this.options).render();
this.table.querySelector('tbody').innerHTML = content.innerHTML;
}
async getData() {
let requestBody = {
'page': this.page - 1,
'pageSize': this.perPage
};
if(this.searchBar){
requestBody.search = this.searchBar.getSearch();
}
const params = new URLSearchParams(requestBody).toString();
const response = await fetch(this.options.url + '?' + params);
const data = await response.json();
this.rerender(data);
}
}
export { DataTable as default };