@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/src/table.js CHANGED
@@ -1,372 +1,7 @@
1
- /* eslint-disable no-magic-numbers */
1
+ import { logic } from "./table/logic"
2
+ import { rendering } from "./table/rendering"
2
3
 
3
4
  export const table = {
4
- init(entity) {
5
- initTable(entity)
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
  }