PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.76
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.76
51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 51.1.51 All 36 releases
king-addons / includes / widgets / Data_Table / script.js

script.js in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.76, at includes/widgets/Data_Table/script.js

370 lines 20.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 "use strict";
2 (function ($) {
3 $(window).on('elementor/frontend/init', function () {
4 elementorFrontend.hooks.addAction('frontend/element_ready/king-addons-data-table.default', function ($scope) {
5 // Add elementor handler
6 elementorFrontend.elementsHandler.addHandler(
7 elementorModules.frontend.handlers.Base.extend({
8 onInit: function onInit() {
9 const $scope = this.$element;
10 const $tableContainer = $scope.find('.king-addons-table-inner-container');
11 const $tableBody = $tableContainer.find('tbody');
12 const initialRows = $tableBody.find('tr');
13 const paginationListItems = $scope.find('.king-addons-table-custom-pagination-list-item');
14 let beforeFilter = $tableBody.find('.king-addons-table-row');
15 let value = "";
16
17 // Initialize Perfect Scrollbar if needed
18 new PerfectScrollbar($tableContainer[0], {});
19
20 // Helper: get number of items per page (as integer)
21 const getItemsPerPage = () => +$tableContainer.attr('data-rows-per-page') || 0;
22
23 /**
24 * Updates the table with the filtered set of rows for a given page index (1-based).
25 * Also optionally checks the live-search value for resetting the table if empty.
26 */
27 const displayRowsForPage = (pageIndex, checkSearch) => {
28 // Hide table while updating
29 $tableBody.hide();
30
31 const itemsPerPage = getItemsPerPage();
32 // Filter rows that should appear on the given page
33 const newRows = initialRows.filter((i) => {
34 // Original code uses index++ inside filter; equivalently use (i+1) here
35 const rowNumber = i + 1;
36 return (
37 rowNumber > itemsPerPage * (pageIndex - 1) &&
38 rowNumber <= itemsPerPage * pageIndex
39 );
40 });
41
42 // Update the table with these new rows
43 $tableBody.html(newRows);
44
45 // If checkSearch is true and the current search is empty, revert back to beforeFilter
46 if (checkSearch && value === "") {
47 $tableBody.html(beforeFilter);
48 }
49
50 $tableBody.show();
51 // Refresh the 'beforeFilter' cache
52 beforeFilter = $tableBody.find('.king-addons-table-row');
53 // Remove highlight classes if any
54 beforeFilter
55 .find('.king-addons-table-tr-before-remove')
56 .removeClass('king-addons-table-tr-before-remove');
57
58 updateEntryInfo();
59 };
60
61 /**
62 * Removes 'active' class from all pagination items,
63 * sets the correct item as active, and adjusts their visibility.
64 */
65 const setActivePaginationItem = (pageIndex) => {
66 paginationListItems.removeClass('king-addons-active-pagination-item');
67 paginationListItems.each((i, el) => {
68 // Page indices in UI are basically i+1 if user numbered them from 1..n
69 if (i + 1 === pageIndex) {
70 $(el).addClass('king-addons-active-pagination-item');
71 }
72 });
73 adjustPaginationList();
74 };
75
76 /**
77 * Called by previous/next clicks to move forward/back by `delta` pages.
78 * If the new page is out of range, does nothing.
79 */
80 const changePage = (delta) => {
81 const currentPage = +$scope.find('.king-addons-active-pagination-item').text() || 1;
82 const totalPages = paginationListItems.length;
83 const newPage = currentPage + delta;
84 if (newPage < 1 || newPage > totalPages) return;
85
86 setActivePaginationItem(newPage);
87 // For Prev/Next we do want to check if search is empty
88 displayRowsForPage(newPage, true);
89 };
90
91 /**
92 * Adjusts pagination list items to show/hide neighbors correctly.
93 */
94 const adjustPaginationList = () => {
95 const paginationIndex = $scope.find('.king-addons-active-pagination-item').index();
96 paginationListItems.each((i, el) => {
97 // Always display the first/last, and the 2 neighbors around current item
98 if (
99 i === 0 ||
100 i === paginationListItems.length - 1 ||
101 (i <= paginationIndex && i >= paginationIndex - 2)
102 ) {
103 $(el).css('display', 'flex');
104 } else {
105 $(el).css('display', 'none');
106 }
107 });
108 };
109
110 /**
111 * Updates the "Showing X to Y of Z Entries" text (if enabled).
112 */
113 const updateEntryInfo = () => {
114 if ($tableContainer.attr('data-entry-info') !== 'yes') return;
115
116 const itemsPerPage = getItemsPerPage();
117 const entryPage = +$scope.find('.king-addons-active-pagination-item').text() || 1;
118 const rowsOnPage = $tableBody.find('tr').length;
119
120 const lastEntry = itemsPerPage * entryPage - (itemsPerPage - rowsOnPage);
121 const firstEntry = lastEntry - rowsOnPage + 1;
122
123 const info = `Showing ${firstEntry} to ${lastEntry} of ${initialRows.length} Entries.`;
124 $scope.find('.king-addons-entry-info').html(info);
125 };
126
127 /**
128 * Live search logic: filters rows by matching text in any table cell.
129 */
130 const initLiveSearch = () => {
131 $scope.find(".king-addons-table-live-search").on("keyup", function () {
132 value = this.value.toLowerCase().trim();
133
134 if (value !== "") {
135 $scope.find('.king-addons-table-pagination-cont')
136 .addClass('king-addons-hide-pagination-on-search');
137 } else {
138 $scope.find('.king-addons-table-pagination-cont')
139 .removeClass('king-addons-hide-pagination-on-search');
140 }
141
142 const filteredRows = [];
143 initialRows.each((i, row) => {
144 const $row = $(row);
145 $row.find("td").each((_, cell) => {
146 const cellText = $(cell).text().toLowerCase().trim();
147 // If found a match, add row to `filteredRows` once
148 if (cellText.indexOf(value) !== -1) {
149 filteredRows.push($row);
150 return false; // break out of .each loop for this row
151 }
152 });
153 });
154
155 // If user cleared search, revert to all filtered items
156 $tableBody.html(value === "" ? beforeFilter : filteredRows);
157 updateEntryInfo();
158 });
159 };
160
161 /**
162 * Table sorting logic by clicking on <th>.
163 */
164 const initTableSorting = () => {
165 // Only if data-table-sorting is 'yes'
166 if ($tableContainer.attr('data-table-sorting') !== 'yes') return;
167
168 // Remove highlight class if user clicks outside any TH or highlight cell
169 $(window).on('click', (e) => {
170 const isTh = $(e.target).hasClass('king-addons-table-th');
171 const inTh = $(e.target).closest('.king-addons-table-th').length > 0;
172 const isActiveTd = $(e.target).hasClass('king-addons-active-td-bg-color');
173 const inActiveTd = $(e.target).closest('.king-addons-active-td-bg-color').length > 0;
174
175 if (!isTh && !inTh && !isActiveTd && !inActiveTd) {
176 $scope.find('td.king-addons-active-td-bg-color').removeClass('king-addons-active-td-bg-color');
177 }
178 });
179
180 const getCellValue = (row, idx) =>
181 $(row).children('td').eq(idx).text();
182
183 const comparer = (idx) => (a, b) => {
184 const valA = getCellValue(a, idx);
185 const valB = getCellValue(b, idx);
186 // Numeric vs. text comparison
187 if ($.isNumeric(valA) && $.isNumeric(valB)) {
188 return valA - valB;
189 }
190 return valA.toString().localeCompare(valB);
191 };
192
193 // Handle clicks on <th> for sorting
194 $scope.find('th').on('click', function () {
195 const $this = $(this);
196 const indexOfTr = $this.index();
197
198 // Highlight the column's cells
199 $scope.find('td').each(function () {
200 $(this).toggleClass(
201 'king-addons-active-td-bg-color',
202 $(this).index() === indexOfTr
203 );
204 });
205
206 // Reset the sorting icon on every TH
207 $scope.find('th .king-addons-sorting-icon').html('<i class="fas fa-sort" aria-hidden="true"></i>');
208
209 // Sort the rows
210 const table = $this.closest('table');
211 let rows = table.find('tr:gt(0)').toArray().sort(comparer(indexOfTr));
212
213 // Toggle ascending/descending
214 this.asc = !this.asc;
215 const isCustom = $scope.hasClass('king-addons-data-table-type-custom');
216
217 if ((isCustom && !this.asc) || (!isCustom && this.asc)) {
218 // Sort reversed
219 rows = rows.reverse();
220 $this.find('.king-addons-sorting-icon').html(
221 isCustom
222 ? '<i class="fas fa-sort-down" aria-hidden="true"></i>'
223 : '<i class="fas fa-sort-up" aria-hidden="true"></i>'
224 );
225 } else {
226 // Normal ascending
227 $this.find('.king-addons-sorting-icon').html(
228 isCustom
229 ? '<i class="fas fa-sort-up" aria-hidden="true"></i>'
230 : '<i class="fas fa-sort-down" aria-hidden="true"></i>'
231 );
232 }
233
234 // Re-append sorted rows
235 for (let i = 0; i < rows.length; i++) {
236 table.append(rows[i]);
237 }
238
239 // Remove leftover classes from "expanded" or appended rows
240 beforeFilter.find('.king-addons-table-tr-before-remove').each(function () {
241 $(this)
242 .closest('.king-addons-table-row')
243 .next('.king-addons-table-appended-tr')
244 .remove();
245 $(this).removeClass('king-addons-table-tr-before-remove');
246 });
247 });
248 };
249
250 /**
251 * If data-row-pagination="yes", prepend an index column (#).
252 */
253 const initRowPagination = () => {
254 if ($tableContainer.attr('data-row-pagination') !== 'yes') return;
255
256 $scope.find('.king-addons-table-head-row').prepend(
257 '<th class="king-addons-table-th-pag" style="vertical-align: middle;">#</th>'
258 );
259 initialRows.each(function (index) {
260 $(this).prepend(
261 `<td class="king-addons-table-td-pag" style="vertical-align: middle;">
262 <span style="vertical-align: middle;">${index + 1}</span>
263 </td>`
264 );
265 });
266 };
267
268 /**
269 * Export buttons logic (CSV/XLS).
270 */
271 const initTableExport = () => {
272 if (!$scope.find('.king-addons-table-export-button-cont').length) return;
273
274 const exportBtn = $scope.find('.king-addons-table-export-button-cont .king-addons-button');
275
276 const htmlToCSV = (filename, $view) => {
277 const rows = $view.find(".king-addons-table-row");
278 const data = [];
279 rows.each((_, row) => {
280 const cols = row.querySelectorAll(".king-addons-table-text");
281 const rowData = Array.from(cols).map(cell => cell.innerText);
282 data.push(rowData.join(","));
283 });
284 downloadCSVFile(data.join("\n"), filename);
285 };
286
287 const downloadCSVFile = (csv, filename) => {
288 const csvFile = new Blob([csv], { type: "text/csv" });
289 const downloadLink = document.createElement("a");
290 downloadLink.download = filename;
291 downloadLink.href = window.URL.createObjectURL(csvFile);
292 downloadLink.style.display = "none";
293 document.body.appendChild(downloadLink);
294 downloadLink.click();
295 };
296
297 exportBtn.each(function () {
298 const $btn = $(this);
299 if ($btn.hasClass('king-addons-xls')) {
300 // XLS export
301 $btn.on('click', () => {
302 const $table = $scope.find('table');
303 TableToExcel.convert($table[0], {
304 name: 'export.xlsx',
305 sheet: { name: 'Sheet 1' },
306 });
307 });
308 } else if ($btn.hasClass('king-addons-csv')) {
309 // CSV export
310 $btn.on('click', () => {
311 htmlToCSV("placeholder.csv", $scope.find('.king-addons-data-table'));
312 });
313 }
314 });
315 };
316
317 /**
318 * Initializes custom pagination if enabled.
319 */
320 const initCustomPagination = () => {
321 if ($tableContainer.attr('data-custom-pagination') !== 'yes') return;
322
323 // Show only the first set of rows (page 1) initially
324 const itemsPerPage = getItemsPerPage();
325 const firstPageRows = initialRows.filter((i) => i < itemsPerPage);
326 $tableBody.html(firstPageRows);
327
328 adjustPaginationList();
329
330 // When clicking on a specific page number
331 paginationListItems.on('click', function () {
332 // Remove the old active, set the new active
333 paginationListItems.removeClass('king-addons-active-pagination-item');
334 $(this).addClass('king-addons-active-pagination-item');
335 adjustPaginationList();
336
337 const pageIndex = +$(this).text();
338 // For direct page clicks, we do NOT revert to beforeFilter if search is empty
339 displayRowsForPage(pageIndex, false);
340 });
341
342 // Handle prev/next clicks
343 $scope.find('.king-addons-table-prev-next').each(function () {
344 if ($(this).hasClass('king-addons-table-custom-pagination-prev')) {
345 $(this).on('click', () => changePage(-1));
346 } else {
347 $(this).on('click', () => changePage(1));
348 }
349 });
350 };
351
352 // ---------------------------------------------------
353 // Execute initializations
354 // ---------------------------------------------------
355 initCustomPagination();
356 initLiveSearch();
357 initTableSorting();
358 initRowPagination();
359 initTableExport();
360
361 // Finally, remove "hidden" class so the table becomes visible
362 $tableContainer.removeClass('king-addons-hide-table-before-arrange');
363 updateEntryInfo();
364 },
365 }),
366 { $element: $scope }
367 );
368 });
369 });
370 })(jQuery);