How to create mantine/hooks usePagination in Vue. #3248
-
Hi, thank you so much for the awesome library!!! I'm trying to create a vue-equivalent to https://mantine.dev/hooks/use-pagination/ My current implementation seems working for the base use-case, but I haven't figured it out how to make props reactive, if someone can take a look would be great. DEMO: https://stackblitz.com/edit/nuxt-starter-wjfoat?file=composables%2Fpagination.ts Composableimport { ref, computed, ComputedRef } from 'vue';
export function range(start: number, end: number): number[] {
const length = end - start + 1;
return Array.from({ length }, (_, index) => index + start);
}
export const DOTS = 'dots';
export interface PaginationParams {
/** Page selected on initial render, defaults to 1 */
initialPage?: number;
/** Controlled active page number */
page?: number;
/** Total amount of pages */
total: number;
/** Siblings amount on left/right side of selected page, defaults to 1 */
siblings?: number;
/** Amount of elements visible on left/right edges, defaults to 1 */
boundaries?: number;
/** Callback fired after change of each page */
onChange?: (page: number) => void;
}
export interface PaginationResult {
range: ComputedRef<(number | typeof DOTS)[]>;
active: Ref<number>;
setPage: (pageNumber: number) => void;
next: () => void;
previous: () => void;
first: () => void;
last: () => void;
}
export function usePagination({
total,
siblings = 1,
boundaries = 1,
page,
initialPage = 1,
onChange,
}: PaginationParams): PaginationResult {
const _total = computed(() => Math.max(Math.trunc(total), 0));
const activePage = ref(page || initialPage);
const setPage = (pageNumber: number): void => {
if (pageNumber <= 0) {
activePage.value = 1;
} else if (pageNumber > _total.value) {
activePage.value = _total.value;
} else {
activePage.value = pageNumber;
}
};
const next = (): void => setPage(activePage.value + 1);
const previous = (): void => setPage(activePage.value - 1);
const first = (): void => setPage(1);
const last = (): void => setPage(_total.value);
const paginationRange = computed((): (number | typeof DOTS)[] => {
const totalPageNumbers = siblings * 2 + 3 + boundaries * 2;
if (totalPageNumbers >= _total.value) {
return range(1, _total.value);
}
const leftSiblingIndex = Math.max(activePage.value - siblings, boundaries);
const rightSiblingIndex = Math.min(
activePage.value + siblings,
_total.value - boundaries
);
const shouldShowLeftDots = leftSiblingIndex > boundaries + 2;
const shouldShowRightDots =
rightSiblingIndex < _total.value - (boundaries + 1);
if (!shouldShowLeftDots && shouldShowRightDots) {
const leftItemCount = siblings * 2 + boundaries + 2;
return [
...range(1, leftItemCount),
DOTS,
...range(_total.value - (boundaries - 1), _total.value),
];
}
if (shouldShowLeftDots && !shouldShowRightDots) {
const rightItemCount = boundaries + 1 + 2 * siblings;
return [
...range(1, boundaries),
DOTS,
...range(_total.value - rightItemCount, _total.value),
];
}
return [
...range(1, boundaries),
DOTS,
...range(leftSiblingIndex, rightSiblingIndex),
DOTS,
...range(_total.value - boundaries + 1, _total.value),
];
});
return {
range: paginationRange,
active: activePage,
setPage,
next,
previous,
first,
last,
};
} Small Demo:<script setup lang="ts">
const total = ref(100);
const { active, first, last, next, previous, range, setPage } = usePagination({ total: total.value });
</script>
<template>
<label>
Total:
<input v-model.number="total" type="number" />
</label>
<div class="flex gap-2 p-2">
<button @click="first">First</button>
<button @click="previous">Previous</button>
<button @click="next">Next</button>
<button @click="last">Last</button>
<button @click="setPage(5)">Go page 5</button>
</div>
Active: {{ active }} <br />
Total: {{ total }} <br />
<ul class="flex gap-2 p-2">
<li v-for="page in range" :class="{ 'font-bold': active === page }">{{ page }}</li>
</ul>
</template> I'm not as experienced with composables as with React hooks. I'm trying to achieve that when total (ref) changes, the composable in some way re-runs as it does in React (I know that's not how Vue reactivity works out of the box) Screen.Recording.2023-07-22.at.5.03.50.PM.mov |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
I found a working implementation at https://github.com/Sun0fABeach/vue-use-paginator |
Beta Was this translation helpful? Give feedback.
I found a working implementation at https://github.com/Sun0fABeach/vue-use-paginator