"use strict"; (function ($) { $(window).on('elementor/frontend/init', function () { elementorFrontend.hooks.addAction('frontend/element_ready/king-addons-data-table.default', function ($scope) { // Add elementor handler elementorFrontend.elementsHandler.addHandler( elementorModules.frontend.handlers.Base.extend({ onInit: function onInit() { const $scope = this.$element; const $tableContainer = $scope.find('.king-addons-table-inner-container'); const $tableBody = $tableContainer.find('tbody'); const initialRows = $tableBody.find('tr'); const paginationListItems = $scope.find('.king-addons-table-custom-pagination-list-item'); let beforeFilter = $tableBody.find('.king-addons-table-row'); let value = ""; // Initialize Perfect Scrollbar if needed new PerfectScrollbar($tableContainer[0], {}); // Helper: get number of items per page (as integer) const getItemsPerPage = () => +$tableContainer.attr('data-rows-per-page') || 0; /** * Updates the table with the filtered set of rows for a given page index (1-based). * Also optionally checks the live-search value for resetting the table if empty. */ const displayRowsForPage = (pageIndex, checkSearch) => { // Hide table while updating $tableBody.hide(); const itemsPerPage = getItemsPerPage(); // Filter rows that should appear on the given page const newRows = initialRows.filter((i) => { // Original code uses index++ inside filter; equivalently use (i+1) here const rowNumber = i + 1; return ( rowNumber > itemsPerPage * (pageIndex - 1) && rowNumber <= itemsPerPage * pageIndex ); }); // Update the table with these new rows $tableBody.html(newRows); // If checkSearch is true and the current search is empty, revert back to beforeFilter if (checkSearch && value === "") { $tableBody.html(beforeFilter); } $tableBody.show(); // Refresh the 'beforeFilter' cache beforeFilter = $tableBody.find('.king-addons-table-row'); // Remove highlight classes if any beforeFilter .find('.king-addons-table-tr-before-remove') .removeClass('king-addons-table-tr-before-remove'); updateEntryInfo(); }; /** * Removes 'active' class from all pagination items, * sets the correct item as active, and adjusts their visibility. */ const setActivePaginationItem = (pageIndex) => { paginationListItems.removeClass('king-addons-active-pagination-item'); paginationListItems.each((i, el) => { // Page indices in UI are basically i+1 if user numbered them from 1..n if (i + 1 === pageIndex) { $(el).addClass('king-addons-active-pagination-item'); } }); adjustPaginationList(); }; /** * Called by previous/next clicks to move forward/back by `delta` pages. * If the new page is out of range, does nothing. */ const changePage = (delta) => { const currentPage = +$scope.find('.king-addons-active-pagination-item').text() || 1; const totalPages = paginationListItems.length; const newPage = currentPage + delta; if (newPage < 1 || newPage > totalPages) return; setActivePaginationItem(newPage); // For Prev/Next we do want to check if search is empty displayRowsForPage(newPage, true); }; /** * Adjusts pagination list items to show/hide neighbors correctly. */ const adjustPaginationList = () => { const paginationIndex = $scope.find('.king-addons-active-pagination-item').index(); paginationListItems.each((i, el) => { // Always display the first/last, and the 2 neighbors around current item if ( i === 0 || i === paginationListItems.length - 1 || (i <= paginationIndex && i >= paginationIndex - 2) ) { $(el).css('display', 'flex'); } else { $(el).css('display', 'none'); } }); }; /** * Updates the "Showing X to Y of Z Entries" text (if enabled). */ const updateEntryInfo = () => { if ($tableContainer.attr('data-entry-info') !== 'yes') return; const itemsPerPage = getItemsPerPage(); const entryPage = +$scope.find('.king-addons-active-pagination-item').text() || 1; const rowsOnPage = $tableBody.find('tr').length; const lastEntry = itemsPerPage * entryPage - (itemsPerPage - rowsOnPage); const firstEntry = lastEntry - rowsOnPage + 1; const info = `Showing ${firstEntry} to ${lastEntry} of ${initialRows.length} Entries.`; $scope.find('.king-addons-entry-info').html(info); }; /** * Live search logic: filters rows by matching text in any table cell. */ const initLiveSearch = () => { $scope.find(".king-addons-table-live-search").on("keyup", function () { value = this.value.toLowerCase().trim(); if (value !== "") { $scope.find('.king-addons-table-pagination-cont') .addClass('king-addons-hide-pagination-on-search'); } else { $scope.find('.king-addons-table-pagination-cont') .removeClass('king-addons-hide-pagination-on-search'); } const filteredRows = []; initialRows.each((i, row) => { const $row = $(row); $row.find("td").each((_, cell) => { const cellText = $(cell).text().toLowerCase().trim(); // If found a match, add row to `filteredRows` once if (cellText.indexOf(value) !== -1) { filteredRows.push($row); return false; // break out of .each loop for this row } }); }); // If user cleared search, revert to all filtered items $tableBody.html(value === "" ? beforeFilter : filteredRows); updateEntryInfo(); }); }; /** * Table sorting logic by clicking on . */ const initTableSorting = () => { // Only if data-table-sorting is 'yes' if ($tableContainer.attr('data-table-sorting') !== 'yes') return; // Remove highlight class if user clicks outside any TH or highlight cell $(window).on('click', (e) => { const isTh = $(e.target).hasClass('king-addons-table-th'); const inTh = $(e.target).closest('.king-addons-table-th').length > 0; const isActiveTd = $(e.target).hasClass('king-addons-active-td-bg-color'); const inActiveTd = $(e.target).closest('.king-addons-active-td-bg-color').length > 0; if (!isTh && !inTh && !isActiveTd && !inActiveTd) { $scope.find('td.king-addons-active-td-bg-color').removeClass('king-addons-active-td-bg-color'); } }); const getCellValue = (row, idx) => $(row).children('td').eq(idx).text(); const comparer = (idx) => (a, b) => { const valA = getCellValue(a, idx); const valB = getCellValue(b, idx); // Numeric vs. text comparison if ($.isNumeric(valA) && $.isNumeric(valB)) { return valA - valB; } return valA.toString().localeCompare(valB); }; // Handle clicks on for sorting $scope.find('th').on('click', function () { const $this = $(this); const indexOfTr = $this.index(); // Highlight the column's cells $scope.find('td').each(function () { $(this).toggleClass( 'king-addons-active-td-bg-color', $(this).index() === indexOfTr ); }); // Reset the sorting icon on every TH $scope.find('th .king-addons-sorting-icon').html(''); // Sort the rows const table = $this.closest('table'); let rows = table.find('tr:gt(0)').toArray().sort(comparer(indexOfTr)); // Toggle ascending/descending this.asc = !this.asc; const isCustom = $scope.hasClass('king-addons-data-table-type-custom'); if ((isCustom && !this.asc) || (!isCustom && this.asc)) { // Sort reversed rows = rows.reverse(); $this.find('.king-addons-sorting-icon').html( isCustom ? '' : '' ); } else { // Normal ascending $this.find('.king-addons-sorting-icon').html( isCustom ? '' : '' ); } // Re-append sorted rows for (let i = 0; i < rows.length; i++) { table.append(rows[i]); } // Remove leftover classes from "expanded" or appended rows beforeFilter.find('.king-addons-table-tr-before-remove').each(function () { $(this) .closest('.king-addons-table-row') .next('.king-addons-table-appended-tr') .remove(); $(this).removeClass('king-addons-table-tr-before-remove'); }); }); }; /** * If data-row-pagination="yes", prepend an index column (#). */ const initRowPagination = () => { if ($tableContainer.attr('data-row-pagination') !== 'yes') return; $scope.find('.king-addons-table-head-row').prepend( '#' ); initialRows.each(function (index) { $(this).prepend( ` ${index + 1} ` ); }); }; /** * Export buttons logic (CSV/XLS). */ const initTableExport = () => { if (!$scope.find('.king-addons-table-export-button-cont').length) return; const exportBtn = $scope.find('.king-addons-table-export-button-cont .king-addons-button'); const htmlToCSV = (filename, $view) => { const rows = $view.find(".king-addons-table-row"); const data = []; rows.each((_, row) => { const cols = row.querySelectorAll(".king-addons-table-text"); const rowData = Array.from(cols).map(cell => cell.innerText); data.push(rowData.join(",")); }); downloadCSVFile(data.join("\n"), filename); }; const downloadCSVFile = (csv, filename) => { const csvFile = new Blob([csv], { type: "text/csv" }); const downloadLink = document.createElement("a"); downloadLink.download = filename; downloadLink.href = window.URL.createObjectURL(csvFile); downloadLink.style.display = "none"; document.body.appendChild(downloadLink); downloadLink.click(); }; exportBtn.each(function () { const $btn = $(this); if ($btn.hasClass('king-addons-xls')) { // XLS export $btn.on('click', () => { const $table = $scope.find('table'); TableToExcel.convert($table[0], { name: 'export.xlsx', sheet: { name: 'Sheet 1' }, }); }); } else if ($btn.hasClass('king-addons-csv')) { // CSV export $btn.on('click', () => { htmlToCSV("placeholder.csv", $scope.find('.king-addons-data-table')); }); } }); }; /** * Initializes custom pagination if enabled. */ const initCustomPagination = () => { if ($tableContainer.attr('data-custom-pagination') !== 'yes') return; // Show only the first set of rows (page 1) initially const itemsPerPage = getItemsPerPage(); const firstPageRows = initialRows.filter((i) => i < itemsPerPage); $tableBody.html(firstPageRows); adjustPaginationList(); // When clicking on a specific page number paginationListItems.on('click', function () { // Remove the old active, set the new active paginationListItems.removeClass('king-addons-active-pagination-item'); $(this).addClass('king-addons-active-pagination-item'); adjustPaginationList(); const pageIndex = +$(this).text(); // For direct page clicks, we do NOT revert to beforeFilter if search is empty displayRowsForPage(pageIndex, false); }); // Handle prev/next clicks $scope.find('.king-addons-table-prev-next').each(function () { if ($(this).hasClass('king-addons-table-custom-pagination-prev')) { $(this).on('click', () => changePage(-1)); } else { $(this).on('click', () => changePage(1)); } }); }; // --------------------------------------------------- // Execute initializations // --------------------------------------------------- initCustomPagination(); initLiveSearch(); initTableSorting(); initRowPagination(); initTableExport(); // Finally, remove "hidden" class so the table becomes visible $tableContainer.removeClass('king-addons-hide-table-before-arrange'); updateEntryInfo(); }, }), { $element: $scope } ); }); }); })(jQuery);