PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.11
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.11
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / data-table / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.11, at blocks/data-table/frontend.js

273 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 function toArray(nl) {
5 return Array.prototype.slice.call(nl || []);
6 }
7
8 function initDataTable(wrap) {
9 var searchInput = wrap.querySelector('[data-search]');
10 var table = wrap.querySelector('.bkbg-dt-table');
11 var thead = table ? table.querySelector('.bkbg-dt-thead') : null;
12 var tbody = table ? table.querySelector('.bkbg-dt-tbody') : null;
13 var headers = thead ? toArray(thead.querySelectorAll('.bkbg-dt-th')) : [];
14 var allRows = tbody ? toArray(tbody.querySelectorAll('.bkbg-dt-tr')) : [];
15
16 var sortable = wrap.getAttribute('data-sortable') === '1';
17 var paginate = wrap.getAttribute('data-paginate') === '1';
18 var perPage = parseInt(wrap.getAttribute('data-per-page'), 10) || 10;
19
20 var currentSort = { col: -1, dir: 'asc' };
21 var currentPage = 1;
22 var filteredRows = allRows.slice();
23
24 // Search functionality
25 function filterRows(query) {
26 query = (query || '').toLowerCase().trim();
27 if (!query) {
28 filteredRows = allRows.slice();
29 } else {
30 filteredRows = allRows.filter(function (row) {
31 var text = row.textContent.toLowerCase();
32 return text.indexOf(query) !== -1;
33 });
34 }
35 currentPage = 1;
36 render();
37 }
38
39 // Sort functionality
40 function sortRows(colIndex) {
41 if (currentSort.col === colIndex) {
42 currentSort.dir = currentSort.dir === 'asc' ? 'desc' : 'asc';
43 } else {
44 currentSort.col = colIndex;
45 currentSort.dir = 'asc';
46 }
47
48 // Update header UI
49 headers.forEach(function (th, i) {
50 th.removeAttribute('data-sort');
51 if (i === colIndex) {
52 th.setAttribute('data-sort', currentSort.dir);
53 }
54 });
55
56 filteredRows.sort(function (a, b) {
57 var cellA = a.children[colIndex];
58 var cellB = b.children[colIndex];
59 if (!cellA || !cellB) return 0;
60
61 var valA = cellA.textContent.trim();
62 var valB = cellB.textContent.trim();
63
64 // Try numeric sort
65 var numA = parseFloat(valA.replace(/[^0-9.-]/g, ''));
66 var numB = parseFloat(valB.replace(/[^0-9.-]/g, ''));
67
68 if (!isNaN(numA) && !isNaN(numB)) {
69 return currentSort.dir === 'asc' ? numA - numB : numB - numA;
70 }
71
72 // String sort
73 var cmp = valA.localeCompare(valB);
74 return currentSort.dir === 'asc' ? cmp : -cmp;
75 });
76
77 currentPage = 1;
78 render();
79 }
80
81 // Pagination
82 function renderPagination() {
83 var existing = wrap.querySelector('.bkbg-dt-pagination');
84 if (existing) existing.remove();
85
86 if (!paginate || filteredRows.length <= perPage) return;
87
88 var totalPages = Math.ceil(filteredRows.length / perPage);
89 var pagination = document.createElement('div');
90 pagination.className = 'bkbg-dt-pagination';
91
92 // Previous button
93 var prevBtn = document.createElement('button');
94 prevBtn.className = 'bkbg-dt-page-btn';
95 prevBtn.textContent = '';
96 prevBtn.disabled = currentPage === 1;
97 prevBtn.addEventListener('click', function () {
98 if (currentPage > 1) {
99 currentPage--;
100 render();
101 }
102 });
103 pagination.appendChild(prevBtn);
104
105 // Page buttons
106 var startPage = Math.max(1, currentPage - 2);
107 var endPage = Math.min(totalPages, startPage + 4);
108 if (endPage - startPage < 4) {
109 startPage = Math.max(1, endPage - 4);
110 }
111
112 for (var i = startPage; i <= endPage; i++) {
113 (function (page) {
114 var btn = document.createElement('button');
115 btn.className = 'bkbg-dt-page-btn' + (page === currentPage ? ' is-active' : '');
116 btn.textContent = page;
117 btn.addEventListener('click', function () {
118 currentPage = page;
119 render();
120 });
121 pagination.appendChild(btn);
122 })(i);
123 }
124
125 // Next button
126 var nextBtn = document.createElement('button');
127 nextBtn.className = 'bkbg-dt-page-btn';
128 nextBtn.textContent = '';
129 nextBtn.disabled = currentPage === totalPages;
130 nextBtn.addEventListener('click', function () {
131 if (currentPage < totalPages) {
132 currentPage++;
133 render();
134 }
135 });
136 pagination.appendChild(nextBtn);
137
138 // Info
139 var info = document.createElement('span');
140 info.className = 'bkbg-dt-page-info';
141 var start = (currentPage - 1) * perPage + 1;
142 var end = Math.min(currentPage * perPage, filteredRows.length);
143 info.textContent = start + '-' + end + ' of ' + filteredRows.length;
144 pagination.appendChild(info);
145
146 wrap.appendChild(pagination);
147 }
148
149 // Render visible rows
150 function render() {
151 if (!tbody) return;
152
153 // Reorder DOM elements to match sorted order
154 filteredRows.forEach(function (row) {
155 tbody.appendChild(row);
156 });
157
158 // Hide all rows first
159 allRows.forEach(function (row) {
160 row.style.display = 'none';
161 });
162
163 // Show filtered & paginated
164 var start = paginate ? (currentPage - 1) * perPage : 0;
165 var end = paginate ? start + perPage : filteredRows.length;
166
167 for (var i = start; i < end && i < filteredRows.length; i++) {
168 filteredRows[i].style.display = '';
169 }
170
171 // Empty state
172 var emptyEl = wrap.querySelector('.bkbg-dt-empty');
173 if (filteredRows.length === 0) {
174 if (!emptyEl) {
175 emptyEl = document.createElement('div');
176 emptyEl.className = 'bkbg-dt-empty';
177 emptyEl.textContent = 'No results found';
178 wrap.querySelector('.bkbg-dt-container').appendChild(emptyEl);
179 }
180 emptyEl.style.display = '';
181 } else if (emptyEl) {
182 emptyEl.style.display = 'none';
183 }
184
185 renderPagination();
186 }
187
188 // Export to CSV
189 function exportCsv() {
190 function csvCell(text) {
191 var s = String(text == null ? '' : text);
192 // Normalize whitespace/newlines for CSV rows.
193 s = s.replace(/\r?\n/g, ' ').trim();
194 // Prevent Excel/Sheets formula injection when opening CSV.
195 // See: values starting with = + - @ can be treated as formulas.
196 if (/^[=+\-@]/.test(s)) {
197 s = "'" + s;
198 }
199 s = s.replace(/"/g, '""');
200 return '"' + s + '"';
201 }
202
203 var headerRow = headers.map(function (th) {
204 return csvCell(th.textContent);
205 }).join(',');
206
207 var dataRows = filteredRows.map(function (row) {
208 return toArray(row.children).map(function (td) {
209 return csvCell(td.textContent);
210 }).join(',');
211 });
212
213 var csv = [headerRow].concat(dataRows).join('\n');
214 var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
215 var url = URL.createObjectURL(blob);
216 var link = document.createElement('a');
217 link.href = url;
218 link.download = 'table-data.csv';
219 link.click();
220 URL.revokeObjectURL(url);
221 }
222
223 // Event listeners
224 if (searchInput) {
225 var debounceTimer;
226 searchInput.addEventListener('input', function () {
227 clearTimeout(debounceTimer);
228 debounceTimer = setTimeout(function () {
229 filterRows(searchInput.value);
230 }, 200);
231 });
232 }
233
234 if (sortable) {
235 headers.forEach(function (th, index) {
236 th.addEventListener('click', function () {
237 sortRows(index);
238 });
239 th.style.cursor = 'pointer';
240 });
241 }
242
243 var exportBtn = wrap.querySelector('[data-export="csv"]');
244 if (exportBtn) {
245 exportBtn.addEventListener('click', function (e) {
246 e.preventDefault();
247 exportCsv();
248 });
249 }
250
251 // Initial render
252 render();
253 }
254
255 function init() {
256 var tables = document.querySelectorAll('.bkbg-dt-wrap:not([data-initialized])');
257 for (var i = 0; i < tables.length; i++) {
258 tables[i].setAttribute('data-initialized', '1');
259 initDataTable(tables[i]);
260 }
261 }
262
263 // Initialize on DOMContentLoaded
264 if (document.readyState === 'loading') {
265 document.addEventListener('DOMContentLoaded', init);
266 } else {
267 init();
268 }
269
270 // Also try on window load as fallback
271 window.addEventListener('load', init);
272 })();
273