PluginProbe
Tableberg – Simple Gutenberg Table Block / 1.1.5
Tableberg – Simple Gutenberg Table Block v1.1.5
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 / src / sorting.ts

sorting.ts in Tableberg – Simple Gutenberg Table Block 1.1.5, at src/sorting.ts

140 lines 3.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import {
2 Cell,
3 CellElement,
4 CellKey,
5 ColumnConfigs,
6 getCellKey,
7 SortableType,
8 TableConfig,
9 } from "./attributes";
10 import { getElementTextContent } from "./elements";
11
12 export type SortOrder = "asc" | "desc";
13
14 function getCellElements(
15 cells: Record<CellKey, Cell>,
16 row: number,
17 col: number
18 ): CellElement[] {
19 return cells[getCellKey(row, col)]?.elements || [];
20 }
21
22 export function tableHasMergedCells(cells: Record<CellKey, Cell>): boolean {
23 return Object.values(cells).some(cell => {
24 const rowSpan = cell.span?.rowSpan || 1;
25 const colSpan = cell.span?.colSpan || 1;
26 return rowSpan > 1 || colSpan > 1;
27 });
28 }
29
30 export function columnHasMultipleElements(
31 column: number,
32 cells: Record<CellKey, Cell>
33 ): boolean {
34 return Object.entries(cells).some(([key, cell]) => {
35 const [, col] = key.split(",").map(Number);
36 return col === column && (cell.elements?.length || 0) > 1;
37 });
38 }
39
40 export function isColumnSortable(
41 column: number,
42 cells: Record<CellKey, Cell>
43 ): boolean {
44 if (tableHasMergedCells(cells)) {
45 return false;
46 }
47
48 if (columnHasMultipleElements(column, cells)) {
49 return false;
50 }
51
52 return true;
53 }
54
55 export function sortRowsByColumn(
56 cells: Record<CellKey, Cell>,
57 totalRows: number,
58 table: TableConfig,
59 column: number,
60 sortType: SortableType,
61 order: SortOrder
62 ): number[] {
63 const { headerEnabled, footerEnabled } = table;
64
65 const headerRow = headerEnabled ? 0 : -1;
66 const footerRow = footerEnabled ? totalRows - 1 : -1;
67
68 const sortableRows: Array<{
69 index: number;
70 value: string | number | Date;
71 }> = [];
72
73 for (let row = 0; row < totalRows; row++) {
74 if (row === headerRow || row === footerRow) {
75 continue;
76 }
77
78 const cellElements = getCellElements(cells, row, column);
79
80 let rawValue = "";
81 if (cellElements.length > 0) {
82 rawValue = getElementTextContent(cellElements[0]);
83 }
84
85 let parsedValue: string | number | Date;
86 switch (sortType) {
87 case "number": {
88 const cleaned = rawValue.replace(/[^0-9.-]/g, "");
89 const num = parseFloat(cleaned);
90 parsedValue = isNaN(num) ? 0 : num;
91 break;
92 }
93 case "date": {
94 const date = new Date(rawValue);
95 parsedValue = isNaN(date.getTime()) ? new Date(0) : date;
96 break;
97 }
98 case "text":
99 default:
100 parsedValue = rawValue.toLowerCase();
101 }
102
103 sortableRows.push({ index: row, value: parsedValue });
104 }
105
106 sortableRows.sort(({ value: a }, { value: b }) => {
107 let result: number;
108
109 if (typeof a === "number" && typeof b === "number") {
110 result = a - b;
111 } else if (a instanceof Date && b instanceof Date) {
112 result = a.getTime() - b.getTime();
113 } else {
114 result = String(a).localeCompare(String(b));
115 }
116
117 return order === "asc" ? result : -result;
118 });
119
120 const result: number[] = [];
121
122 if (headerEnabled) {
123 result.push(0);
124 }
125
126 for (const { index } of sortableRows) {
127 result.push(index);
128 }
129
130 if (footerEnabled) {
131 result.push(totalRows - 1);
132 }
133
134 return result;
135 }
136
137 export function hasSortableColumns(columns: ColumnConfigs): boolean {
138 return columns.some(config => !!config?.sortable);
139 }
140