From deb49b224260395d7b9e8c737df52d8af759fc1e Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Wed, 14 Jan 2026 16:10:17 +0800 Subject: [PATCH] pagination and search stuff --- next-ui/src/composables/pagination.ts | 59 +++++++++++++++++++++++++++ next-ui/src/composables/search.ts | 21 ++++++++++ next-ui/src/types/PageRequest.ts | 11 +++++ 3 files changed, 91 insertions(+) create mode 100644 next-ui/src/composables/pagination.ts create mode 100644 next-ui/src/composables/search.ts diff --git a/next-ui/src/composables/pagination.ts b/next-ui/src/composables/pagination.ts new file mode 100644 index 00000000..cd08f641 --- /dev/null +++ b/next-ui/src/composables/pagination.ts @@ -0,0 +1,59 @@ +import { syncRef } from '@vueuse/core' +import type { PageSize } from '@/types/page' +import { useRouteQuery } from '@vueuse/router' + +/** + * Provide synchronized refs for page tracking in 0-index and 1-index. + * + * `page1` is initialized from `route.query.page`, the route query is kept in sync. + * + * The current page can never be over the `pageCount`. + * The consumer is responsible for updating `pageCount`. + */ +export function usePagination() { + const queryPage = useRouteQuery('page', '1', { transform: Number }) + const page0 = ref(queryPage.value - 1) + const page1 = ref(queryPage.value) + const pageCount = ref(0) + + syncRef(page0, page1, { + transform: { + ltr: (left) => left + 1, + rtl: (right) => right - 1, + }, + }) + syncRef(page1, queryPage, { + direction: 'ltr', + }) + + watch([page1, pageCount], ([newPage1, newPageCount]) => { + if (newPage1 > newPageCount) page1.value = newPageCount + }) + + return { + /** + * The 0-index current page + */ + page0: page0, + /** + * The 1-index current page + */ + page1: page1, + /** + * The total page count. Should be updated by the consumer. + */ + pageCount: pageCount, + } +} + +/** + * Reactive itemsPerPage in Vuetify format, where unpaged is converted to `-1` + * @param pageSize + */ +export function useItemsPerPage(pageSize: MaybeRefOrGetter) { + const itemsPerPage = computed(() => (pageSize === 'unpaged' ? -1 : (pageSize as number))) + + return { + itemsPerPage: itemsPerPage, + } +} diff --git a/next-ui/src/composables/search.ts b/next-ui/src/composables/search.ts new file mode 100644 index 00000000..f849b211 --- /dev/null +++ b/next-ui/src/composables/search.ts @@ -0,0 +1,21 @@ +import type { components } from '@/generated/openapi/komga' + +export function useSearchConditionLibraries( + libraries: MaybeRefOrGetter, +) { + const condition = computed(() => { + return { + anyOf: + toValue(libraries)?.map((it) => ({ + libraryId: { + operator: 'Is', + value: it.id, + }, + })) ?? [], + } + }) + + return { + librariesCondition: condition, + } +} diff --git a/next-ui/src/types/PageRequest.ts b/next-ui/src/types/PageRequest.ts index ce12d321..5373cce4 100644 --- a/next-ui/src/types/PageRequest.ts +++ b/next-ui/src/types/PageRequest.ts @@ -1,4 +1,6 @@ // from Vuetify +import type { PageSize } from '@/types/page' + export type SortItem = { key: string; order?: boolean | 'asc' | 'desc' } function vSortItemToSort(sortItem: SortItem): string { @@ -13,6 +15,15 @@ export class PageRequest { readonly size?: number readonly sort?: string[] + static FromPageSize(pageSize: PageSize, page?: number, sort?: string[]) { + return new PageRequest( + page, + pageSize === 'unpaged' ? undefined : pageSize, + sort, + pageSize === 'unpaged', + ) + } + static Unpaged(): PageRequest { return new PageRequest(undefined, undefined, undefined, true) }