@htlkg/components 0.0.1 → 0.0.2
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/dist/composables/index.js +206 -19
- package/dist/composables/index.js.map +1 -1
- package/package.json +40 -40
- package/src/composables/__test-useTable__.ts +34 -0
- package/src/composables/index.ts +1 -1
- package/src/composables/useTable.md +350 -0
- package/src/composables/useTable.ts +328 -27
- package/src/data/Table.demo.vue +26 -10
- package/src/data/Table.vue +13 -39
- package/src/forms/JsonSchemaForm.test.ts +98 -168
- package/src/forms/JsonSchemaForm.unit.test.ts +97 -45
- package/src/forms/JsonSchemaForm.vue +17 -1
- package/src/index.ts +3 -0
- package/src/navigation/AdminWrapper.vue +198 -0
- package/src/navigation/Tabs.test.ts +55 -27
- package/src/navigation/index.ts +1 -0
- package/src/overlays/Alert.test.ts +39 -74
- package/src/overlays/Drawer.test.ts +57 -23
- package/src/overlays/Modal.test.ts +54 -42
- package/src/stores/index.ts +7 -0
- package/src/stores/user.ts +33 -0
- package/src/test-setup.ts +235 -0
|
@@ -1,44 +1,169 @@
|
|
|
1
|
-
import { ref, computed, type Ref, type ComputedRef } from 'vue';
|
|
1
|
+
import { ref, computed, unref, type Ref, type ComputedRef, type MaybeRef } from 'vue';
|
|
2
|
+
|
|
3
|
+
export interface SmartFilter {
|
|
4
|
+
category: string;
|
|
5
|
+
operator: string;
|
|
6
|
+
value: any;
|
|
7
|
+
}
|
|
2
8
|
|
|
3
9
|
export interface UseTableOptions<T> {
|
|
4
|
-
items: T[]
|
|
10
|
+
items: MaybeRef<T[]>; // Accept both arrays and refs/computed
|
|
5
11
|
pageSize?: number;
|
|
6
12
|
sortKey?: string;
|
|
7
13
|
sortOrder?: 'asc' | 'desc';
|
|
14
|
+
idKey?: string; // Key to use for item identification (default: 'id')
|
|
8
15
|
}
|
|
9
16
|
|
|
10
17
|
export interface UseTableReturn<T> {
|
|
18
|
+
// Pagination
|
|
11
19
|
currentPage: Ref<number>;
|
|
12
20
|
pageSize: Ref<number>;
|
|
21
|
+
totalPages: ComputedRef<number>;
|
|
22
|
+
totalItems: ComputedRef<number>;
|
|
23
|
+
paginatedItems: ComputedRef<T[]>;
|
|
24
|
+
|
|
25
|
+
// Sorting
|
|
13
26
|
sortKey: Ref<string>;
|
|
14
27
|
sortOrder: Ref<'asc' | 'desc'>;
|
|
15
|
-
selectedItems: Ref<T[]>;
|
|
16
|
-
paginatedItems: ComputedRef<T[]>;
|
|
17
|
-
totalPages: ComputedRef<number>;
|
|
18
28
|
sortedItems: ComputedRef<T[]>;
|
|
29
|
+
|
|
30
|
+
// Selection
|
|
31
|
+
selectedItems: Ref<T[]>;
|
|
32
|
+
selectedItemIds: Ref<Set<string | number>>;
|
|
33
|
+
allSelected: ComputedRef<boolean>;
|
|
34
|
+
someSelected: ComputedRef<boolean>;
|
|
35
|
+
|
|
36
|
+
// Filtering
|
|
37
|
+
activeFilters: Ref<SmartFilter[]>;
|
|
38
|
+
filteredItems: ComputedRef<T[]>;
|
|
39
|
+
|
|
40
|
+
// Column visibility
|
|
41
|
+
hiddenColumns: Ref<number[]>;
|
|
42
|
+
|
|
43
|
+
// Reset state
|
|
44
|
+
resetSelected: Ref<boolean>;
|
|
45
|
+
|
|
46
|
+
// Methods - Pagination
|
|
19
47
|
setPage: (page: number) => void;
|
|
20
48
|
setPageSize: (size: number) => void;
|
|
49
|
+
|
|
50
|
+
// Methods - Sorting
|
|
21
51
|
setSorting: (key: string, order?: 'asc' | 'desc') => void;
|
|
52
|
+
handleOrderBy: (event: { value: string; orderDirection: 'asc' | 'desc' }) => void;
|
|
53
|
+
|
|
54
|
+
// Methods - Selection
|
|
22
55
|
selectItem: (item: T) => void;
|
|
23
56
|
deselectItem: (item: T) => void;
|
|
24
57
|
selectAll: () => void;
|
|
58
|
+
selectAllOnPage: () => void;
|
|
25
59
|
clearSelection: () => void;
|
|
26
60
|
isSelected: (item: T) => boolean;
|
|
61
|
+
|
|
62
|
+
// Methods - Filtering
|
|
63
|
+
applyFilters: (filters: SmartFilter[]) => void;
|
|
64
|
+
clearFilters: () => void;
|
|
65
|
+
removeFilter: (index: number) => void;
|
|
66
|
+
|
|
67
|
+
// Methods - Column visibility
|
|
68
|
+
toggleColumn: (index: number) => void;
|
|
69
|
+
showColumn: (index: number) => void;
|
|
70
|
+
hideColumn: (index: number) => void;
|
|
71
|
+
|
|
72
|
+
// Event handlers for uiTable
|
|
73
|
+
handlePageChange: (page: number) => void;
|
|
74
|
+
handlePageSizeChange: (size: number | string) => void;
|
|
75
|
+
handleSmartFiltersApplied: (filters: SmartFilter[]) => void;
|
|
76
|
+
handleSmartFiltersCleared: () => void;
|
|
77
|
+
handleSmartFilterDeleted: (index: number) => void;
|
|
78
|
+
handleColumnsVisibilityChanged: (event: { index: number; hidden: boolean }) => void;
|
|
79
|
+
handleModalAction: (event: { modal: string; action: string }) => void;
|
|
27
80
|
}
|
|
28
81
|
|
|
29
82
|
export function useTable<T extends Record<string, any>>(
|
|
30
83
|
options: UseTableOptions<T>
|
|
31
84
|
): UseTableReturn<T> {
|
|
85
|
+
const idKey = options.idKey ?? 'id';
|
|
86
|
+
|
|
87
|
+
// Create a reactive reference to items with safety check
|
|
88
|
+
const items = computed(() => {
|
|
89
|
+
try {
|
|
90
|
+
const unwrapped = unref(options.items);
|
|
91
|
+
// Ensure we always return an array
|
|
92
|
+
if (!unwrapped) return [];
|
|
93
|
+
if (!Array.isArray(unwrapped)) {
|
|
94
|
+
console.warn('useTable: items is not an array', unwrapped);
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
return unwrapped;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('useTable: Error unwrapping items', error);
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// State - Pagination
|
|
32
105
|
const currentPage = ref(1);
|
|
33
106
|
const pageSize = ref(options.pageSize ?? 10);
|
|
107
|
+
|
|
108
|
+
// State - Sorting
|
|
34
109
|
const sortKey = ref(options.sortKey ?? '');
|
|
35
110
|
const sortOrder = ref<'asc' | 'desc'>(options.sortOrder ?? 'asc');
|
|
36
|
-
|
|
111
|
+
|
|
112
|
+
// State - Selection
|
|
113
|
+
const selectedItems = ref<T[]>([]) as Ref<T[]>;
|
|
114
|
+
const selectedItemIds = ref<Set<string | number>>(new Set());
|
|
115
|
+
const resetSelected = ref(false);
|
|
116
|
+
|
|
117
|
+
// State - Filtering
|
|
118
|
+
const activeFilters = ref<SmartFilter[]>([]);
|
|
119
|
+
|
|
120
|
+
// State - Column visibility
|
|
121
|
+
const hiddenColumns = ref<number[]>([]);
|
|
122
|
+
|
|
123
|
+
// Computed - Filtering
|
|
124
|
+
const filteredItems = computed(() => {
|
|
125
|
+
if (activeFilters.value.length === 0) return items.value;
|
|
126
|
+
|
|
127
|
+
return items.value.filter(item => {
|
|
128
|
+
// All filters must pass (AND logic by default)
|
|
129
|
+
return activeFilters.value.every(filter => {
|
|
130
|
+
const { category, operator, value } = filter;
|
|
131
|
+
|
|
132
|
+
if (value === undefined || value === null || value === '') return true;
|
|
133
|
+
|
|
134
|
+
const itemValue = item[category];
|
|
135
|
+
|
|
136
|
+
// Handle different operators
|
|
137
|
+
switch (operator) {
|
|
138
|
+
case 'contains':
|
|
139
|
+
return String(itemValue).toLowerCase().includes(String(value).toLowerCase());
|
|
140
|
+
case 'is':
|
|
141
|
+
case '=':
|
|
142
|
+
return itemValue === value;
|
|
143
|
+
case '>':
|
|
144
|
+
case 'greater':
|
|
145
|
+
return Number(itemValue) > Number(value);
|
|
146
|
+
case '<':
|
|
147
|
+
case 'less':
|
|
148
|
+
return Number(itemValue) < Number(value);
|
|
149
|
+
case '>=':
|
|
150
|
+
case 'greaterOrEqual':
|
|
151
|
+
return Number(itemValue) >= Number(value);
|
|
152
|
+
case '<=':
|
|
153
|
+
case 'lessOrEqual':
|
|
154
|
+
return Number(itemValue) <= Number(value);
|
|
155
|
+
default:
|
|
156
|
+
return String(itemValue).toLowerCase().includes(String(value).toLowerCase());
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
|
37
161
|
|
|
162
|
+
// Computed - Sorting
|
|
38
163
|
const sortedItems = computed(() => {
|
|
39
|
-
if (!sortKey.value) return
|
|
164
|
+
if (!sortKey.value) return filteredItems.value;
|
|
40
165
|
|
|
41
|
-
return [...
|
|
166
|
+
return [...filteredItems.value].sort((a, b) => {
|
|
42
167
|
const aVal = a[sortKey.value];
|
|
43
168
|
const bVal = b[sortKey.value];
|
|
44
169
|
const order = sortOrder.value === 'asc' ? 1 : -1;
|
|
@@ -47,10 +172,16 @@ export function useTable<T extends Record<string, any>>(
|
|
|
47
172
|
if (aVal == null) return 1;
|
|
48
173
|
if (bVal == null) return -1;
|
|
49
174
|
|
|
175
|
+
// Handle different types
|
|
176
|
+
if (typeof aVal === 'string' && typeof bVal === 'string') {
|
|
177
|
+
return aVal.localeCompare(bVal) * order;
|
|
178
|
+
}
|
|
179
|
+
|
|
50
180
|
return aVal > bVal ? order : -order;
|
|
51
181
|
});
|
|
52
182
|
});
|
|
53
183
|
|
|
184
|
+
// Computed - Pagination
|
|
54
185
|
const paginatedItems = computed(() => {
|
|
55
186
|
const start = (currentPage.value - 1) * pageSize.value;
|
|
56
187
|
const end = start + pageSize.value;
|
|
@@ -58,9 +189,22 @@ export function useTable<T extends Record<string, any>>(
|
|
|
58
189
|
});
|
|
59
190
|
|
|
60
191
|
const totalPages = computed(() =>
|
|
61
|
-
Math.ceil(
|
|
192
|
+
Math.ceil(sortedItems.value.length / pageSize.value)
|
|
62
193
|
);
|
|
63
194
|
|
|
195
|
+
const totalItems = computed(() => sortedItems.value.length);
|
|
196
|
+
|
|
197
|
+
// Computed - Selection
|
|
198
|
+
const allSelected = computed(() => {
|
|
199
|
+
if (items.value.length === 0) return false;
|
|
200
|
+
return selectedItemIds.value.size === items.value.length;
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const someSelected = computed(() => {
|
|
204
|
+
return selectedItemIds.value.size > 0 && !allSelected.value;
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Methods - Pagination
|
|
64
208
|
function setPage(page: number) {
|
|
65
209
|
if (page >= 1 && page <= totalPages.value) {
|
|
66
210
|
currentPage.value = page;
|
|
@@ -72,6 +216,16 @@ export function useTable<T extends Record<string, any>>(
|
|
|
72
216
|
currentPage.value = 1; // Reset to first page
|
|
73
217
|
}
|
|
74
218
|
|
|
219
|
+
function handlePageChange(page: number) {
|
|
220
|
+
setPage(page);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function handlePageSizeChange(size: number | string) {
|
|
224
|
+
const numSize = typeof size === 'string' ? parseInt(size) : size;
|
|
225
|
+
setPageSize(numSize);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Methods - Sorting
|
|
75
229
|
function setSorting(key: string, order: 'asc' | 'desc' = 'asc') {
|
|
76
230
|
if (sortKey.value === key) {
|
|
77
231
|
// Toggle order if same key
|
|
@@ -82,53 +236,200 @@ export function useTable<T extends Record<string, any>>(
|
|
|
82
236
|
}
|
|
83
237
|
}
|
|
84
238
|
|
|
239
|
+
function handleOrderBy(event: { value: string; orderDirection: 'asc' | 'desc' }) {
|
|
240
|
+
sortKey.value = event.value;
|
|
241
|
+
sortOrder.value = event.orderDirection;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Methods - Selection
|
|
245
|
+
function getItemId(item: T): string | number {
|
|
246
|
+
return item[idKey];
|
|
247
|
+
}
|
|
248
|
+
|
|
85
249
|
function selectItem(item: T) {
|
|
86
|
-
|
|
87
|
-
|
|
250
|
+
const id = getItemId(item);
|
|
251
|
+
if (!selectedItemIds.value.has(id)) {
|
|
252
|
+
selectedItemIds.value.add(id);
|
|
253
|
+
(selectedItems.value as T[]).push(item);
|
|
88
254
|
}
|
|
89
255
|
}
|
|
90
256
|
|
|
91
257
|
function deselectItem(item: T) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
258
|
+
const id = getItemId(item);
|
|
259
|
+
if (selectedItemIds.value.has(id)) {
|
|
260
|
+
selectedItemIds.value.delete(id);
|
|
261
|
+
const index = (selectedItems.value as T[]).findIndex(i => getItemId(i) === id);
|
|
262
|
+
if (index !== -1) {
|
|
263
|
+
(selectedItems.value as T[]).splice(index, 1);
|
|
264
|
+
}
|
|
98
265
|
}
|
|
99
266
|
}
|
|
100
267
|
|
|
101
268
|
function selectAll() {
|
|
102
|
-
|
|
269
|
+
selectedItemIds.value.clear();
|
|
270
|
+
(selectedItems.value as T[]).length = 0;
|
|
271
|
+
|
|
272
|
+
items.value.forEach(item => {
|
|
273
|
+
const id = getItemId(item);
|
|
274
|
+
selectedItemIds.value.add(id);
|
|
275
|
+
(selectedItems.value as T[]).push(item);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function selectAllOnPage() {
|
|
280
|
+
paginatedItems.value.forEach(item => {
|
|
281
|
+
selectItem(item);
|
|
282
|
+
});
|
|
103
283
|
}
|
|
104
284
|
|
|
105
285
|
function clearSelection() {
|
|
106
|
-
|
|
286
|
+
selectedItemIds.value.clear();
|
|
287
|
+
(selectedItems.value as T[]).length = 0;
|
|
288
|
+
resetSelected.value = true;
|
|
289
|
+
setTimeout(() => {
|
|
290
|
+
resetSelected.value = false;
|
|
291
|
+
}, 100);
|
|
107
292
|
}
|
|
108
293
|
|
|
109
294
|
function isSelected(item: T): boolean {
|
|
110
|
-
|
|
111
|
-
return
|
|
112
|
-
|
|
113
|
-
|
|
295
|
+
const id = getItemId(item);
|
|
296
|
+
return selectedItemIds.value.has(id);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Methods - Filtering
|
|
300
|
+
function applyFilters(filters: SmartFilter[]) {
|
|
301
|
+
activeFilters.value = filters;
|
|
302
|
+
currentPage.value = 1; // Reset to first page when filters change
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function clearFilters() {
|
|
306
|
+
activeFilters.value = [];
|
|
307
|
+
currentPage.value = 1;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function removeFilter(index: number) {
|
|
311
|
+
if (index >= 0 && index < activeFilters.value.length) {
|
|
312
|
+
activeFilters.value.splice(index, 1);
|
|
313
|
+
currentPage.value = 1;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function handleSmartFiltersApplied(filters: SmartFilter[]) {
|
|
318
|
+
applyFilters(filters);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function handleSmartFiltersCleared() {
|
|
322
|
+
clearFilters();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function handleSmartFilterDeleted(index: number) {
|
|
326
|
+
removeFilter(index);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Methods - Column visibility
|
|
330
|
+
function toggleColumn(index: number) {
|
|
331
|
+
const hiddenIndex = hiddenColumns.value.indexOf(index);
|
|
332
|
+
if (hiddenIndex > -1) {
|
|
333
|
+
hiddenColumns.value.splice(hiddenIndex, 1);
|
|
334
|
+
} else {
|
|
335
|
+
hiddenColumns.value.push(index);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function showColumn(index: number) {
|
|
340
|
+
const hiddenIndex = hiddenColumns.value.indexOf(index);
|
|
341
|
+
if (hiddenIndex > -1) {
|
|
342
|
+
hiddenColumns.value.splice(hiddenIndex, 1);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function hideColumn(index: number) {
|
|
347
|
+
if (!hiddenColumns.value.includes(index)) {
|
|
348
|
+
hiddenColumns.value.push(index);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function handleColumnsVisibilityChanged(event: { index: number; hidden: boolean }) {
|
|
353
|
+
if (event.hidden) {
|
|
354
|
+
hideColumn(event.index);
|
|
355
|
+
} else {
|
|
356
|
+
showColumn(event.index);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Methods - Modal actions
|
|
361
|
+
function handleModalAction(event: { modal: string; action: string }) {
|
|
362
|
+
if (event.modal === 'selectAllItemsModal' || event.modal.includes('selectAll')) {
|
|
363
|
+
if (event.action === 'selectAll') {
|
|
364
|
+
selectAll();
|
|
365
|
+
} else if (event.action === 'close') {
|
|
366
|
+
selectAllOnPage();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
114
369
|
}
|
|
115
370
|
|
|
116
371
|
return {
|
|
372
|
+
// Pagination
|
|
117
373
|
currentPage,
|
|
118
374
|
pageSize,
|
|
375
|
+
totalPages,
|
|
376
|
+
totalItems,
|
|
377
|
+
paginatedItems,
|
|
378
|
+
|
|
379
|
+
// Sorting
|
|
119
380
|
sortKey,
|
|
120
381
|
sortOrder,
|
|
121
|
-
selectedItems,
|
|
122
|
-
paginatedItems,
|
|
123
|
-
totalPages,
|
|
124
382
|
sortedItems,
|
|
383
|
+
|
|
384
|
+
// Selection
|
|
385
|
+
selectedItems,
|
|
386
|
+
selectedItemIds,
|
|
387
|
+
allSelected,
|
|
388
|
+
someSelected,
|
|
389
|
+
|
|
390
|
+
// Filtering
|
|
391
|
+
activeFilters,
|
|
392
|
+
filteredItems,
|
|
393
|
+
|
|
394
|
+
// Column visibility
|
|
395
|
+
hiddenColumns,
|
|
396
|
+
|
|
397
|
+
// Reset state
|
|
398
|
+
resetSelected,
|
|
399
|
+
|
|
400
|
+
// Methods - Pagination
|
|
125
401
|
setPage,
|
|
126
402
|
setPageSize,
|
|
403
|
+
handlePageChange,
|
|
404
|
+
handlePageSizeChange,
|
|
405
|
+
|
|
406
|
+
// Methods - Sorting
|
|
127
407
|
setSorting,
|
|
408
|
+
handleOrderBy,
|
|
409
|
+
|
|
410
|
+
// Methods - Selection
|
|
128
411
|
selectItem,
|
|
129
412
|
deselectItem,
|
|
130
413
|
selectAll,
|
|
414
|
+
selectAllOnPage,
|
|
131
415
|
clearSelection,
|
|
132
|
-
isSelected
|
|
416
|
+
isSelected,
|
|
417
|
+
|
|
418
|
+
// Methods - Filtering
|
|
419
|
+
applyFilters,
|
|
420
|
+
clearFilters,
|
|
421
|
+
removeFilter,
|
|
422
|
+
handleSmartFiltersApplied,
|
|
423
|
+
handleSmartFiltersCleared,
|
|
424
|
+
handleSmartFilterDeleted,
|
|
425
|
+
|
|
426
|
+
// Methods - Column visibility
|
|
427
|
+
toggleColumn,
|
|
428
|
+
showColumn,
|
|
429
|
+
hideColumn,
|
|
430
|
+
handleColumnsVisibilityChanged,
|
|
431
|
+
|
|
432
|
+
// Methods - Modal actions
|
|
433
|
+
handleModalAction,
|
|
133
434
|
};
|
|
134
435
|
}
|
package/src/data/Table.demo.vue
CHANGED
|
@@ -21,13 +21,13 @@ const allItems = ref([
|
|
|
21
21
|
{ id: 15, name: 'Maria Garcia', email: 'maria@example.com', role: 'User', status: 'Inactive', department: 'Marketing' },
|
|
22
22
|
]);
|
|
23
23
|
|
|
24
|
-
// Table
|
|
25
|
-
const
|
|
26
|
-
{ name: 'Name', value: 'name' },
|
|
27
|
-
{ name: 'Email', value: 'email' },
|
|
28
|
-
{ name: 'Role', value: 'role' },
|
|
29
|
-
{ name: 'Status', value: 'status' },
|
|
30
|
-
{ name: 'Department', value: 'department' }
|
|
24
|
+
// Table header
|
|
25
|
+
const header = [
|
|
26
|
+
{ name: 'Name', value: 'name', tooltip: 'User full name' },
|
|
27
|
+
{ name: 'Email', value: 'email', tooltip: 'User email address' },
|
|
28
|
+
{ name: 'Role', value: 'role', tooltip: 'User role in the system' },
|
|
29
|
+
{ name: 'Status', value: 'status', tooltip: 'Account status' },
|
|
30
|
+
{ name: 'Department', value: 'department', tooltip: 'User department' }
|
|
31
31
|
];
|
|
32
32
|
|
|
33
33
|
// State
|
|
@@ -164,11 +164,27 @@ const filteredItems = computed(() => {
|
|
|
164
164
|
return items;
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
-
// Computed: Paginated items
|
|
167
|
+
// Computed: Paginated items formatted for uiTable
|
|
168
168
|
const paginatedItems = computed(() => {
|
|
169
169
|
const start = (currentPage.value - 1) * itemsPerPage.value;
|
|
170
170
|
const end = start + itemsPerPage.value;
|
|
171
|
-
|
|
171
|
+
const items = filteredItems.value.slice(start, end);
|
|
172
|
+
|
|
173
|
+
// Format items for uiTable
|
|
174
|
+
return items.map(item => ({
|
|
175
|
+
id: item.id,
|
|
176
|
+
row: [
|
|
177
|
+
item.name,
|
|
178
|
+
item.email,
|
|
179
|
+
item.role,
|
|
180
|
+
{
|
|
181
|
+
content: item.status,
|
|
182
|
+
color: item.status === 'Active' ? 'green' as const : 'gray' as const,
|
|
183
|
+
type: 'tag' as const
|
|
184
|
+
},
|
|
185
|
+
item.department
|
|
186
|
+
]
|
|
187
|
+
}));
|
|
172
188
|
});
|
|
173
189
|
|
|
174
190
|
// Computed: Total pages
|
|
@@ -261,7 +277,7 @@ const handleItemsPerPageChanged = (size: number) => {
|
|
|
261
277
|
<!-- Table Component -->
|
|
262
278
|
<Table
|
|
263
279
|
:items="paginatedItems"
|
|
264
|
-
:
|
|
280
|
+
:header="header"
|
|
265
281
|
:loading="loading"
|
|
266
282
|
:actions="actions"
|
|
267
283
|
:select-all-items-modal="selectAllItemsModal"
|
package/src/data/Table.vue
CHANGED
|
@@ -8,16 +8,10 @@ import {
|
|
|
8
8
|
uiTable
|
|
9
9
|
} from '@hotelinking/ui';
|
|
10
10
|
|
|
11
|
-
export interface Column {
|
|
12
|
-
name: string;
|
|
13
|
-
value: string;
|
|
14
|
-
tooltip?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
11
|
interface Props {
|
|
18
12
|
// Data
|
|
19
|
-
items:
|
|
20
|
-
|
|
13
|
+
items: TableItemType[];
|
|
14
|
+
header: UiTableInterface['header'];
|
|
21
15
|
|
|
22
16
|
// State
|
|
23
17
|
loading?: boolean;
|
|
@@ -129,32 +123,6 @@ const emit = defineEmits<{
|
|
|
129
123
|
const internalHiddenColumns = ref<number[]>(props.hiddenColumns || []);
|
|
130
124
|
const selectedItemIds = ref<Set<string | number>>(new Set());
|
|
131
125
|
|
|
132
|
-
// Convert columns to uiTable header format
|
|
133
|
-
const tableHeader = computed<UiTableInterface['header']>(() =>
|
|
134
|
-
props.columns.map(col => ({
|
|
135
|
-
name: col.name,
|
|
136
|
-
value: col.value,
|
|
137
|
-
tooltip: col.tooltip
|
|
138
|
-
}))
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
// Convert items to uiTable format
|
|
142
|
-
const tableItems = computed<TableItemType[]>(() =>
|
|
143
|
-
props.items.map((item, index) => ({
|
|
144
|
-
id: item.id || index,
|
|
145
|
-
disabled: item.disabled || false,
|
|
146
|
-
row: props.columns.map(col => {
|
|
147
|
-
const key = col.value || col.name.toLowerCase();
|
|
148
|
-
const value = item[key];
|
|
149
|
-
// Return the value as-is if it's already formatted for uiTable
|
|
150
|
-
if (typeof value === 'object' && value !== null && 'type' in value) {
|
|
151
|
-
return value;
|
|
152
|
-
}
|
|
153
|
-
return String(value ?? '');
|
|
154
|
-
})
|
|
155
|
-
}))
|
|
156
|
-
);
|
|
157
|
-
|
|
158
126
|
// Page size options for uiTable
|
|
159
127
|
const pageSizeOptions = computed(() => [
|
|
160
128
|
{ name: '10', value: '10', active: props.pageSize === 10 },
|
|
@@ -186,7 +154,12 @@ function handleTableAction(data: { action: string; items: Array<string | number>
|
|
|
186
154
|
selectedItemIds.value = new Set(data.items);
|
|
187
155
|
updateSelectedItems();
|
|
188
156
|
emit('table-action', data);
|
|
189
|
-
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function resetSelectedItems() {
|
|
160
|
+
selectedItemIds.value.clear();
|
|
161
|
+
updateSelectedItems();
|
|
162
|
+
emit('update:resetSelected', true);
|
|
190
163
|
}
|
|
191
164
|
|
|
192
165
|
function handleTableActionSelected(item: any) {
|
|
@@ -256,7 +229,7 @@ function handleSelectedItemsDeleted() {
|
|
|
256
229
|
}
|
|
257
230
|
|
|
258
231
|
function updateSelectedItems() {
|
|
259
|
-
const selected = props.items.filter(item => selectedItemIds.value.has(item.id));
|
|
232
|
+
const selected = props.items.filter(item => item.id && selectedItemIds.value.has(item.id));
|
|
260
233
|
emit('update:selected', selected);
|
|
261
234
|
}
|
|
262
235
|
|
|
@@ -268,15 +241,16 @@ defineExpose({
|
|
|
268
241
|
updateSelectedItems();
|
|
269
242
|
},
|
|
270
243
|
getHiddenColumns: () => internalHiddenColumns.value,
|
|
271
|
-
getSelectedItems: () => Array.from(selectedItemIds.value)
|
|
244
|
+
getSelectedItems: () => Array.from(selectedItemIds.value),
|
|
245
|
+
getSelectedItemsData: () => props.items.filter(item => item.id && selectedItemIds.value.has(item.id))
|
|
272
246
|
});
|
|
273
247
|
</script>
|
|
274
248
|
|
|
275
249
|
<template>
|
|
276
250
|
<uiTable
|
|
277
251
|
:loading="loading"
|
|
278
|
-
:header="
|
|
279
|
-
:items="
|
|
252
|
+
:header="header"
|
|
253
|
+
:items="items"
|
|
280
254
|
:ordered-by="orderedBy"
|
|
281
255
|
:order-direction="orderDirection"
|
|
282
256
|
:actions="(actions as any)"
|