Skip to content

Commit 2cc7c4e

Browse files
committed
feat: add pagination size options for list views with dynamic selection
1 parent f61689c commit 2cc7c4e

4 files changed

Lines changed: 124 additions & 3 deletions

File tree

adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,48 @@ export default {
319319
]
320320
```
321321

322+
### List Page Size Options
323+
You can define available pagination sizes using options.listPageSizeOptions. This allows users to choose how many records they want to see per page in the list view.
324+
```typescript title="./resources/apartments.ts"
325+
export default {
326+
resourceId: 'aparts',
327+
options: {
328+
...
329+
listPageSize: 10,
330+
// listPageSizeOptions can be a static array
331+
//diff-add
332+
listPageSizeOptions: [10, 20, 50],
333+
// OR a function for dynamic options based on user role
334+
//diff-add
335+
listPageSizeOptions: ({ adminUser }) => {
336+
//diff-add
337+
if (adminUser?.dbUser?.role === 'superadmin') {
338+
//diff-add
339+
return [50, 100, 500];
340+
//diff-add
341+
}
342+
//diff-add
343+
return [10, 20, 50];
344+
//diff-add
345+
},
346+
}
347+
}
348+
]
349+
```
350+
#### How it works
351+
- listPageSize defines the default number of records per page when the list is opened.
352+
- listPageSizeOptions defines the available page size options shown to the user.
353+
354+
For example: listPageSizeOptions: [10, 20, 50] will allow switching between 10 / 20 / 50 records per page.
355+
356+
#### UI behavior
357+
Page size switching is implemented via a select dropdown (select input) in the table pagination controls.
358+
- User opens the select
359+
- Chooses a value (e.g. 20)
360+
- Table reloads with the new page size
361+
> ☝️Notes
362+
If `listPageSizeOptions` is not provided (or resolves to an empty array), the page size select is not shown. The selected value updates the table immediately and triggers a data refetch. Use `listPageSize` to define the initial number of records per page, and `listPageSizeOptions` to define which page sizes the user can switch between.
363+
322364
### Virtual scroll
323365

324366
Set `options.listVirtualScrollEnabled` to true to enable virtual scrolling in the table. The default value is false. Enable this option if you need to display a large number of records on a single page.

adminforth/spa/src/components/ResourceListTable.vue

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,22 @@
340340
</span>
341341
</template>
342342
</span>
343+
<div v-if="totalRows > 0 && pageSizeOptionsComputed?.length"
344+
class="flex items-center gap-2 ml-auto" >
345+
<span class="text-sm text-lightListTablePaginationHelpText dark:text-darkListTablePaginationHelpText whitespace-nowrap">
346+
{{ $t('Rows per page') }}
347+
</span>
348+
<Select
349+
v-model="pageSizeInternal"
350+
:options="pageSizeOptionsComputed"
351+
:searchDisabled="true"
352+
:disableTogleOfSelectedItem="true"
353+
:style="{ width: selectDynamicWidth }"
354+
:placeholder="pageSizeInternal?.toString()"
355+
class="text-sm"
356+
classesForInput="h-[34px] min-h-0 py-1 pl-2 pr-6 text-sm rounded-md cursor-pointer af-button-shadow bg-lightDropdownButtonsBackground text-lightDropdownButtonsText border-lightDropdownButtonsBorder dark:bg-darkDropdownButtonsBackground dark:text-darkDropdownButtonsText dark:border-darkDropdownButtonsBorder"
357+
/>
358+
</div>
343359
</div>
344360
</template>
345361

@@ -368,6 +384,7 @@ import { useAdminforth } from '@/adminforth';
368384
import Checkbox from '@/afcl/Checkbox.vue';
369385
import ListActionsThreeDots from '@/components/ListActionsThreeDots.vue';
370386
import CallActionWrapper from '@/components/CallActionWrapper.vue'
387+
import { Select } from '@/afcl';
371388
372389
const coreStore = useCoreStore();
373390
const { t } = useI18n();
@@ -407,10 +424,45 @@ const emits = defineEmits([
407424
'update:page',
408425
'update:sort',
409426
'update:checkboxes',
410-
'update:records'
427+
'update:records',
428+
'update:pageSize'
411429
412430
]);
413431
432+
const pageSizeOptionsComputed = computed(() => {
433+
const options = props.resource?.options?.listPageSizeOptions || [10, 20, 50, 100];
434+
return options.map(size => ({
435+
value: size,
436+
label: size.toString()
437+
}));
438+
});
439+
440+
const pageSizeInternal = ref(props.pageSize);
441+
442+
const selectDynamicWidth = computed(() => {
443+
const length = pageSizeInternal.value?.toString().length || 2;
444+
return `${length + 5}ch`;
445+
})
446+
447+
watch(() => pageSizeInternal.value, (newSize) => {
448+
if (newSize) {
449+
localStorage.setItem(`pageSize_${props.resource?.resourceId}`, newSize.toString());
450+
emits('update:pageSize', newSize);
451+
page.value = 1;
452+
}
453+
});
454+
455+
onMounted(() => {
456+
const savedSize = localStorage.getItem(`pageSize_${props.resource?.resourceId}`);
457+
if (savedSize) {
458+
const sizeNum = parseInt(savedSize);
459+
if (props.resource?.options?.listPageSizeOptions?.includes(sizeNum)) {
460+
pageSizeInternal.value = sizeNum;
461+
emits('update:pageSize', sizeNum);
462+
}
463+
}
464+
});
465+
414466
const checkboxesInternal: Ref<any[]> = ref([]);
415467
const pageInput = ref('1');
416468
const page = ref(1);

adminforth/spa/src/views/ListView.vue

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@
171171
@update:sort="sort = $event"
172172
@update:checkboxes="checkboxes = $event"
173173
@update:records="getListInner"
174+
@update:pageSize="(newSize) => { pageSize = newSize; page = 1; }"
174175
:sort="sort"
175176
:pageSize="pageSize"
176177
:totalRows="totalRows"
@@ -261,7 +262,23 @@ const customActionLoadingStates = ref<{[key: string]: boolean}>({});
261262
const DEFAULT_PAGE_SIZE = 10;
262263
263264
264-
const pageSize = computed(() => coreStore.resource?.options?.listPageSize || DEFAULT_PAGE_SIZE);
265+
const pageSize = ref(DEFAULT_PAGE_SIZE);
266+
267+
const syncPageSize = () => {
268+
const resourceId = route.params.resourceId;
269+
const savedSize = localStorage.getItem(`pageSize_${resourceId}`);
270+
271+
if (savedSize) {
272+
pageSize.value = parseInt(savedSize);
273+
}
274+
else if (coreStore.resource?.options?.listPageSize) {
275+
pageSize.value = coreStore.resource.options.listPageSize;
276+
}
277+
else {
278+
pageSize.value = DEFAULT_PAGE_SIZE;
279+
}
280+
};
281+
265282
const isVirtualScrollEnabled = computed(() => coreStore.resource?.options?.listVirtualScrollEnabled || false);
266283
const listBufferSize = computed(() => coreStore.resource?.options?.listBufferSize || 30);
267284
@@ -396,6 +413,9 @@ async function init() {
396413
await coreStore.fetchResourceFull({
397414
resourceId: route.params.resourceId as string
398415
});
416+
417+
syncPageSize();
418+
399419
isPageLoaded.value = true;
400420
// !!! clear filters should be in same tick with sort assignment so that watch can catch it as one change
401421
@@ -446,7 +466,7 @@ async function init() {
446466
}
447467
}
448468
449-
watch([page, sort, () => filtersStore.filters], async () => {
469+
watch([page, sort, pageSize, () => filtersStore.filters], async () => {
450470
// console.log('🔄️ page/sort/filter change fired, page:', page.value);
451471
await getListInner();
452472
}, { deep: true });

adminforth/types/Common.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,13 @@ export interface AdminForthResourceInputCommon {
472472
* Page size for list view
473473
*/
474474
listPageSize?: number,
475+
476+
/**
477+
* Available page size options for list view, provided as an array of page sizes
478+
* or a function returning them. When set together with `listPageSize`, the page
479+
* size should be one of the values returned here.
480+
*/
481+
listPageSizeOptions?: number[];
475482

476483
/**
477484
* Whether to use virtual scroll in list view.

0 commit comments

Comments
 (0)