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 };