From 51f98676140866e8b3dbdee455a7dce8a1010f53 Mon Sep 17 00:00:00 2001 From: JOSEPH LE Date: Wed, 21 Feb 2024 16:07:26 +0700 Subject: [PATCH] first commit --- JDataTable.js | 383 ++++++++++++++++++++++++++++++++++ index.html | 183 +++++++++++++++++ index.php | 552 -------------------------------------------------- 3 files changed, 566 insertions(+), 552 deletions(-) create mode 100644 JDataTable.js create mode 100644 index.html delete mode 100644 index.php diff --git a/JDataTable.js b/JDataTable.js new file mode 100644 index 0000000..0e54259 --- /dev/null +++ b/JDataTable.js @@ -0,0 +1,383 @@ +class TableData { + config = { + order: null, + filter: null, + heads: [], + page: 1, + per_page: 15, + data: {}, + rowRender: (colName, colValue, row) => { + return colValue; + }, + footer: { + pagination: false, + detail: false, + }, + sort: { + enable: false, + listColumn: [], + }, + }; + + constructor(tableId, config) { + this.config = { + ...this.config, + ...config, + }; + this.tableId = tableId; + } + + // Update configuration, display data after sorting, listen again for appropriate icon + sorted(column) { + if ( + this.config.order !== null && + Object.values(this.config.order)[0] === "asc" + ) { + this.config.order = null; + this.updateConfig(this.config); + } else { + if (this.config.order === null) { + this.config.order = { + [`order_by_${column}`]: "desc", + }; + this.updateConfig(this.config); + this.attachSortEventListeners(".ri-arrow-down-s-fill"); + } else { + this.config.order = { + [`order_by_${column}`]: "asc", + }; + this.updateConfig(this.config); + this.attachSortEventListeners(".ri-arrow-up-s-fill"); + } + } + } + + // Listen events of sort buttons + attachSortEventListeners(className) { + const icons = document.querySelectorAll(className); + icons.forEach((icon) => { + icon.addEventListener("click", (event) => { + const columnName = event.target.dataset.columnName; + this.sorted(columnName, icon); + }); + }); + } + + // Show the sort icon again when the sort button is clicked + renderIconSort(column) { + if (this.config.order === null) { + return ``; + } else { + var orderKey = Object.keys(this.config.order)[0]; + var orderValue = Object.values(this.config.order)[0]; + if (orderKey === `order_by_${column}`) { + if (orderValue === "asc") { + return ``; + } else { + return ``; + } + } else { + return ""; + } + } + } + + // Listen events of per page + attachPerPageEventListeners() { + const elements = document.querySelectorAll( + `${this.tableId} #perpage div #perPageSelect` + ); + elements[0].addEventListener("change", (event) => { + const value = event.target.value; + this.config.per_page = value; + this.render(); + }); + } + + // Listen events of pagination buttons + attachPaginationEventListeners() { + const pages = document.querySelectorAll(".page_number"); + pages.forEach((page) => { + page.addEventListener("click", (event) => { + const pageNumber = event.target.dataset.columnName; + if (pageNumber !== "null" && pageNumber !== this.config.page) { + this.config.page = pageNumber; + this.render(); + } + }); + }); + } + + // Listen events of action buttons + attachActionEventListeners(className) { + const buttons = document.querySelectorAll(className); + buttons.forEach((btn) => { + btn.addEventListener("click", (event) => { + const rowId = event.target.dataset.rowId; + if (rowId) { + if (className === ".btn_edit") { + this.rowEdit(rowId); + } + if (className === ".btn_done") { + this.rowEditDone(rowId); + } + } + }); + }); + } + + // Render headers in table + renderHeads() { + var ths = ""; + this.config.heads.map((head) => { + if (head.name === "__actions") { + ths += ` +
+ ${head.value} +
+ `; + } else { + if (head.hasOwnProperty("render")) { + ths += ` +
+ ${head.render(head)} + ${ + this.config.sort.enable && + this.config.sort.listColumn && + this.config.sort.listColumn.includes(head.name) + ? this.renderIconSort(head.name) + : "" + } +
+ `; + } else { + ths += ` +
+ ${head.value} + ${ + this.config.sort.enable && + this.config.sort.listColumn && + this.config.sort.listColumn.includes(head.name) + ? this.renderIconSort(head.name) + : "" + } +
+ `; + } + } + }); + var html = ` + + ${ths} + + `; + return html; + } + + // Render rows in table + renderRows(data) { + let html = ""; + data.data.map((row, index) => { + html += ``; + this.config.heads.map((col) => { + html += `${this.config.rowRender(col.name, row[col.name], row)}`; + }); + html += ""; + }); + html += ""; + // Initial pagination use 'data.links' + let htmlPagination = `"; + html += ` +
`; + html += `
+ +
+ + + +
+ +
`; + if (this.config.footer.detail) { + html += ``; + } + + if (this.config.footer.pagination) { + html += ``; + } + + html += `
`; + $(this.tableId).append(html); + this.attachPaginationEventListeners(); + this.attachPerPageEventListeners(); + } + + // Render table + async render() { + let data; + let params = { + per_page: this.config.per_page, + page: this.config.page, + }; + // Clear html + $(this.tableId).html(""); + // Initial headers + $(this.tableId).append(this.renderHeads()); + this.attachSortEventListeners(".ri-expand-up-down-fill"); + + // Append 'order' and 'filter' into params if exist + if (this.config.hasOwnProperty("ajax")) { + if (this.config.order !== null) { + Object.assign(params, this.config.order); + } + if (this.config.filter !== null) { + Object.assign(params, this.config.filter); + } + // Call ajax and render data + data = await this.config.ajax(params); + this.renderRows(data); + } else { + // Use data transmitted directly + data = this.config.data; + this.renderRows(data); + } + + // Start listening to events of already initialized buttons + this.attachActionEventListeners(".btn_edit"); + this.attachActionEventListeners(".btn_done"); + } + + // Get the current configuration of the table + getConfig() { + return this.config; + } + + // Automatically merger new config and re-render + updateConfig(newConfig) { + this.config = { + ...this.config, + ...newConfig, + }; + this.render(); + } + + // Define and automatically create edit buttons + // Used in rowRender configuration + actionEdit(row) { + return ` +
+ + +
+ `; + } + + // Define and automatically create editable cells + // Used in rowRender configuration + cellEdit(colName, colValue) { + return ` +
${colValue}
+
+ `; + } + + // Works when the edit button is clicked + rowEdit(rowId) { + this.config.heads.map((col) => { + var cellShow = $(`#row_${rowId} .cell_show .${col.name}`); + var cellEdit = $(`#row_${rowId} .cell_edit .${col.name}`); + + if (cellShow) { + cellShow.hide(); + } + + if (cellEdit) { + cellEdit.show(); + } + }); + $(`#row_${rowId} div .btn_done`).show(); + $(`#row_${rowId} div .btn_edit`).hide(); + } + + rowEditDone(rowId) { + // Initial params with row id + let params = { + id: parseInt(rowId), + }; + + // Get api update info + let apiUpdateInfo = this.config.apiUpdate; + + // Add edited values ​​to params + this.config.heads.map((col) => { + var cellEdit = $(`#row_${rowId} .cell_edit .${col.name}`); + + if (cellEdit.val()) { + Object.assign(params, { + [col.name]: cellEdit.val(), + }); + } + }); + // Call API update data row + if (this.config.hasOwnProperty("apiUpdate")) { + $.ajax({ + url: apiUpdateInfo.url, + type: apiUpdateInfo.type, + data: params, + headers: apiUpdateInfo.headers, + success: (response) => { + // alert("Edit success"); + this.render(); + }, + error: function (error) { + console.log(error); + alert("Edit Fail"); + }, + }); + } else { + alert("Missing updated API information"); + } + + // + $(`#row_${rowId} div .btn_done`).hide(); + $(`#row_${rowId} div .btn_edit`).show(); + } + + // Update config and render when change per page + changePerPage() { + this.config.per_page = $(`${this.tableId} #perPageSelect`).val(); + this.render(); + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..c3c621a --- /dev/null +++ b/index.html @@ -0,0 +1,183 @@ + + + + + JDataTable + + + + + + + + + + + +
+

JDataTable

+

A library used to initialize a basic data table. Includes the main components of a data table such as: sorting, per page, pagination, table. Supports additional configuration of search and filtering features.

+ + +
+
+
Filter
+
+
+ + +
+
+ + +
+ +
+
+
+ + + +
+ + +
+ + + + +
+ + + + + + +
+ + + + + \ No newline at end of file diff --git a/index.php b/index.php deleted file mode 100644 index 7b1a868..0000000 --- a/index.php +++ /dev/null @@ -1,552 +0,0 @@ - - - - - Bootstrap Example - - - - - - - - - - -
-

Striped Rows

-

The .table-striped class adds zebra-stripes to a table:

- - -
-
-
Filter
-
-
- - -
-
- - -
- -
-
-
- - - -
- - -
- - - - -
- - - - - - -
- - - - - \ No newline at end of file