@inglorious/web 2.0.1 → 2.1.0
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 +71 -11
- package/package.json +4 -2
- package/src/index.js +3 -1
- package/src/list.js +6 -2
- package/src/table/base.css +34 -0
- package/src/table/filters/date.js +42 -0
- package/src/table/filters/number.js +22 -0
- package/src/table/filters/range.js +42 -0
- package/src/table/filters/select.js +34 -0
- package/src/table/filters/text.js +22 -0
- package/src/table/filters.js +24 -0
- package/src/table/logic.js +401 -0
- package/src/table/rendering.js +239 -0
- package/src/table/theme.css +83 -0
- package/src/table.js +4 -369
package/src/table.js
CHANGED
|
@@ -1,372 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { logic } from "./table/logic"
|
|
2
|
+
import { rendering } from "./table/rendering"
|
|
2
3
|
|
|
3
4
|
export const table = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
},
|
|
7
|
-
|
|
8
|
-
create(entity) {
|
|
9
|
-
initTable(entity)
|
|
10
|
-
},
|
|
11
|
-
|
|
12
|
-
sortChange(entity, columnId) {
|
|
13
|
-
const column = entity.columns.find((c) => c.id === columnId)
|
|
14
|
-
if (!column?.isSortable) return
|
|
15
|
-
|
|
16
|
-
const existingIndex = entity.sorts.findIndex((s) => s.column === columnId)
|
|
17
|
-
|
|
18
|
-
if (existingIndex !== -1) {
|
|
19
|
-
// Toggle direction
|
|
20
|
-
const existing = entity.sorts[existingIndex]
|
|
21
|
-
if (existing.direction === "asc") {
|
|
22
|
-
existing.direction = "desc"
|
|
23
|
-
} else {
|
|
24
|
-
// Remove from sort if going from desc back to nothing
|
|
25
|
-
entity.sorts.splice(existingIndex, 1)
|
|
26
|
-
}
|
|
27
|
-
} else {
|
|
28
|
-
// Add new sort
|
|
29
|
-
entity.sorts.push({ column: columnId, direction: "asc" })
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (entity.pagination) {
|
|
33
|
-
entity.pagination.page = 0
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
sortsClear(entity) {
|
|
38
|
-
entity.sorts = []
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
filter(entity, { columnId, value }) {
|
|
42
|
-
if (value === null || value === undefined || value === "") {
|
|
43
|
-
delete entity.filters[columnId]
|
|
44
|
-
} else {
|
|
45
|
-
entity.filters[columnId] = value
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Reset to first page when filtering
|
|
49
|
-
if (entity.pagination) {
|
|
50
|
-
entity.pagination.page = 0
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
filtersClear(entity) {
|
|
55
|
-
entity.filters = {}
|
|
56
|
-
if (entity.pagination) {
|
|
57
|
-
entity.pagination.page = 0
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
pageChange(entity, page) {
|
|
62
|
-
if (!entity.pagination) return
|
|
63
|
-
|
|
64
|
-
const totalPages = Math.ceil(
|
|
65
|
-
getTotalRows(entity) / entity.pagination.pageSize,
|
|
66
|
-
)
|
|
67
|
-
entity.pagination.page = Math.max(0, Math.min(page, totalPages - 1))
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
pageNext(entity) {
|
|
71
|
-
if (!entity.pagination) return
|
|
72
|
-
const totalPages = Math.ceil(
|
|
73
|
-
getTotalRows(entity) / entity.pagination.pageSize,
|
|
74
|
-
)
|
|
75
|
-
entity.pagination.page = Math.min(
|
|
76
|
-
entity.pagination.page + 1,
|
|
77
|
-
totalPages - 1,
|
|
78
|
-
)
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
pagePrev(entity) {
|
|
82
|
-
if (!entity.pagination) return
|
|
83
|
-
entity.pagination.page = Math.max(entity.pagination.page - 1, 0)
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
pageSizeChange(entity, pageSize) {
|
|
87
|
-
if (!entity.pagination) return
|
|
88
|
-
|
|
89
|
-
entity.pagination.pageSize = pageSize
|
|
90
|
-
entity.pagination.page = 0
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
rowSelect(entity, rowId) {
|
|
94
|
-
if (!entity.selection.includes(rowId)) {
|
|
95
|
-
entity.selection.push(rowId)
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
rowDeselect(entity, rowId) {
|
|
100
|
-
const index = entity.selection.indexOf(rowId)
|
|
101
|
-
if (index !== -1) {
|
|
102
|
-
entity.selection.splice(index, 1)
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
rowToggle(entity, rowId) {
|
|
107
|
-
const index = entity.selection.indexOf(rowId)
|
|
108
|
-
if (index === -1) {
|
|
109
|
-
entity.selection.push(rowId)
|
|
110
|
-
} else {
|
|
111
|
-
entity.selection.splice(index, 1)
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
rowsToggleAll(entity) {
|
|
116
|
-
const rows = getRows(entity)
|
|
117
|
-
const allSelected = rows.every((row) => entity.selection.includes(row.id))
|
|
118
|
-
|
|
119
|
-
if (allSelected) {
|
|
120
|
-
// Deselect all visible
|
|
121
|
-
rows.forEach((row) => {
|
|
122
|
-
const index = entity.selection.indexOf(row.id)
|
|
123
|
-
if (index !== -1) entity.selection.splice(index, 1)
|
|
124
|
-
})
|
|
125
|
-
} else {
|
|
126
|
-
// Select all visible
|
|
127
|
-
rows.forEach((row) => {
|
|
128
|
-
if (!entity.selection.includes(row.id)) {
|
|
129
|
-
entity.selection.push(row.id)
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
rowsSelectAll(entity) {
|
|
136
|
-
const rows = getRows(entity)
|
|
137
|
-
rows.forEach((row) => {
|
|
138
|
-
if (!entity.selection.includes(row.id)) {
|
|
139
|
-
entity.selection.push(row.id)
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
},
|
|
143
|
-
|
|
144
|
-
selectionClear(entity) {
|
|
145
|
-
entity.selection.length = 0
|
|
146
|
-
},
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Helper functions outside the type (like form helpers)
|
|
150
|
-
|
|
151
|
-
export function getRows(entity) {
|
|
152
|
-
let rows = entity.data || []
|
|
153
|
-
|
|
154
|
-
// Filtering
|
|
155
|
-
if (Object.keys(entity.filters).length > 0) {
|
|
156
|
-
rows = rows.filter((row) => {
|
|
157
|
-
return Object.entries(entity.filters).every(([columnId, filterValue]) => {
|
|
158
|
-
const column = entity.columns.find((c) => c.id === columnId)
|
|
159
|
-
if (!column) return true
|
|
160
|
-
|
|
161
|
-
// Custom filter function
|
|
162
|
-
if (column.filterFn) {
|
|
163
|
-
return column.filterFn(row, filterValue)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Default filters by type
|
|
167
|
-
const value = row[columnId]
|
|
168
|
-
|
|
169
|
-
if (column.type === "number") {
|
|
170
|
-
if (typeof filterValue === "object") {
|
|
171
|
-
const { min, max } = filterValue
|
|
172
|
-
if (min !== undefined && value < min) return false
|
|
173
|
-
if (max !== undefined && value > max) return false
|
|
174
|
-
return true
|
|
175
|
-
}
|
|
176
|
-
return value === filterValue
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (column.type === "boolean") {
|
|
180
|
-
return value === filterValue
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// String filtering (case-insensitive contains)
|
|
184
|
-
return String(value)
|
|
185
|
-
.toLowerCase()
|
|
186
|
-
.includes(String(filterValue).toLowerCase())
|
|
187
|
-
})
|
|
188
|
-
})
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Sorting
|
|
192
|
-
if (entity.sorts.length) {
|
|
193
|
-
rows = [...rows].sort((a, b) => {
|
|
194
|
-
for (const { column: columnId, direction } of entity.sorts) {
|
|
195
|
-
const column = entity.columns.find((c) => c.id === columnId)
|
|
196
|
-
let aVal = a[columnId]
|
|
197
|
-
let bVal = b[columnId]
|
|
198
|
-
|
|
199
|
-
// Custom sort function
|
|
200
|
-
if (column?.sortFn) {
|
|
201
|
-
const result =
|
|
202
|
-
direction === "asc" ? column.sortFn(a, b) : column.sortFn(b, a)
|
|
203
|
-
if (result !== 0) return result
|
|
204
|
-
continue
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Default sorting
|
|
208
|
-
if (aVal === bVal) continue
|
|
209
|
-
if (aVal == null) return 1
|
|
210
|
-
if (bVal == null) return -1
|
|
211
|
-
|
|
212
|
-
const comparison = aVal < bVal ? -1 : 1
|
|
213
|
-
return direction === "asc" ? comparison : -comparison
|
|
214
|
-
}
|
|
215
|
-
return 0
|
|
216
|
-
})
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Pagination
|
|
220
|
-
if (entity.pagination) {
|
|
221
|
-
const { page, pageSize } = entity.pagination
|
|
222
|
-
const start = page * pageSize
|
|
223
|
-
rows = rows.slice(start, start + pageSize)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return rows
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export function getTotalRows(entity) {
|
|
230
|
-
if (!entity.data) return 0
|
|
231
|
-
|
|
232
|
-
// If no filters, return total data length
|
|
233
|
-
if (Object.keys(entity.filters).length === 0) {
|
|
234
|
-
return entity.data.length
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Count filtered rows
|
|
238
|
-
return entity.data.filter((row) => {
|
|
239
|
-
return Object.entries(entity.filters).every(([columnId, filterValue]) => {
|
|
240
|
-
const column = entity.columns.find((c) => c.id === columnId)
|
|
241
|
-
if (!column) return true
|
|
242
|
-
|
|
243
|
-
// Custom filter function
|
|
244
|
-
if (column.filterFn) {
|
|
245
|
-
return column.filterFn(row, filterValue)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Default filters by type
|
|
249
|
-
const value = row[columnId]
|
|
250
|
-
|
|
251
|
-
if (column.type === "number") {
|
|
252
|
-
if (typeof filterValue === "object") {
|
|
253
|
-
const { min, max } = filterValue
|
|
254
|
-
if (min !== undefined && value < min) return false
|
|
255
|
-
if (max !== undefined && value > max) return false
|
|
256
|
-
return true
|
|
257
|
-
}
|
|
258
|
-
return value === filterValue
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (column.type === "boolean") {
|
|
262
|
-
return value === filterValue
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// String filtering (case-insensitive contains)
|
|
266
|
-
return String(value)
|
|
267
|
-
.toLowerCase()
|
|
268
|
-
.includes(String(filterValue).toLowerCase())
|
|
269
|
-
})
|
|
270
|
-
}).length
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
export function getPaginationInfo(entity) {
|
|
274
|
-
if (!entity.pagination) return null
|
|
275
|
-
|
|
276
|
-
const totalRows = getTotalRows(entity)
|
|
277
|
-
const { page, pageSize } = entity.pagination
|
|
278
|
-
const totalPages = Math.ceil(totalRows / pageSize)
|
|
279
|
-
const start = page * pageSize
|
|
280
|
-
const end = Math.min((page + 1) * pageSize, totalRows)
|
|
281
|
-
|
|
282
|
-
return {
|
|
283
|
-
page,
|
|
284
|
-
pageSize,
|
|
285
|
-
totalPages,
|
|
286
|
-
totalRows,
|
|
287
|
-
start,
|
|
288
|
-
end,
|
|
289
|
-
hasNextPage: page < totalPages - 1,
|
|
290
|
-
hasPrevPage: page > 0,
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export function getSortDirection(entity, columnId) {
|
|
295
|
-
const sort = entity.sorts.find((s) => s.column === columnId)
|
|
296
|
-
return sort?.direction || null
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export function getSortIndex(entity, columnId) {
|
|
300
|
-
return entity.sorts.findIndex((s) => s.column === columnId)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
export function getFilter(entity, columnId) {
|
|
304
|
-
return entity.filters[columnId]
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
export function isRowSelected(entity, rowId) {
|
|
308
|
-
return entity.selection.includes(rowId)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
export function isAllSelected(entity) {
|
|
312
|
-
const rows = getRows(entity)
|
|
313
|
-
return rows.length && rows.every((row) => entity.selection.includes(row.id))
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export function isSomeSelected(entity) {
|
|
317
|
-
const rows = getRows(entity)
|
|
318
|
-
const selectedCount = rows.filter((row) =>
|
|
319
|
-
entity.selection.includes(row.id),
|
|
320
|
-
).length
|
|
321
|
-
return selectedCount && selectedCount < rows.length
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function initTable(entity) {
|
|
325
|
-
// Auto-generate columns from first data item if not provided
|
|
326
|
-
if (!entity.columns && entity.data?.length) {
|
|
327
|
-
const [firstRow] = entity.data
|
|
328
|
-
|
|
329
|
-
entity.columns = Object.keys(firstRow).map((key) => {
|
|
330
|
-
const value = firstRow[key]
|
|
331
|
-
const type = getDefaultColumnType(value)
|
|
332
|
-
const [firstChar, ...rest] = key
|
|
333
|
-
|
|
334
|
-
return {
|
|
335
|
-
id: key,
|
|
336
|
-
title: [firstChar.toUpperCase(), ...rest].join(""),
|
|
337
|
-
isSortable: true,
|
|
338
|
-
isFilterable: type !== "boolean",
|
|
339
|
-
type,
|
|
340
|
-
width: getDefaultColumnWidth(type),
|
|
341
|
-
}
|
|
342
|
-
})
|
|
343
|
-
} else {
|
|
344
|
-
entity.columns ??= []
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// State
|
|
348
|
-
entity.sorts ??= []
|
|
349
|
-
entity.filters ??= {}
|
|
350
|
-
entity.selection ??= []
|
|
351
|
-
|
|
352
|
-
// Pagination (null = disabled)
|
|
353
|
-
entity.pagination ??= null
|
|
354
|
-
|
|
355
|
-
if (entity.pagination) {
|
|
356
|
-
entity.pagination.page ??= 0
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function getDefaultColumnType(value) {
|
|
361
|
-
if (typeof value === "number") return "number"
|
|
362
|
-
if (typeof value === "boolean") return "boolean"
|
|
363
|
-
if (value instanceof Date) return "date"
|
|
364
|
-
return "string"
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
function getDefaultColumnWidth(type) {
|
|
368
|
-
if (type === "number") return 100
|
|
369
|
-
if (type === "boolean") return 80
|
|
370
|
-
if (type === "date") return 120
|
|
371
|
-
return 200
|
|
5
|
+
...logic,
|
|
6
|
+
...rendering,
|
|
372
7
|
}
|