@inglorious/web 4.4.0 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -221
- package/package.json +5 -28
- package/src/form/handlers.js +332 -0
- package/src/form/helpers.js +32 -0
- package/src/form/index.js +4 -373
- package/src/index.js +1 -0
- package/src/router/handlers.js +345 -0
- package/src/router/helpers.js +46 -0
- package/src/router/index.js +5 -384
- package/src/test.js +1 -1
- package/types/index.d.ts +0 -3
- package/src/list/index.js +0 -7
- package/src/list/logic.js +0 -75
- package/src/list/rendering.js +0 -105
- package/src/select/base.css +0 -52
- package/src/select/index.js +0 -7
- package/src/select/logic.js +0 -332
- package/src/select/rendering.js +0 -352
- package/src/select/theme.css +0 -133
- package/src/table/base.css +0 -34
- package/src/table/filters/date.js +0 -42
- package/src/table/filters/number.js +0 -22
- package/src/table/filters/range.js +0 -40
- package/src/table/filters/select.js +0 -34
- package/src/table/filters/text.js +0 -22
- package/src/table/filters.js +0 -24
- package/src/table/index.js +0 -7
- package/src/table/logic.js +0 -573
- package/src/table/rendering.js +0 -340
- package/src/table/theme.css +0 -83
- package/types/list.d.ts +0 -91
- package/types/select.d.ts +0 -136
- package/types/table.d.ts +0 -248
package/src/table/logic.js
DELETED
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-magic-numbers */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('../../types/table').TableEntity} TableEntity
|
|
5
|
-
* @typedef {import('../../types/table').TableColumn} TableColumn
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export const logic = {
|
|
9
|
-
/**
|
|
10
|
-
* Resets the table entity with default state.
|
|
11
|
-
* @param {TableEntity} entity
|
|
12
|
-
*/
|
|
13
|
-
create(entity) {
|
|
14
|
-
initTable(entity)
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Toggles sorting for a column.
|
|
19
|
-
* @param {TableEntity} entity
|
|
20
|
-
* @param {string} columnId
|
|
21
|
-
*/
|
|
22
|
-
sortChange(entity, columnId) {
|
|
23
|
-
const column = entity.columns.find((c) => c.id === columnId)
|
|
24
|
-
if (!column?.isSortable) return
|
|
25
|
-
|
|
26
|
-
const existingIndex = entity.sorts.findIndex((s) => s.column === columnId)
|
|
27
|
-
|
|
28
|
-
if (existingIndex !== -1) {
|
|
29
|
-
// Toggle direction
|
|
30
|
-
const existing = entity.sorts[existingIndex]
|
|
31
|
-
if (existing.direction === "asc") {
|
|
32
|
-
existing.direction = "desc"
|
|
33
|
-
} else {
|
|
34
|
-
// Remove from sort if going from desc back to nothing
|
|
35
|
-
entity.sorts.splice(existingIndex, 1)
|
|
36
|
-
}
|
|
37
|
-
} else {
|
|
38
|
-
// Add new sort
|
|
39
|
-
entity.sorts.push({ column: columnId, direction: "asc" })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (entity.pagination) {
|
|
43
|
-
entity.pagination.page = 0
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Clears all sorts.
|
|
49
|
-
* @param {TableEntity} entity
|
|
50
|
-
*/
|
|
51
|
-
sortsClear(entity) {
|
|
52
|
-
entity.sorts = []
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Updates a filter value for a column.
|
|
57
|
-
* @param {TableEntity} entity
|
|
58
|
-
* @param {{ columnId: string, value: any }} payload
|
|
59
|
-
*/
|
|
60
|
-
filterChange(entity, { columnId, value }) {
|
|
61
|
-
if (value == null || value === "") {
|
|
62
|
-
delete entity.filters[columnId]
|
|
63
|
-
} else {
|
|
64
|
-
entity.filters[columnId] = value
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Reset to first page when filtering
|
|
68
|
-
if (entity.pagination) {
|
|
69
|
-
entity.pagination.page = 0
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Clears all filters.
|
|
75
|
-
* @param {TableEntity} entity
|
|
76
|
-
*/
|
|
77
|
-
filtersClear(entity) {
|
|
78
|
-
entity.filters = {}
|
|
79
|
-
if (entity.pagination) {
|
|
80
|
-
entity.pagination.page = 0
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Updates the search term.
|
|
86
|
-
* @param {TableEntity} entity
|
|
87
|
-
* @param {string} search
|
|
88
|
-
*/
|
|
89
|
-
searchChange(entity, search) {
|
|
90
|
-
entity.search.value = search
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Changes the current page.
|
|
95
|
-
* @param {TableEntity} entity
|
|
96
|
-
* @param {number} page
|
|
97
|
-
*/
|
|
98
|
-
pageChange(entity, page) {
|
|
99
|
-
if (!entity.pagination) return
|
|
100
|
-
|
|
101
|
-
const totalPages = Math.ceil(
|
|
102
|
-
getTotalRows(entity) / entity.pagination.pageSize,
|
|
103
|
-
)
|
|
104
|
-
entity.pagination.page = Math.max(0, Math.min(page, totalPages - 1))
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Moves to the next page.
|
|
109
|
-
* @param {TableEntity} entity
|
|
110
|
-
*/
|
|
111
|
-
pageNext(entity) {
|
|
112
|
-
if (!entity.pagination) return
|
|
113
|
-
const totalPages = Math.ceil(
|
|
114
|
-
getTotalRows(entity) / entity.pagination.pageSize,
|
|
115
|
-
)
|
|
116
|
-
entity.pagination.page = Math.min(
|
|
117
|
-
entity.pagination.page + 1,
|
|
118
|
-
totalPages - 1,
|
|
119
|
-
)
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Moves to the previous page.
|
|
124
|
-
* @param {TableEntity} entity
|
|
125
|
-
*/
|
|
126
|
-
pagePrev(entity) {
|
|
127
|
-
if (!entity.pagination) return
|
|
128
|
-
entity.pagination.page = Math.max(entity.pagination.page - 1, 0)
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Changes the page size.
|
|
133
|
-
* @param {TableEntity} entity
|
|
134
|
-
* @param {number} pageSize
|
|
135
|
-
*/
|
|
136
|
-
pageSizeChange(entity, pageSize) {
|
|
137
|
-
if (!entity.pagination) return
|
|
138
|
-
|
|
139
|
-
entity.pagination.pageSize = pageSize
|
|
140
|
-
entity.pagination.page = 0
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Selects a row.
|
|
145
|
-
* @param {TableEntity} entity
|
|
146
|
-
* @param {string|number} rowId
|
|
147
|
-
*/
|
|
148
|
-
rowSelect(entity, rowId) {
|
|
149
|
-
if (!entity.isMultiSelect) {
|
|
150
|
-
entity.selection = []
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (!entity.selection.includes(rowId)) {
|
|
154
|
-
entity.selection.push(rowId)
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Deselects a row.
|
|
160
|
-
* @param {TableEntity} entity
|
|
161
|
-
* @param {string|number} rowId
|
|
162
|
-
*/
|
|
163
|
-
rowDeselect(entity, rowId) {
|
|
164
|
-
const index = entity.selection.indexOf(rowId)
|
|
165
|
-
if (index !== -1) {
|
|
166
|
-
entity.selection.splice(index, 1)
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Toggles row selection.
|
|
172
|
-
* @param {TableEntity} entity
|
|
173
|
-
* @param {string|number} rowId
|
|
174
|
-
*/
|
|
175
|
-
rowToggle(entity, rowId) {
|
|
176
|
-
const index = entity.selection.indexOf(rowId)
|
|
177
|
-
|
|
178
|
-
if (index === -1) {
|
|
179
|
-
if (!entity.isMultiSelect) {
|
|
180
|
-
entity.selection = [rowId] // Replace entirely
|
|
181
|
-
} else {
|
|
182
|
-
entity.selection.push(rowId)
|
|
183
|
-
}
|
|
184
|
-
} else {
|
|
185
|
-
entity.selection.splice(index, 1)
|
|
186
|
-
}
|
|
187
|
-
},
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Toggles selection of all currently visible rows.
|
|
191
|
-
* @param {TableEntity} entity
|
|
192
|
-
*/
|
|
193
|
-
rowsToggleAll(entity) {
|
|
194
|
-
const rows = getRows(entity)
|
|
195
|
-
const allSelected = rows.every((row) => entity.selection.includes(row.id))
|
|
196
|
-
|
|
197
|
-
if (allSelected) {
|
|
198
|
-
// Deselect all visible
|
|
199
|
-
rows.forEach((row) => {
|
|
200
|
-
const index = entity.selection.indexOf(row.id)
|
|
201
|
-
if (index !== -1) entity.selection.splice(index, 1)
|
|
202
|
-
})
|
|
203
|
-
} else {
|
|
204
|
-
// Select all visible
|
|
205
|
-
rows.forEach((row) => {
|
|
206
|
-
if (!entity.selection.includes(row.id)) {
|
|
207
|
-
entity.selection.push(row.id)
|
|
208
|
-
}
|
|
209
|
-
})
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Selects all currently visible rows.
|
|
215
|
-
* @param {TableEntity} entity
|
|
216
|
-
*/
|
|
217
|
-
rowsSelectAll(entity) {
|
|
218
|
-
const rows = getRows(entity)
|
|
219
|
-
rows.forEach((row) => {
|
|
220
|
-
if (!entity.selection.includes(row.id)) {
|
|
221
|
-
entity.selection.push(row.id)
|
|
222
|
-
}
|
|
223
|
-
})
|
|
224
|
-
},
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Clears all row selections.
|
|
228
|
-
* @param {TableEntity} entity
|
|
229
|
-
*/
|
|
230
|
-
selectionClear(entity) {
|
|
231
|
-
entity.selection.length = 0
|
|
232
|
-
},
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Helper functions
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Gets the processed rows (filtered, searched, sorted, paginated).
|
|
239
|
-
* @param {TableEntity} entity
|
|
240
|
-
* @returns {any[]}
|
|
241
|
-
*/
|
|
242
|
-
export function getRows(entity) {
|
|
243
|
-
let rows = entity.data
|
|
244
|
-
rows = applyFilters(entity, rows)
|
|
245
|
-
rows = applySearch(entity, rows)
|
|
246
|
-
rows = applySorts(entity, rows)
|
|
247
|
-
rows = applyPagination(entity, rows)
|
|
248
|
-
|
|
249
|
-
return rows
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Gets the total number of rows after filtering and searching.
|
|
254
|
-
* @param {TableEntity} entity
|
|
255
|
-
* @returns {number}
|
|
256
|
-
*/
|
|
257
|
-
export function getTotalRows(entity) {
|
|
258
|
-
let rows = entity.data
|
|
259
|
-
rows = applyFilters(entity, rows)
|
|
260
|
-
rows = applySearch(entity, rows)
|
|
261
|
-
return rows.length
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Gets pagination information.
|
|
266
|
-
* @param {TableEntity} entity
|
|
267
|
-
* @returns {object|null}
|
|
268
|
-
*/
|
|
269
|
-
export function getPaginationInfo(entity) {
|
|
270
|
-
if (!entity.pagination) return null
|
|
271
|
-
|
|
272
|
-
const totalRows = getTotalRows(entity)
|
|
273
|
-
const { page, pageSize } = entity.pagination
|
|
274
|
-
const totalPages = Math.ceil(totalRows / pageSize)
|
|
275
|
-
const start = page * pageSize
|
|
276
|
-
const end = Math.min((page + 1) * pageSize, totalRows)
|
|
277
|
-
|
|
278
|
-
return {
|
|
279
|
-
page,
|
|
280
|
-
pageSize,
|
|
281
|
-
totalPages,
|
|
282
|
-
totalRows,
|
|
283
|
-
start,
|
|
284
|
-
end,
|
|
285
|
-
hasNextPage: page < totalPages - 1,
|
|
286
|
-
hasPrevPage: page > 0,
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Gets the sort direction for a column.
|
|
292
|
-
* @param {TableEntity} entity
|
|
293
|
-
* @param {string} columnId
|
|
294
|
-
* @returns {"asc"|"desc"|null}
|
|
295
|
-
*/
|
|
296
|
-
export function getSortDirection(entity, columnId) {
|
|
297
|
-
const sort = entity.sorts.find((s) => s.column === columnId)
|
|
298
|
-
return sort?.direction || null
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Gets the sort index (priority) for a column.
|
|
303
|
-
* @param {TableEntity} entity
|
|
304
|
-
* @param {string} columnId
|
|
305
|
-
* @returns {number}
|
|
306
|
-
*/
|
|
307
|
-
export function getSortIndex(entity, columnId) {
|
|
308
|
-
return entity.sorts.findIndex((s) => s.column === columnId)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Gets the filter value for a column.
|
|
313
|
-
* @param {TableEntity} entity
|
|
314
|
-
* @param {string} columnId
|
|
315
|
-
* @returns {any}
|
|
316
|
-
*/
|
|
317
|
-
export function getFilter(entity, columnId) {
|
|
318
|
-
return entity.filters[columnId]
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Checks if a row is selected.
|
|
323
|
-
* @param {TableEntity} entity
|
|
324
|
-
* @param {string|number} rowId
|
|
325
|
-
* @returns {boolean}
|
|
326
|
-
*/
|
|
327
|
-
export function isRowSelected(entity, rowId) {
|
|
328
|
-
return entity.selection.includes(rowId)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Checks if all visible rows are selected.
|
|
333
|
-
* @param {TableEntity} entity
|
|
334
|
-
* @returns {boolean}
|
|
335
|
-
*/
|
|
336
|
-
export function isAllSelected(entity) {
|
|
337
|
-
const rows = getRows(entity)
|
|
338
|
-
return rows.length && rows.every((row) => entity.selection.includes(row.id))
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Checks if some (but not all) visible rows are selected.
|
|
343
|
-
* @param {TableEntity} entity
|
|
344
|
-
* @returns {boolean}
|
|
345
|
-
*/
|
|
346
|
-
export function isSomeSelected(entity) {
|
|
347
|
-
const rows = getRows(entity)
|
|
348
|
-
const selectedCount = rows.filter((row) =>
|
|
349
|
-
entity.selection.includes(row.id),
|
|
350
|
-
).length
|
|
351
|
-
return selectedCount && selectedCount < rows.length
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Private helper functions
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Initializes the table state.
|
|
358
|
-
* @param {TableEntity} entity
|
|
359
|
-
*/
|
|
360
|
-
function initTable(entity) {
|
|
361
|
-
entity.data ??= []
|
|
362
|
-
|
|
363
|
-
// Auto-generate columns from first data item if not provided
|
|
364
|
-
if (!entity.columns && entity.data.length) {
|
|
365
|
-
const [firstRow] = entity.data
|
|
366
|
-
|
|
367
|
-
entity.columns = Object.keys(firstRow).map((key) => {
|
|
368
|
-
const value = firstRow[key]
|
|
369
|
-
const type = getDefaultColumnType(value)
|
|
370
|
-
const filter = getDefaultColumnFilter(type)
|
|
371
|
-
|
|
372
|
-
return {
|
|
373
|
-
id: key,
|
|
374
|
-
title: capitalize(key),
|
|
375
|
-
type,
|
|
376
|
-
isSortable: false,
|
|
377
|
-
isFilterable: false,
|
|
378
|
-
filter,
|
|
379
|
-
width: getDefaultColumnWidth(filter.type),
|
|
380
|
-
}
|
|
381
|
-
})
|
|
382
|
-
} else {
|
|
383
|
-
entity.columns ??= []
|
|
384
|
-
entity.columns.forEach((column) => {
|
|
385
|
-
column.title ??= capitalize(column.id)
|
|
386
|
-
column.type ??= getDefaultColumnType()
|
|
387
|
-
column.filter ??= getDefaultColumnFilter(column.type)
|
|
388
|
-
column.width ??= getDefaultColumnWidth(column.filter.type)
|
|
389
|
-
})
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// State
|
|
393
|
-
entity.sorts ??= []
|
|
394
|
-
entity.filters ??= {}
|
|
395
|
-
entity.search ??= null
|
|
396
|
-
if (entity.search) {
|
|
397
|
-
entity.search.value ??= ""
|
|
398
|
-
}
|
|
399
|
-
entity.selection ??= []
|
|
400
|
-
|
|
401
|
-
entity.pagination ??= null
|
|
402
|
-
if (entity.pagination) {
|
|
403
|
-
entity.pagination.page ??= 0
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Infers the column type from a value.
|
|
409
|
-
* @param {any} value
|
|
410
|
-
* @returns {string}
|
|
411
|
-
*/
|
|
412
|
-
function getDefaultColumnType(value) {
|
|
413
|
-
if (typeof value === "number") return "number"
|
|
414
|
-
if (typeof value === "boolean") return "boolean"
|
|
415
|
-
if (value instanceof Date) return "date"
|
|
416
|
-
return "string"
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Gets the default filter configuration for a column type.
|
|
421
|
-
* @param {string} type
|
|
422
|
-
* @returns {object}
|
|
423
|
-
*/
|
|
424
|
-
function getDefaultColumnFilter(type) {
|
|
425
|
-
if (type === "number") return { type: "range" }
|
|
426
|
-
if (type === "boolean")
|
|
427
|
-
return { type: "select", options: [null, true, false] }
|
|
428
|
-
if (type === "date") return { type: "date" }
|
|
429
|
-
return { type: "text" }
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Gets the default column width based on filter type.
|
|
434
|
-
* @param {string} filterType
|
|
435
|
-
* @returns {number}
|
|
436
|
-
*/
|
|
437
|
-
function getDefaultColumnWidth(filterType) {
|
|
438
|
-
if (filterType === "number") return 70
|
|
439
|
-
if (filterType === "range") return 100
|
|
440
|
-
if (filterType === "select") return 70
|
|
441
|
-
if (filterType === "date") return 120
|
|
442
|
-
if (filterType === "time") return 120
|
|
443
|
-
if (filterType === "datetime") return 170
|
|
444
|
-
return 200
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Applies filters to the data.
|
|
449
|
-
* @param {TableEntity} entity
|
|
450
|
-
* @param {any[]} rows
|
|
451
|
-
* @returns {any[]}
|
|
452
|
-
*/
|
|
453
|
-
function applyFilters(entity, rows) {
|
|
454
|
-
if (!Object.keys(entity.filters).length) {
|
|
455
|
-
return rows
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
return rows.filter((row) => {
|
|
459
|
-
return Object.entries(entity.filters).every(([columnId, filterValue]) => {
|
|
460
|
-
const column = entity.columns.find((c) => c.id === columnId)
|
|
461
|
-
if (!column) return true
|
|
462
|
-
|
|
463
|
-
// Custom filter function
|
|
464
|
-
if (column.filterFn) {
|
|
465
|
-
return column.filterFn(row, filterValue)
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Default filters by type
|
|
469
|
-
const value = row[columnId]
|
|
470
|
-
|
|
471
|
-
if (["range", "date", "time", "datetime"].includes(column.filter.type)) {
|
|
472
|
-
const { min, max } = filterValue
|
|
473
|
-
if (min != null && value < min) return false
|
|
474
|
-
if (max != null && value > max) return false
|
|
475
|
-
return true
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
if (["number", "boolean", "select"].includes(column.filter.type)) {
|
|
479
|
-
return value === filterValue
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// String filtering (case-insensitive contains)
|
|
483
|
-
return String(value)
|
|
484
|
-
.toLowerCase()
|
|
485
|
-
.includes(String(filterValue).toLowerCase())
|
|
486
|
-
})
|
|
487
|
-
})
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
/**
|
|
491
|
-
* Applies search to the data.
|
|
492
|
-
* @param {TableEntity} entity
|
|
493
|
-
* @param {any[]} rows
|
|
494
|
-
* @returns {any[]}
|
|
495
|
-
*/
|
|
496
|
-
function applySearch(entity, rows) {
|
|
497
|
-
if (!entity.search?.value) {
|
|
498
|
-
return rows
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
const searchLower = entity.search.value.toLowerCase()
|
|
502
|
-
|
|
503
|
-
return rows.filter((row) =>
|
|
504
|
-
entity.columns.some((column) => {
|
|
505
|
-
const value = row[column.id]
|
|
506
|
-
const formattedValue = column.format?.(value) ?? String(value)
|
|
507
|
-
return formattedValue.toLowerCase().includes(searchLower)
|
|
508
|
-
}),
|
|
509
|
-
)
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* Applies sorting to the data.
|
|
514
|
-
* @param {TableEntity} entity
|
|
515
|
-
* @param {any[]} rows
|
|
516
|
-
* @returns {any[]}
|
|
517
|
-
*/
|
|
518
|
-
function applySorts(entity, rows) {
|
|
519
|
-
if (!entity.sorts.length) {
|
|
520
|
-
return rows
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
return [...rows].sort((a, b) => {
|
|
524
|
-
for (const { column: columnId, direction } of entity.sorts) {
|
|
525
|
-
const column = entity.columns.find((c) => c.id === columnId)
|
|
526
|
-
let aVal = a[columnId]
|
|
527
|
-
let bVal = b[columnId]
|
|
528
|
-
|
|
529
|
-
// Custom sort function
|
|
530
|
-
if (column?.sortFn) {
|
|
531
|
-
const result =
|
|
532
|
-
direction === "asc" ? column.sortFn(a, b) : column.sortFn(b, a)
|
|
533
|
-
if (result !== 0) return result
|
|
534
|
-
continue
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
// Default sorting
|
|
538
|
-
if (aVal === bVal) continue
|
|
539
|
-
if (aVal == null) return 1
|
|
540
|
-
if (bVal == null) return -1
|
|
541
|
-
|
|
542
|
-
const comparison = aVal < bVal ? -1 : 1
|
|
543
|
-
return direction === "asc" ? comparison : -comparison
|
|
544
|
-
}
|
|
545
|
-
return 0
|
|
546
|
-
})
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
/**
|
|
550
|
-
* Applies pagination to the data.
|
|
551
|
-
* @param {TableEntity} entity
|
|
552
|
-
* @param {any[]} rows
|
|
553
|
-
* @returns {any[]}
|
|
554
|
-
*/
|
|
555
|
-
function applyPagination(entity, rows) {
|
|
556
|
-
if (!entity.pagination) {
|
|
557
|
-
return rows
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
const { page, pageSize } = entity.pagination
|
|
561
|
-
const start = page * pageSize
|
|
562
|
-
return rows.slice(start, start + pageSize)
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
/**
|
|
566
|
-
* Capitalizes the first letter of a string.
|
|
567
|
-
* @param {string} str
|
|
568
|
-
* @returns {string}
|
|
569
|
-
*/
|
|
570
|
-
function capitalize(str) {
|
|
571
|
-
const [firstChar, ...rest] = str
|
|
572
|
-
return [firstChar.toUpperCase(), ...rest].join("")
|
|
573
|
-
}
|