@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
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# useTable Composable
|
|
2
|
+
|
|
3
|
+
Enhanced table state management composable that integrates seamlessly with the hotelinking/ui `uiTable` component.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Pagination**: Full pagination support with page size control
|
|
8
|
+
- ✅ **Sorting**: Multi-column sorting with asc/desc order
|
|
9
|
+
- ✅ **Selection**: Row selection with select all/page functionality
|
|
10
|
+
- ✅ **Filtering**: Smart filters with multiple operators (contains, is, >, <, etc.)
|
|
11
|
+
- ✅ **Column Visibility**: Show/hide columns dynamically
|
|
12
|
+
- ✅ **Event Handlers**: Pre-built handlers for all uiTable events
|
|
13
|
+
|
|
14
|
+
## Basic Usage
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { useTable } from '@htlkg/components/composables';
|
|
18
|
+
|
|
19
|
+
const table = useTable({
|
|
20
|
+
items: myData,
|
|
21
|
+
pageSize: 10,
|
|
22
|
+
sortKey: 'name',
|
|
23
|
+
sortOrder: 'asc',
|
|
24
|
+
idKey: 'id', // optional, defaults to 'id'
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Integration with uiTable
|
|
29
|
+
|
|
30
|
+
```vue
|
|
31
|
+
<template>
|
|
32
|
+
<Table
|
|
33
|
+
:header="tableColumns"
|
|
34
|
+
:items="table.paginatedItems.value"
|
|
35
|
+
:loading="loading"
|
|
36
|
+
:current-page="table.currentPage.value"
|
|
37
|
+
:total-pages="table.totalPages.value"
|
|
38
|
+
:total-items="table.totalItems.value"
|
|
39
|
+
:page-size="table.pageSize.value"
|
|
40
|
+
:ordered-by="table.sortKey.value"
|
|
41
|
+
:order-direction="table.sortOrder.value"
|
|
42
|
+
:hidden-columns="table.hiddenColumns.value"
|
|
43
|
+
:reset-selected="table.resetSelected.value"
|
|
44
|
+
:smart-filter-categories="filterCategories"
|
|
45
|
+
:select-all-items-modal="selectAllModal"
|
|
46
|
+
@change-page="table.handlePageChange"
|
|
47
|
+
@change-page-size="table.handlePageSizeChange"
|
|
48
|
+
@order-by="table.handleOrderBy"
|
|
49
|
+
@smart-filters-sent="table.handleSmartFiltersApplied"
|
|
50
|
+
@smart-filters-cleared="table.handleSmartFiltersCleared"
|
|
51
|
+
@smart-filter-deleted="table.handleSmartFilterDeleted"
|
|
52
|
+
@columns-visibility-changed="table.handleColumnsVisibilityChanged"
|
|
53
|
+
@modal-action="table.handleModalAction"
|
|
54
|
+
/>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<script setup lang="ts">
|
|
58
|
+
import { useTable } from '@htlkg/components/composables';
|
|
59
|
+
import { Table } from '@htlkg/components/data';
|
|
60
|
+
|
|
61
|
+
const table = useTable({
|
|
62
|
+
items: myData,
|
|
63
|
+
pageSize: 10,
|
|
64
|
+
sortKey: 'name',
|
|
65
|
+
sortOrder: 'asc',
|
|
66
|
+
});
|
|
67
|
+
</script>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## API Reference
|
|
71
|
+
|
|
72
|
+
### Options
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
interface UseTableOptions<T> {
|
|
76
|
+
items: T[]; // Array of items to display
|
|
77
|
+
pageSize?: number; // Items per page (default: 10)
|
|
78
|
+
sortKey?: string; // Initial sort column
|
|
79
|
+
sortOrder?: 'asc' | 'desc'; // Initial sort order
|
|
80
|
+
idKey?: string; // Property to use as unique ID (default: 'id')
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Return Value
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
interface UseTableReturn<T> {
|
|
88
|
+
// State - Pagination
|
|
89
|
+
currentPage: Ref<number>;
|
|
90
|
+
pageSize: Ref<number>;
|
|
91
|
+
totalPages: ComputedRef<number>;
|
|
92
|
+
totalItems: ComputedRef<number>;
|
|
93
|
+
paginatedItems: ComputedRef<T[]>;
|
|
94
|
+
|
|
95
|
+
// State - Sorting
|
|
96
|
+
sortKey: Ref<string>;
|
|
97
|
+
sortOrder: Ref<'asc' | 'desc'>;
|
|
98
|
+
sortedItems: ComputedRef<T[]>;
|
|
99
|
+
|
|
100
|
+
// State - Selection
|
|
101
|
+
selectedItems: Ref<T[]>;
|
|
102
|
+
selectedItemIds: Ref<Set<string | number>>;
|
|
103
|
+
allSelected: ComputedRef<boolean>;
|
|
104
|
+
someSelected: ComputedRef<boolean>;
|
|
105
|
+
|
|
106
|
+
// State - Filtering
|
|
107
|
+
activeFilters: Ref<SmartFilter[]>;
|
|
108
|
+
filteredItems: ComputedRef<T[]>;
|
|
109
|
+
|
|
110
|
+
// State - Column visibility
|
|
111
|
+
hiddenColumns: Ref<number[]>;
|
|
112
|
+
|
|
113
|
+
// State - Reset
|
|
114
|
+
resetSelected: Ref<boolean>;
|
|
115
|
+
|
|
116
|
+
// Methods - Pagination
|
|
117
|
+
setPage: (page: number) => void;
|
|
118
|
+
setPageSize: (size: number) => void;
|
|
119
|
+
handlePageChange: (page: number) => void;
|
|
120
|
+
handlePageSizeChange: (size: number | string) => void;
|
|
121
|
+
|
|
122
|
+
// Methods - Sorting
|
|
123
|
+
setSorting: (key: string, order?: 'asc' | 'desc') => void;
|
|
124
|
+
handleOrderBy: (event: { value: string; orderDirection: 'asc' | 'desc' }) => void;
|
|
125
|
+
|
|
126
|
+
// Methods - Selection
|
|
127
|
+
selectItem: (item: T) => void;
|
|
128
|
+
deselectItem: (item: T) => void;
|
|
129
|
+
selectAll: () => void;
|
|
130
|
+
selectAllOnPage: () => void;
|
|
131
|
+
clearSelection: () => void;
|
|
132
|
+
isSelected: (item: T) => boolean;
|
|
133
|
+
|
|
134
|
+
// Methods - Filtering
|
|
135
|
+
applyFilters: (filters: SmartFilter[]) => void;
|
|
136
|
+
clearFilters: () => void;
|
|
137
|
+
removeFilter: (index: number) => void;
|
|
138
|
+
handleSmartFiltersApplied: (filters: SmartFilter[]) => void;
|
|
139
|
+
handleSmartFiltersCleared: () => void;
|
|
140
|
+
handleSmartFilterDeleted: (index: number) => void;
|
|
141
|
+
|
|
142
|
+
// Methods - Column visibility
|
|
143
|
+
toggleColumn: (index: number) => void;
|
|
144
|
+
showColumn: (index: number) => void;
|
|
145
|
+
hideColumn: (index: number) => void;
|
|
146
|
+
handleColumnsVisibilityChanged: (event: { index: number; hidden: boolean }) => void;
|
|
147
|
+
|
|
148
|
+
// Methods - Modal actions
|
|
149
|
+
handleModalAction: (event: { modal: string; action: string }) => void;
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Smart Filters
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
interface SmartFilter {
|
|
157
|
+
category: string; // Field name to filter on
|
|
158
|
+
operator: string; // Filter operator: 'contains', 'is', '>', '<', '>=', '<='
|
|
159
|
+
value: any; // Filter value
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Supported operators:
|
|
164
|
+
- `contains`: String contains (case-insensitive)
|
|
165
|
+
- `is` or `=`: Exact match
|
|
166
|
+
- `>` or `greater`: Greater than (numeric)
|
|
167
|
+
- `<` or `less`: Less than (numeric)
|
|
168
|
+
- `>=` or `greaterOrEqual`: Greater than or equal (numeric)
|
|
169
|
+
- `<=` or `lessOrEqual`: Less than or equal (numeric)
|
|
170
|
+
|
|
171
|
+
## Examples
|
|
172
|
+
|
|
173
|
+
### Basic Table with Pagination
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const table = useTable({
|
|
177
|
+
items: accounts,
|
|
178
|
+
pageSize: 25,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Use in template
|
|
182
|
+
table.paginatedItems.value // Current page items
|
|
183
|
+
table.currentPage.value // Current page number
|
|
184
|
+
table.totalPages.value // Total pages
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### With Sorting
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
const table = useTable({
|
|
191
|
+
items: accounts,
|
|
192
|
+
sortKey: 'created_at',
|
|
193
|
+
sortOrder: 'desc',
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Programmatic sorting
|
|
197
|
+
table.setSorting('name', 'asc');
|
|
198
|
+
|
|
199
|
+
// Or use the event handler
|
|
200
|
+
@order-by="table.handleOrderBy"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### With Filtering
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
const table = useTable({
|
|
207
|
+
items: accounts,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Apply filters programmatically
|
|
211
|
+
table.applyFilters([
|
|
212
|
+
{ category: 'name', operator: 'contains', value: 'hotel' },
|
|
213
|
+
{ category: 'status', operator: 'is', value: 'active' },
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
// Or use the event handlers
|
|
217
|
+
@smart-filters-sent="table.handleSmartFiltersApplied"
|
|
218
|
+
@smart-filters-cleared="table.handleSmartFiltersCleared"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### With Selection
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const table = useTable({
|
|
225
|
+
items: accounts,
|
|
226
|
+
idKey: 'account_id', // Use custom ID field
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Check selection state
|
|
230
|
+
table.selectedItems.value // Array of selected items
|
|
231
|
+
table.selectedItemIds.value // Set of selected IDs
|
|
232
|
+
table.allSelected.value // All items selected?
|
|
233
|
+
table.someSelected.value // Some items selected?
|
|
234
|
+
|
|
235
|
+
// Programmatic selection
|
|
236
|
+
table.selectItem(account);
|
|
237
|
+
table.selectAll();
|
|
238
|
+
table.clearSelection();
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Complete Example
|
|
242
|
+
|
|
243
|
+
```vue
|
|
244
|
+
<template>
|
|
245
|
+
<div>
|
|
246
|
+
<!-- Stats -->
|
|
247
|
+
<div class="stats">
|
|
248
|
+
<div>Total: {{ table.totalItems.value }}</div>
|
|
249
|
+
<div>Selected: {{ table.selectedItems.value.length }}</div>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<!-- Table -->
|
|
253
|
+
<Table
|
|
254
|
+
:header="columns"
|
|
255
|
+
:items="tableItems"
|
|
256
|
+
:current-page="table.currentPage.value"
|
|
257
|
+
:total-pages="table.totalPages.value"
|
|
258
|
+
:total-items="table.totalItems.value"
|
|
259
|
+
:page-size="table.pageSize.value"
|
|
260
|
+
:ordered-by="table.sortKey.value"
|
|
261
|
+
:order-direction="table.sortOrder.value"
|
|
262
|
+
:hidden-columns="table.hiddenColumns.value"
|
|
263
|
+
:reset-selected="table.resetSelected.value"
|
|
264
|
+
:smart-filter-categories="filterCategories"
|
|
265
|
+
@change-page="table.handlePageChange"
|
|
266
|
+
@change-page-size="table.handlePageSizeChange"
|
|
267
|
+
@order-by="table.handleOrderBy"
|
|
268
|
+
@smart-filters-sent="table.handleSmartFiltersApplied"
|
|
269
|
+
@smart-filters-cleared="table.handleSmartFiltersCleared"
|
|
270
|
+
@smart-filter-deleted="table.handleSmartFilterDeleted"
|
|
271
|
+
@columns-visibility-changed="table.handleColumnsVisibilityChanged"
|
|
272
|
+
@modal-action="table.handleModalAction"
|
|
273
|
+
/>
|
|
274
|
+
</div>
|
|
275
|
+
</template>
|
|
276
|
+
|
|
277
|
+
<script setup lang="ts">
|
|
278
|
+
import { computed } from 'vue';
|
|
279
|
+
import { useTable } from '@htlkg/components/composables';
|
|
280
|
+
import { Table } from '@htlkg/components/data';
|
|
281
|
+
|
|
282
|
+
// Your data
|
|
283
|
+
const accounts = ref([...]);
|
|
284
|
+
|
|
285
|
+
// Initialize table
|
|
286
|
+
const table = useTable({
|
|
287
|
+
items: accounts.value,
|
|
288
|
+
pageSize: 10,
|
|
289
|
+
sortKey: 'name',
|
|
290
|
+
sortOrder: 'asc',
|
|
291
|
+
idKey: 'id',
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Transform data for display
|
|
295
|
+
const tableItems = computed(() => {
|
|
296
|
+
return table.paginatedItems.value.map(account => ({
|
|
297
|
+
id: account.id,
|
|
298
|
+
row: [
|
|
299
|
+
account.name,
|
|
300
|
+
{ content: account.status, type: 'tag', color: 'green' },
|
|
301
|
+
account.created_at,
|
|
302
|
+
],
|
|
303
|
+
}));
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Table configuration
|
|
307
|
+
const columns = [
|
|
308
|
+
{ name: 'Name', value: 'name' },
|
|
309
|
+
{ name: 'Status', value: 'status' },
|
|
310
|
+
{ name: 'Created', value: 'created_at' },
|
|
311
|
+
];
|
|
312
|
+
|
|
313
|
+
const filterCategories = [
|
|
314
|
+
{
|
|
315
|
+
id: 'name',
|
|
316
|
+
name: 'Name',
|
|
317
|
+
componentType: 'uiInput',
|
|
318
|
+
defaultProps: { placeholder: 'Search...' },
|
|
319
|
+
},
|
|
320
|
+
];
|
|
321
|
+
</script>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Best Practices
|
|
325
|
+
|
|
326
|
+
1. **Use computed for items**: Pass reactive data to `useTable` for automatic updates
|
|
327
|
+
2. **Transform data separately**: Keep raw data in `useTable`, transform for display in computed
|
|
328
|
+
3. **Custom ID keys**: Use `idKey` option when your items don't have an `id` property
|
|
329
|
+
4. **Event handlers**: Use the built-in `handle*` methods for seamless uiTable integration
|
|
330
|
+
5. **Filter logic**: The composable handles AND logic by default; customize if needed
|
|
331
|
+
|
|
332
|
+
## Migration from Manual State
|
|
333
|
+
|
|
334
|
+
Before:
|
|
335
|
+
```typescript
|
|
336
|
+
const currentPage = ref(1);
|
|
337
|
+
const pageSize = ref(10);
|
|
338
|
+
const sortKey = ref('name');
|
|
339
|
+
// ... lots of manual state management
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
After:
|
|
343
|
+
```typescript
|
|
344
|
+
const table = useTable({
|
|
345
|
+
items: myData,
|
|
346
|
+
pageSize: 10,
|
|
347
|
+
sortKey: 'name',
|
|
348
|
+
});
|
|
349
|
+
// All state and handlers included!
|
|
350
|
+
```
|