PluginProbe
Tableberg – Simple Gutenberg Table Block / 0.5.6
Tableberg – Simple Gutenberg Table Block v0.5.6
1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 trunk 0.0.2 0.2.1 0.3.2 0.3.3 0.4.1 0.5.0 0.5.1 0.5.2 0.5.3 0.5.4 0.5.5 0.5.6 0.5.7 All 42 releases
tableberg / includes / assets / js / frontend.js

frontend.js in Tableberg – Simple Gutenberg Table Block 0.5.6, at includes/assets/js/frontend.js

699 lines 21.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (() => {
2 "use strict";
3 document.addEventListener("DOMContentLoaded", () => {
4 const tables = document.querySelectorAll("[data-tableberg-responsive]");
5 if (tables.length) {
6 window.addEventListener("resize", () => {
7 tables.forEach(resizeTable);
8 });
9 tables.forEach(resizeTable);
10 }
11
12 const searchBoxes = document.querySelectorAll(
13 "[data-tableberg-search]",
14 );
15
16 if (searchBoxes.length) {
17 searchBoxes.forEach((searchBox) => {
18 searchBox.addEventListener("input", () => {
19 searchTable(searchBox);
20 });
21 });
22 }
23 });
24
25 /**
26 *
27 * @param {HTMLTableElement} table
28 */
29 function resizeTable(table) {
30 const opts = table.dataset;
31
32 if (
33 opts.tablebergMobileWidth &&
34 window.innerWidth <= opts.tablebergMobileWidth
35 ) {
36 if (opts.tablebergMobileMode === "stack") {
37 const renderMode =
38 "stack-" +
39 opts.tablebergMobileDirection +
40 "-" +
41 opts.tablebergMobileCount;
42 if (opts.tablebergMobileDirection === "row") {
43 toRowStack(
44 table,
45 opts.tablebergMobileHeader,
46 opts.tablebergMobileCount,
47 renderMode,
48 );
49 } else {
50 toColStack(
51 table,
52 opts.tablebergMobileHeader,
53 opts.tablebergMobileCount,
54 renderMode,
55 );
56 }
57 } else if (opts.tablebergMobileMode === "scroll") {
58 toScrollTable(table);
59 } else {
60 reviveTable(table);
61 }
62 } else if (
63 opts.tablebergTabletWidth &&
64 window.innerWidth <= opts.tablebergTabletWidth
65 ) {
66 if (opts.tablebergTabletMode === "stack") {
67 const renderMode =
68 "stack-" +
69 opts.tablebergTabletDirection +
70 "-" +
71 opts.tablebergTabletCount;
72 if (opts.tablebergTabletDirection === "row") {
73 toRowStack(
74 table,
75 opts.tablebergTabletHeader,
76 opts.tablebergTabletCount,
77 renderMode,
78 );
79 } else {
80 toColStack(
81 table,
82 opts.tablebergTabletHeader,
83 opts.tablebergTabletCount,
84 renderMode,
85 );
86 }
87 } else if (opts.tablebergTabletMode === "scroll") {
88 toScrollTable(table);
89 } else {
90 reviveTable(table);
91 }
92 } else {
93 reviveTable(table);
94 }
95 }
96
97 /**
98 *
99 * @param {HTMLTableElement} table
100 * @param {boolean} header
101 * @param {number} count
102 * @param {string} tag
103 */
104
105 function toRowStack(table, header, count, tag) {
106 if (table.dataset.tablebergLast === tag) {
107 return;
108 }
109 reviveTable(table);
110 setTableClassName(table, "tableberg-rowstack-table");
111 table.setAttribute("data-tableberg-last", tag);
112 const colGroup = table.querySelector("colgroup");
113 if (colGroup) {
114 colGroup.style.display = "none";
115 }
116
117 const cells = table.querySelectorAll("th,td");
118
119 const tbody = table.querySelector("tbody") || table;
120 tbody.innerHTML = "";
121
122 const masterRowMap = new Map();
123
124 const headerArr = [];
125 let colCount = Math.max(count || 1, 1);
126
127 const cols = parseInt(table.dataset.tablebergCols);
128
129 if (table.dataset.tablebergHeader) {
130 for (const cell of cells) {
131 if (cell.dataset.tablebergRow > 0) {
132 break;
133 }
134 markRowCell(cell, "header");
135 }
136 }
137
138 if (header) {
139 colCount++;
140 for (const cell of cells) {
141 if (cell.dataset.tablebergRow > 0) {
142 break;
143 }
144 headerArr.push(cell);
145 }
146 }
147
148 let rowCount = 0;
149 for (const cell of cells) {
150 const subRow = parseInt(cell.dataset.tablebergCol);
151 const masterRow = masterRowMap.get(subRow);
152
153 let row = parseInt(cell.dataset.tablebergRow);
154 if (table.dataset.tablebergHeader && row > 0) {
155 row++;
156 }
157
158 if (
159 table.dataset.tablebergFooter &&
160 ((table.dataset.tablebergHeader &&
161 row == table.dataset.tablebergRows) ||
162 (!table.dataset.tablebergHeader &&
163 row + 1 == table.dataset.tablebergRows))
164 ) {
165 markRowCell(cell, "footer");
166 } else if (row > 0) {
167 if (row % 2) {
168 markRowCell(cell, "even-row");
169 } else {
170 markRowCell(cell, "odd-row");
171 }
172 }
173
174 if (!masterRow) {
175 const rowEl = document.createElement("tr");
176 masterRowMap.set(subRow, {
177 lastRow: rowCount,
178 count: 1,
179 lastRowEL: rowEl,
180 });
181 rowEl.appendChild(cell);
182 tbody.appendChild(rowEl);
183 rowCount++;
184 } else if (masterRow.count == colCount) {
185 const rowEl = document.createElement("tr");
186 tbody.appendChild(rowEl);
187
188 let thisRowColCount = 1;
189 if (header) {
190 const headerCell = headerArr[subRow].cloneNode(true);
191 headerCell.setAttribute("data-tableberg-tmp", "1");
192 rowEl.appendChild(headerCell);
193 thisRowColCount++;
194 }
195 masterRowMap.set(subRow, {
196 lastRow: rowCount,
197 count: thisRowColCount,
198 lastRowEL: rowEl,
199 });
200
201 rowEl.appendChild(cell);
202
203 if (rowCount % cols === 0) {
204 rowEl.style.borderTop = "3px solid gray";
205 }
206 rowCount++;
207 } else {
208 masterRow.count++;
209 masterRow.lastRowEL.appendChild(cell);
210 }
211 }
212 }
213
214 /**
215 *
216 * @param {HTMLTableElement} table
217 * @param {boolean} header
218 * @param {number} count
219 * @param {string} tag
220 */
221
222 function toColStack(table, header, count, tag) {
223 const oldMode = table.dataset.tablebergLast;
224 if (oldMode === tag) {
225 return;
226 }
227 if (!header) {
228 return;
229 }
230 count = parseInt(count);
231 reviveTable(table);
232
233 table.setAttribute("data-tableberg-last", tag);
234
235 setTableClassName(table, "tableberg-colstack-table");
236
237 const cells = Array.from(table.querySelectorAll("th,td"));
238
239 if (oldMode && oldMode.match("stack-row")) {
240 cells.sort((a, b) => {
241 const aRow = parseInt(a.dataset.tablebergRow);
242 const bRow = parseInt(b.dataset.tablebergRow);
243 const diff1 = aRow - bRow;
244 if (diff1 !== 0) {
245 return diff1;
246 }
247 const aCol = parseInt(a.dataset.tablebergCol);
248 const bCol = parseInt(b.dataset.tablebergCol);
249 return aCol - bCol;
250 });
251 }
252
253 const tbody = table.querySelector("tbody") || table;
254 tbody.innerHTML = "";
255
256 const headerArr = [];
257 let stackRowCount = Math.max(count || 1, 1);
258
259 let rowIdxStart = 0;
260 let rowCount = -1,
261 lastRow = -1,
262 stackTrack = 0,
263 lastRowEl;
264
265 if (header) {
266 stackRowCount++;
267 rowCount++;
268 lastRowEl = document.createElement("tr");
269 markRow(lastRowEl, "header");
270 tbody.appendChild(lastRowEl);
271 stackTrack++;
272
273 for (; rowIdxStart < cells.length; rowIdxStart++) {
274 const cell = cells[rowIdxStart];
275 if (cell.dataset.tablebergRow > 0) {
276 break;
277 }
278 lastRowEl.appendChild(cell);
279 headerArr.push(cell);
280 }
281 }
282
283 const footer = table.dataset.tablebergFooter;
284 const footerArr = [];
285
286 if (footer) {
287 const fRow = parseInt(table.dataset.tablebergRows) - 1;
288 let i = cells.length - 1;
289 for (; i > -1; i--) {
290 if (cells[i].dataset.tablebergRow < fRow) {
291 break;
292 }
293 footerArr.unshift(cells[i]);
294 }
295 cells.splice(i + 1);
296 }
297
298 for (let idx = rowIdxStart; idx < cells.length; idx++) {
299 const cell = cells[idx];
300
301 if (lastRow != cell.dataset.tablebergRow) {
302 lastRow = cell.dataset.tablebergRow;
303
304 let row = parseInt(lastRow);
305
306 if (header) {
307 row++;
308 if (stackTrack == stackRowCount) {
309 rowCount++;
310
311 lastRowEl = document.createElement("tr");
312 markRow(lastRowEl, "header");
313 lastRowEl.setAttribute("data-tableberg-tmp", "1");
314 tbody.appendChild(lastRowEl);
315
316 stackTrack = 1;
317
318 for (const cell of headerArr) {
319 lastRowEl.appendChild(cell.cloneNode(true));
320 }
321 }
322 }
323 rowCount++;
324 lastRowEl = document.createElement("tr");
325
326 if (row % 2) {
327 markRow(lastRowEl, "even-row");
328 } else {
329 markRow(lastRowEl, "odd-row");
330 }
331 tbody.appendChild(lastRowEl);
332 stackTrack++;
333 }
334
335 lastRowEl.appendChild(cell);
336 }
337
338 if (footerArr.length > 0) {
339 lastRowEl = document.createElement("tr");
340 markRow(lastRowEl, "footer");
341 tbody.appendChild(lastRowEl);
342
343 footerArr.forEach((cell) => lastRowEl.appendChild(cell));
344 }
345 }
346
347 /**
348 *
349 * @param {HTMLTableElement} table
350 * @returns
351 */
352
353 function reviveTable(table) {
354 const oldMode = table.dataset.tablebergLast;
355 if (!oldMode) {
356 return;
357 }
358 table.removeAttribute("data-tableberg-last");
359 table.parentElement.classList.remove("tableberg-scroll-x");
360
361 table
362 .querySelectorAll("[data-tableberg-tmp]")
363 .forEach((el) => el.remove());
364
365 if (!oldMode || !oldMode.match("stack")) {
366 return;
367 }
368
369 setTableClassName(table);
370
371 const colGroup = table.querySelector("colgroup");
372 if (colGroup) {
373 colGroup.removeAttribute("style");
374 }
375
376 const cells = Array.from(table.querySelectorAll("th,td"));
377 cells.sort((a, b) => {
378 const aRow = parseInt(a.dataset.tablebergRow);
379 const bRow = parseInt(b.dataset.tablebergRow);
380 const diff1 = aRow - bRow;
381 if (diff1 !== 0) {
382 return diff1;
383 }
384 const aCol = parseInt(a.dataset.tablebergCol);
385 const bCol = parseInt(b.dataset.tablebergCol);
386 return aCol - bCol;
387 });
388
389 const tbody = table.querySelector("tbody") || table;
390 tbody.innerHTML = "";
391
392 let lastRow = -1,
393 lastRowEl;
394
395 for (const cell of cells) {
396 if (lastRow != cell.dataset.tablebergRow) {
397 lastRow = cell.dataset.tablebergRow;
398 lastRowEl = document.createElement("tr");
399
400 let row = parseInt(lastRow);
401 if (table.dataset.tablebergHeader) {
402 if (row === 0) {
403 markRow(lastRowEl, "header");
404 } else {
405 row++;
406 }
407 }
408
409 if (
410 table.dataset.tablebergFooter &&
411 row == table.dataset.tablebergRows
412 ) {
413 markRow(lastRowEl, "footer");
414 } else if (row > 0) {
415 if (row % 2) {
416 markRow(lastRowEl, "even-row");
417 } else {
418 markRow(lastRowEl, "odd-row");
419 }
420 }
421
422 tbody.appendChild(lastRowEl);
423 }
424
425 lastRowEl.appendChild(cell);
426 }
427 }
428
429 function toScrollTable(table) {
430 const opts = table.dataset;
431 if (opts.tablebergLast === "scroll") {
432 return;
433 }
434 if (opts.tablebergLast) {
435 reviveTable(table);
436 }
437 table.setAttribute("data-tableberg-last", "scroll");
438 table.parentElement.classList.add("tableberg-scroll-x");
439 }
440
441 /**
442 *
443 * @param {HTMLTableCellElement} cell
444 * @param {"even-row" | "odd-row" | "header" | "footer"} oddEven
445 */
446
447 function markRowCell(cell, oddEven) {
448 [
449 "tableberg-even-row-cell",
450 "tableberg-header-cell",
451 "tableberg-footer-cell",
452 "tableberg-odd-row-cell",
453 ].forEach((className) => cell.classList.remove(className));
454 cell.classList.add(`tableberg-${oddEven}-cell`);
455 }
456
457 /**
458 *
459 * @param {HTMLTableRowElement} row
460 * @param {"even-row" | "odd-row" | "header" | "footer"} oddEven
461 */
462
463 function markRow(row, oddEven) {
464 [
465 "tableberg-even-row",
466 "tableberg-header",
467 "tableberg-footer",
468 "tableberg-odd-row",
469 ].forEach((className) => row.classList.remove(className));
470 row.classList.add(`tableberg-${oddEven}`);
471 }
472
473 /**
474 *
475 * @param {HTMLTableElement} table
476 * @param {string} className
477 */
478 function setTableClassName(table, className) {
479 ["tableberg-rowstack-table", "tableberg-rowstack-table"].forEach(
480 (className) => table.classList.remove(className),
481 );
482 className && table.classList.add(className);
483 }
484
485 function searchTable(input) {
486 const search = input.value.trim();
487 const rows = input.parentElement.parentElement.querySelectorAll("tr");
488 if (!search || search.length < 3) {
489 if (input.dataset.isDirty) {
490 input.dataset.isDirty = false;
491 Array.from(rows).forEach((row) => {
492 row.style.display = "table-row";
493 });
494 }
495 return;
496 }
497 Array.from(rows).forEach((row) => {
498 if (
499 row.querySelectorAll("th").length > 1 ||
500 row.textContent?.includes(search)
501 ) {
502 row.style.display = "table-row";
503 } else {
504 row.style.display = "none";
505 }
506 });
507 input.dataset.isDirty = true;
508 }
509
510 const vSortBtns = document.querySelectorAll(".tableberg-v-sorter");
511
512 vSortBtns.forEach((btn) => {
513 btn.addEventListener("click", vSortTable);
514 });
515
516 const hSortBtns = document.querySelectorAll(".tableberg-h-sorter");
517
518 hSortBtns.forEach((btn) => {
519 btn.addEventListener("click", hSortTable);
520 });
521
522 /**
523 *
524 * @param {HTMLTableRowElement} row
525 * @param {number} colIndex
526 * @returns
527 */
528 function getCellValue(row, colIndex) {
529 let cellIndex = 0;
530 for (let cell of row.cells) {
531 if (cellIndex === colIndex)
532 return cell.textContent.trim().toLowerCase();
533 cellIndex += cell.colSpan;
534 }
535 return "";
536 }
537
538 function vSortTable() {
539 const table = this.closest("table");
540 const byCol = parseInt(this.parentElement.dataset.tablebergCol);
541 if (!table) return;
542
543 const rows = Array.from(table.tBodies[0].rows);
544 const indexedRows = rows.map((row, index) => ({
545 originalIndex: index,
546 content: getCellValue(row, byCol),
547 }));
548
549 let carry = 0;
550
551 if (table.dataset.tablebergHeader) {
552 indexedRows.splice(0, 1);
553 carry = 1;
554 }
555
556 if (table.dataset.tablebergFooter) {
557 indexedRows.pop();
558 }
559 const newOrder =
560 table.dataset.vSortedBy == byCol && table.dataset.vOrder === "asc"
561 ? "desc"
562 : "asc";
563
564 if (newOrder === "asc") {
565 indexedRows.sort((a, b) =>
566 a.content.localeCompare(b.content, undefined, {
567 numeric: true,
568 }),
569 );
570 } else {
571 indexedRows.sort((a, b) =>
572 b.content.localeCompare(a.content, undefined, {
573 numeric: true,
574 }),
575 );
576 }
577
578 const indexMap = {};
579
580 indexedRows.forEach((item, newIndex) => {
581 indexMap[newIndex + carry] = item.originalIndex;
582 });
583
584 const sortedRows = [];
585 for (let i = 0; i < rows.length; i++) {
586 const row = rows[indexMap[i] || i];
587 sortedRows.push(row);
588 for (const cell of row.cells) {
589 cell.dataset.tablebergRow = i;
590 }
591 }
592
593 const tbody = table.tBodies[0];
594 sortedRows.forEach((row) => tbody.appendChild(row));
595
596 table.dataset.vSortedBy = byCol;
597 table.dataset.vOrder = newOrder;
598
599 this.closest("tr")
600 .querySelectorAll(".tableberg-v-sorter")
601 .forEach((btn) => {
602 btn.remove();
603 });
604
605 let index = 0;
606 for (const cell of tbody.rows[0].cells) {
607 const btn = document.createElement("button");
608 btn.classList.add("tableberg-v-sorter");
609 if (index === byCol) {
610 btn.classList.add(`tableberg-${newOrder}`);
611 }
612 btn.addEventListener("click", vSortTable);
613 cell.appendChild(btn);
614 index++;
615 }
616 }
617
618 function hSortTable() {
619 const table = this.closest("table");
620 const byRow = parseInt(this.parentElement.dataset.tablebergRow);
621 if (!table) return;
622
623 const cols = Array.from(table.tBodies[0].rows[byRow].cells);
624 const indexedCols = [];
625
626 let lastIdx = 0;
627 cols.forEach((col) => {
628 indexedCols.push({
629 originalIndex: lastIdx++,
630 content: col.textContent.trim().toLowerCase(),
631 });
632
633 for (let i = 1; i < col.colSpan; i++) {
634 indexedCols.push({
635 originalIndex: lastIdx++,
636 content: "",
637 });
638 }
639 });
640
641 const newOrder =
642 table.dataset.hSortedBy == byRow && table.dataset.hOrder === "asc"
643 ? "desc"
644 : "asc";
645
646 if (newOrder === "asc") {
647 indexedCols.sort((a, b) => {
648 if (a.content < b.content) return -1;
649 if (a.content > b.content) return 1;
650 return 0;
651 });
652 } else {
653 indexedCols.sort((a, b) => {
654 if (a.content < b.content) return 1;
655 if (a.content > b.content) return -1;
656 return 0;
657 });
658 }
659
660 const indexMap = {};
661
662 indexedCols.forEach((item, newIndex) => {
663 indexMap[item.originalIndex] = newIndex;
664 });
665
666 const tbody = table.tBodies[0];
667
668 table.dataset.hSortedBy = byRow;
669 table.dataset.hOrder = newOrder;
670
671 table.querySelectorAll(".tableberg-h-sorter").forEach((btn) => {
672 btn.remove();
673 });
674
675 let index = 0;
676 for (const row of tbody.rows) {
677
678 const sortedCells = [];
679 for (const cell of row.cells) {
680 const idx = indexMap[cell.dataset.tablebergCol];
681 sortedCells[idx] = cell;
682 cell.dataset.tablebergCol = idx;
683 }
684
685 sortedCells.forEach((cell) => cell && row.appendChild(cell));
686
687 const cell = sortedCells[0];
688 const btn = document.createElement("button");
689 btn.classList.add("tableberg-h-sorter");
690 if (index === byRow) {
691 btn.classList.add(`tableberg-${newOrder}`);
692 }
693 btn.addEventListener("click", hSortTable);
694 cell.appendChild(btn);
695 index++;
696 }
697 }
698 })();
699