@veristone/nuxt-v-app 0.2.5 → 0.2.6
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.
|
@@ -32,13 +32,15 @@ The primary table component. Wraps Nuxt UI's `<UTable>` with a toolbar, filter c
|
|
|
32
32
|
| `data` | `unknown[]` | **required** | Array of row data |
|
|
33
33
|
| `columns` | `TableColumn[]` | **required** | TanStack column definitions |
|
|
34
34
|
| `loading` | `boolean` | `false` | Shows loading skeleton on initial load |
|
|
35
|
-
| `initialPageLimit` | `number` | `10` | Rows per page |
|
|
35
|
+
| `initialPageLimit` | `number` | `10` | Rows per page (client-side mode) |
|
|
36
36
|
| `selectable` | `boolean` | `false` | Enable row selection checkboxes |
|
|
37
37
|
| `filters` | `FilterDefinition[]` | — | Filter dropdown definitions |
|
|
38
38
|
| `onRefresh` | `() => void \| Promise<void>` | — | Shows refresh button; called on click |
|
|
39
39
|
| `onRowClick` | `(row: unknown) => void` | — | Called when a row is clicked |
|
|
40
40
|
| `defaultSort` | `string` | — | Column accessorKey to sort by on mount |
|
|
41
41
|
| `defaultSortDesc` | `boolean` | `false` | Sort descending by default |
|
|
42
|
+
| `manualPagination` | `boolean` | `false` | Enable server-side pagination mode |
|
|
43
|
+
| `total` | `number` | — | Total row count from server (required for manualPagination) |
|
|
42
44
|
|
|
43
45
|
#### v-model Bindings
|
|
44
46
|
|
|
@@ -46,6 +48,9 @@ The primary table component. Wraps Nuxt UI's `<UTable>` with a toolbar, filter c
|
|
|
46
48
|
|-------|------|-------------|
|
|
47
49
|
| `sorting` | `SortingState` | TanStack sorting state |
|
|
48
50
|
| `filterValues` | `Record<string, unknown>` | Active filter values |
|
|
51
|
+
| `globalFilter` | `string` | Search/query filter |
|
|
52
|
+
| `page` | `number` | Current page (1-based, server-side mode) |
|
|
53
|
+
| `itemsPerPage` | `number` | Page size (server-side mode) |
|
|
49
54
|
|
|
50
55
|
#### Slots
|
|
51
56
|
|
|
@@ -371,6 +376,58 @@ const columns = [
|
|
|
371
376
|
</template>
|
|
372
377
|
```
|
|
373
378
|
|
|
379
|
+
### Server-side pagination
|
|
380
|
+
|
|
381
|
+
```vue
|
|
382
|
+
<script setup>
|
|
383
|
+
const columns = [
|
|
384
|
+
{ accessorKey: 'name', header: 'Name' },
|
|
385
|
+
{ accessorKey: 'email', header: 'Email' },
|
|
386
|
+
{ accessorKey: 'status', header: 'Status' },
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
// Server-side pagination state
|
|
390
|
+
const page = ref(1)
|
|
391
|
+
const itemsPerPage = ref(20)
|
|
392
|
+
const total = ref(0)
|
|
393
|
+
const users = ref([])
|
|
394
|
+
const loading = ref(false)
|
|
395
|
+
|
|
396
|
+
// Fetch data from API with pagination params
|
|
397
|
+
async function fetchUsers() {
|
|
398
|
+
loading.value = true
|
|
399
|
+
try {
|
|
400
|
+
const response = await $fetch('/api/users', {
|
|
401
|
+
query: {
|
|
402
|
+
page: page.value,
|
|
403
|
+
limit: itemsPerPage.value,
|
|
404
|
+
}
|
|
405
|
+
})
|
|
406
|
+
users.value = response.data
|
|
407
|
+
total.value = response.total
|
|
408
|
+
} finally {
|
|
409
|
+
loading.value = false
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// React to pagination changes
|
|
414
|
+
watch([page, itemsPerPage], fetchUsers, { immediate: true })
|
|
415
|
+
</script>
|
|
416
|
+
|
|
417
|
+
<template>
|
|
418
|
+
<VATable
|
|
419
|
+
name="Users"
|
|
420
|
+
:data="users"
|
|
421
|
+
:columns="columns"
|
|
422
|
+
:loading="loading"
|
|
423
|
+
:manual-pagination="true"
|
|
424
|
+
:total="total"
|
|
425
|
+
v-model:page="page"
|
|
426
|
+
v-model:items-per-page="itemsPerPage"
|
|
427
|
+
/>
|
|
428
|
+
</template>
|
|
429
|
+
```
|
|
430
|
+
|
|
374
431
|
---
|
|
375
432
|
|
|
376
433
|
## Date Auto-Detection
|
|
@@ -106,7 +106,13 @@
|
|
|
106
106
|
:loading="loading"
|
|
107
107
|
:row-selection-options="{ enableRowSelection: props.selectable }"
|
|
108
108
|
:sorting-options="{ getSortedRowModel: getSortedRowModel() }"
|
|
109
|
-
:pagination-options="{
|
|
109
|
+
:pagination-options="{
|
|
110
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
111
|
+
manualPagination: props.manualPagination,
|
|
112
|
+
...(props.manualPagination && props.total !== undefined
|
|
113
|
+
? { pageCount: Math.ceil(props.total / pagination.pageSize) }
|
|
114
|
+
: {})
|
|
115
|
+
}"
|
|
110
116
|
:ui="{
|
|
111
117
|
th: 'px-3 py-2',
|
|
112
118
|
td: 'px-3 py-2',
|
|
@@ -148,7 +154,7 @@
|
|
|
148
154
|
:items-per-page="pagination.pageSize"
|
|
149
155
|
:total="totalRows"
|
|
150
156
|
size="sm"
|
|
151
|
-
@update:page="
|
|
157
|
+
@update:page="onPageChange"
|
|
152
158
|
/>
|
|
153
159
|
</div>
|
|
154
160
|
</div>
|
|
@@ -184,8 +190,13 @@ const props = withDefaults(defineProps<{
|
|
|
184
190
|
defaultSort?: string;
|
|
185
191
|
/** Default sort direction */
|
|
186
192
|
defaultSortDesc?: boolean;
|
|
193
|
+
/** Enable server-side pagination mode */
|
|
194
|
+
manualPagination?: boolean;
|
|
195
|
+
/** Total row count from server (required for manualPagination) */
|
|
196
|
+
total?: number;
|
|
187
197
|
}>(), {
|
|
188
198
|
selectable: false,
|
|
199
|
+
manualPagination: false,
|
|
189
200
|
});
|
|
190
201
|
|
|
191
202
|
// Sorting state - v-model support
|
|
@@ -197,6 +208,41 @@ const filterValues = defineModel<Record<string, unknown>>("filterValues", {
|
|
|
197
208
|
default: () => ({}),
|
|
198
209
|
});
|
|
199
210
|
|
|
211
|
+
// Server-side pagination - v-model support (1-based page)
|
|
212
|
+
const page = defineModel<number>("page", { default: 1 });
|
|
213
|
+
const itemsPerPage = defineModel<number>("itemsPerPage", { default: 10 });
|
|
214
|
+
|
|
215
|
+
// Internal pagination state (0-based pageIndex for TanStack)
|
|
216
|
+
const pagination = ref({
|
|
217
|
+
pageIndex: 0,
|
|
218
|
+
pageSize: 10,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Initialize pagination from props and sync with v-model
|
|
222
|
+
onMounted(() => {
|
|
223
|
+
const defaultPageSize = props.initialPageLimit ?? 10;
|
|
224
|
+
pagination.value.pageSize = defaultPageSize;
|
|
225
|
+
|
|
226
|
+
// Sync itemsPerPage if not already set by parent
|
|
227
|
+
if (itemsPerPage.value === 10 && defaultPageSize !== 10) {
|
|
228
|
+
itemsPerPage.value = defaultPageSize;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Sync internal pagination with v-model when in manual mode
|
|
233
|
+
watch(
|
|
234
|
+
[page, itemsPerPage],
|
|
235
|
+
([newPage, newItemsPerPage]) => {
|
|
236
|
+
if (props.manualPagination) {
|
|
237
|
+
pagination.value = {
|
|
238
|
+
pageIndex: newPage - 1, // Convert 1-based to 0-based
|
|
239
|
+
pageSize: newItemsPerPage,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
{ immediate: true }
|
|
244
|
+
);
|
|
245
|
+
|
|
200
246
|
const refreshing = ref(false);
|
|
201
247
|
const slots = useSlots()
|
|
202
248
|
|
|
@@ -289,12 +335,6 @@ onMounted(() => {
|
|
|
289
335
|
}
|
|
290
336
|
});
|
|
291
337
|
|
|
292
|
-
// Pagination state
|
|
293
|
-
const pagination = ref({
|
|
294
|
-
pageIndex: 0,
|
|
295
|
-
pageSize: props.initialPageLimit ?? 10,
|
|
296
|
-
});
|
|
297
|
-
|
|
298
338
|
// Column visibility state
|
|
299
339
|
const columnVisibility = ref<Record<string, boolean>>({});
|
|
300
340
|
|
|
@@ -446,6 +486,9 @@ function handleRowClick(row: any) {
|
|
|
446
486
|
|
|
447
487
|
// Computed pagination info
|
|
448
488
|
const totalRows = computed(() => {
|
|
489
|
+
if (props.manualPagination && props.total !== undefined) {
|
|
490
|
+
return props.total;
|
|
491
|
+
}
|
|
449
492
|
return table.value?.tableApi?.getFilteredRowModel().rows.length ?? 0;
|
|
450
493
|
});
|
|
451
494
|
|
|
@@ -461,17 +504,35 @@ const endIndex = computed(() => {
|
|
|
461
504
|
|
|
462
505
|
// Handle page size change
|
|
463
506
|
const onPageSizeChange = (size: number) => {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
507
|
+
if (props.manualPagination) {
|
|
508
|
+
// Emit to parent for server-side handling
|
|
509
|
+
itemsPerPage.value = size;
|
|
510
|
+
page.value = 1; // Reset to first page
|
|
511
|
+
} else {
|
|
512
|
+
pagination.value = {
|
|
513
|
+
pageIndex: 0,
|
|
514
|
+
pageSize: size,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
468
517
|
};
|
|
469
518
|
|
|
470
|
-
//
|
|
519
|
+
// Handle page change from UPagination
|
|
520
|
+
const onPageChange = (newPage: number) => {
|
|
521
|
+
if (props.manualPagination) {
|
|
522
|
+
// Emit to parent for server-side handling
|
|
523
|
+
page.value = newPage;
|
|
524
|
+
} else {
|
|
525
|
+
pagination.value.pageIndex = newPage - 1;
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// Reset pagination when data changes (client-side only)
|
|
471
530
|
watch(
|
|
472
531
|
() => props.data,
|
|
473
532
|
() => {
|
|
474
|
-
|
|
533
|
+
if (!props.manualPagination) {
|
|
534
|
+
pagination.value.pageIndex = 0;
|
|
535
|
+
}
|
|
475
536
|
}
|
|
476
537
|
);
|
|
477
538
|
|