Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,49 @@ export default {
]
```

### List Page Size Options
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.
```typescript title="./resources/apartments.ts"
export default {
resourceId: 'aparts',
options: {
...
listPageSize: 10,
// listPageSizeOptions can be a static array
//diff-add
listPageSizeOptions: [10, 20, 50],
// OR a function for dynamic options based on user role
//diff-add
listPageSizeOptions: ({ adminUser }) => {
//diff-add
Comment thread
kulikp1 marked this conversation as resolved.
if (adminUser?.dbUser?.role === 'superadmin') {
//diff-add
return [50, 100, 500];
//diff-add
}
//diff-add
return [10, 20, 50];
//diff-add
},
}
Comment on lines +329 to +346
}
]
```

#### How it works
- listPageSize defines the default number of records per page when the list is opened.
- listPageSizeOptions defines the available page size options shown to the user.

For example: listPageSizeOptions: [10, 20, 50] will allow switching between 10 / 20 / 50 records per page.

#### UI behavior
Page size switching is implemented via a select dropdown (select input) in the table pagination controls.
- User opens the select
- Chooses a value (e.g. 20)
- Table reloads with the new page size
> ☝️Notes
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.

### Virtual scroll

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.
Expand Down
3 changes: 3 additions & 0 deletions adminforth/modules/restApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,9 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
),
options: {
...resource.options,
listPageSizeOptions: typeof resource.options.listPageSizeOptions === 'function'
? await resource.options.listPageSizeOptions({ adminUser, adminforth: this.adminforth })
: resource.options.listPageSizeOptions,
fieldGroups: resource.options.fieldGroups?.map((group, i) => ({
...group,
noTitle: group.noTitle ?? false,
Expand Down
2 changes: 1 addition & 1 deletion adminforth/spa/src/afcl/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
:class="[{'cursor-pointer': searchDisabled}, classesForInput]"
autocomplete="off" data-custom="no-autofill"
:placeholder="
selectedItems.length && !multiple ? '' : (showDropdown ? $t('Search') : placeholder || $t('Select...'))
selectedItems.length && !multiple ? '' : (showDropdown && !searchDisabled ? $t('Search') : placeholder || $t('Select...'))
"
/>

Expand Down
54 changes: 54 additions & 0 deletions adminforth/spa/src/components/ResourceListTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,24 @@
</span>
</template>
</span>
<div v-if="totalRows > 0 && pageSizeOptionsComputed?.length"
class="flex items-center gap-2 ml-auto" >
<span class="text-sm text-lightListTablePaginationHelpText dark:text-darkListTablePaginationHelpText whitespace-nowrap">
{{ $t('Rows per page') }}
</span>
<Select
v-model="pageSizeInternal"
:options="pageSizeOptionsComputed"
:searchDisabled="true"
:disableTogleOfSelectedItem="true"
:style="{ width: selectDynamicWidth }"
:placeholder="pageSizeInternal?.toString()"
class="text-sm "
classesForInput="h-[34px] min-h-0 py-1 pl-2 pr-6 text-sm rounded-md cursor-pointer af-button-shadow
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w-fit

bg-lightDropdownButtonsBackground text-lightDropdownButtonsText border-lightDropdownButtonsBorder
dark:bg-darkDropdownButtonsBackground dark:text-darkDropdownButtonsText dark:border-darkDropdownButtonsBorder"
/>
</div>
Comment thread
kulikp1 marked this conversation as resolved.
</div>
</template>

Expand Down Expand Up @@ -368,6 +386,7 @@ import { useAdminforth } from '@/adminforth';
import Checkbox from '@/afcl/Checkbox.vue';
import ListActionsThreeDots from '@/components/ListActionsThreeDots.vue';
import CallActionWrapper from '@/components/CallActionWrapper.vue'
import Select from '@/afcl/Select.vue';

const coreStore = useCoreStore();
const { t } = useI18n();
Expand All @@ -378,6 +397,7 @@ const props = withDefaults(defineProps<{
rows: any[] | null,
totalRows: number,
pageSize: number,
pageSizeOptions?: { label: string; value: any }[] | number[] | null;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{ label: string; value: any }[]

I thought user can enter only array with numbers

checkboxes: any[],
sort: any[],
noRoundings?: boolean,
Expand All @@ -402,9 +422,16 @@ const rowsToRender = computed(() => {
}
});

const selectDynamicWidth = computed(() => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just remove fixed width and everything will be fine

const length = pageSizeInternal.value?.toString().length || 2;
const calculatedWidth = length * 9 + 50;
return `${Math.max(68, calculatedWidth)}px`;
});

// emits, update page
const emits = defineEmits([
'update:page',
'update:pageSize',
'update:sort',
'update:checkboxes',
'update:records'
Expand All @@ -414,6 +441,25 @@ const emits = defineEmits([
const checkboxesInternal: Ref<any[]> = ref([]);
const pageInput = ref('1');
const page = ref(1);

const pageSizeInternal = ref(props.pageSize);

const pageSizeOptionsComputed = computed(() => {
if (!props.pageSizeOptions || props.pageSizeOptions.length === 0) {
return null;
}

const firstItem = props.pageSizeOptions[0];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove it

if (typeof firstItem === 'object' && firstItem !== null) {
return props.pageSizeOptions as { label: string; value: any }[];
}

return (props.pageSizeOptions as number[]).map(size => ({
label: size.toString(),
value: size
}));
});

const sort: Ref<Array<{field: string, direction: string}>> = ref([]);

const showListActionsThreeDots = computed(() => {
Expand Down Expand Up @@ -465,6 +511,14 @@ watch(() => props.page, (newPage) => {
page.value = newPage;
});

watch(() => props.pageSize, (newPageSize) => {
pageSizeInternal.value = newPageSize;
});

watch(pageSizeInternal, (newPageSize) => {
emits('update:pageSize', newPageSize);
});

const rowRefs = useTemplateRef<HTMLElement[]>('rowRefs');
const headerRefs = useTemplateRef<HTMLElement[]>('headerRefs');
const rowHeights = ref<number[]>([]);
Expand Down
46 changes: 43 additions & 3 deletions adminforth/spa/src/views/ListView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@
@update:sort="sort = $event"
@update:checkboxes="checkboxes = $event"
@update:records="getListInner"
:pageSizeOptions="PAGE_SIZE_OPTIONS"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove it

@update:pageSize="pageSize = $event"
:sort="sort"
:pageSize="pageSize"
:totalRows="totalRows"
Expand Down Expand Up @@ -260,8 +262,46 @@ const customActionLoadingStates = ref<{[key: string]: boolean}>({});

const DEFAULT_PAGE_SIZE = 10;

const PAGE_SIZE_OPTIONS = computed(() => {
const array = coreStore.resource?.options?.listPageSizeOptions;

if (!Array.isArray(array)) return undefined;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null


return array.map((size: number) => ({
label: size.toString(),
value: size
}));
});

const pageSize = computed({
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove it

get() {
if (route.query.pageSize) {
const parsed = parseInt(route.query.pageSize as string);
if (PAGE_SIZE_OPTIONS.value?.some(o => o.value === parsed)) {
return parsed;
}
}

if (coreStore.resource?.options?.listPageSize) {
return coreStore.resource.options.listPageSize;
}

return DEFAULT_PAGE_SIZE;
},

set(newSize) {
if (initInProcess) return;

page.value = 1;

setQuery({
page: 1,
pageSize: newSize,
});
}
});


const pageSize = computed(() => coreStore.resource?.options?.listPageSize || DEFAULT_PAGE_SIZE);
const isVirtualScrollEnabled = computed(() => coreStore.resource?.options?.listVirtualScrollEnabled || false);
const listBufferSize = computed(() => coreStore.resource?.options?.listBufferSize || 30);

Expand Down Expand Up @@ -427,7 +467,7 @@ async function init() {
// page init should be also in same tick
if (route.query.page) {
page.value = parseInt(route.query.page as string);
}
}

// getList(); - Not needed here, watch will trigger it
columnsMinMax.value = await callAdminForthApi({
Expand All @@ -446,7 +486,7 @@ async function init() {
}
}

watch([page, sort, () => filtersStore.filters], async () => {
watch([page, pageSize, sort, () => filtersStore.filters], async () => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove it

// console.log('🔄️ page/sort/filter change fired, page:', page.value);
await getListInner();
}, { deep: true });
Expand Down
7 changes: 7 additions & 0 deletions adminforth/types/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,13 @@ export interface AdminForthResourceInputCommon {
* Page size for list view
*/
listPageSize?: number,
Comment thread
kulikp1 marked this conversation as resolved.

/**
* Available page size options for list view, provided as an array of page sizes
* or a function returning them. When set together with `listPageSize`, the page
* size should be one of the values returned here.
*/
listPageSizeOptions?: number[] | ((args: { adminUser: AdminUser, adminforth: IAdminForth }) => number[] | Promise<number[]>);

/**
* Whether to use virtual scroll in list view.
Expand Down