Parcourir la source

Rollup and changes

Artur Welp il y a 2 ans
Parent
commit
c3f496f847
5 fichiers modifiés avec 681 ajouts et 6 suppressions
  1. 4 0
      components/pagination/Pagination.js
  2. 670 0
      dist/datatable.js
  3. 6 6
      index.js
  4. 1 0
      package.json
  5. 0 0
      rollup.config.mjs

+ 4 - 0
components/pagination/Pagination.js

@@ -16,6 +16,10 @@ export default class Pagination {
         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) => {

+ 670 - 0
dist/datatable.js

@@ -0,0 +1,670 @@
+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 = '<i class="fa fa-arrow-down" aria-hidden="true"></i>';
+
+    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 = `<i class="fa fa-${this.actions[0].icon}" aria-hidden="true"></i> ${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 = `<i class="fa fa-${action.icon}" aria-hidden="true"></i> ${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 = `<i class="fa fa-${action.icon}" aria-hidden="true"></i> ${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 `<i class="fa fa-${icon}" aria-hidden="true"></i> ${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 = `<i class="fa fa-${this.icon}" aria-hidden="true"></i>`;
+
+        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 };

+ 6 - 6
index.js

@@ -20,23 +20,23 @@ export default class DataTable {
     render(data) {
         const { actions, rowActions, columns, rows, total } = data;
         this.table = document.createElement('table');
-        this.table.classList.add('table');
+        this.table.classList.add('table', 'table-responsive');
 
         this.searchBar = new ToolBar((searchText) => {
             this.getData(searchText);
         }, actions, this.options);
 
-        this.headerElement = new TableHeader(columns);
-        const tableHeader = this.headerElement.render();
-        this.table.appendChild(tableHeader.colgroup);
-        this.table.appendChild(tableHeader.thead);
-
         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());
 

+ 1 - 0
package.json

@@ -3,6 +3,7 @@
     "version": "1.0.0",
     "description": "A library for rendering datatables in JavaScript",
     "main": "dist/bundle.js",
+    "type": "module",
     "scripts": {
         "build": "rollup -c",
         "test": "echo \"No tests yet...\" && exit 0"

+ 0 - 0
rollup.config.js → rollup.config.mjs