UNPKG

@grail-ui/svelte

Version:

[![NPM](https://img.shields.io/npm/v/@grail-ui/svelte)](https://www.npmjs.com/package/@grail-ui/svelte) [![minified](https://img.shields.io/bundlephobia/min/@grail-ui/svelte)](https://bundlephobia.com/package/@grail-ui/svelte) [![minified + zipped](https:

113 lines (112 loc) 4.53 kB
import { derived, readable, writable } from 'svelte/store'; import { writableEffect } from '../util/store.js'; export const createPagination = ({ boundaryCount = 1, total = 0, perPage = 10, hideNextButton = false, hidePrevButton = false, page = 1, hideFirstButton = true, hideLastButton = true, siblingCount = 1, ariaLabel = 'Pagination Navigation', getPageAriaLabel = (page) => `Goto Page ${page}`, onPageChange, } = {}) => { const page$ = writableEffect(page, onPageChange); const total$ = writable(total); const perPage$ = writable(perPage); const count$ = derived([total$, perPage$], ([$total, $perPage]) => { return Math.ceil(+$total / +$perPage); }); const items = derived([page$, count$], ([$page, $count]) => { const itemList = getItems({ boundaryCount, count: $count, hideNextButton, hidePrevButton, page: $page, hideFirstButton, hideLastButton, siblingCount, }); return itemList; }); const navAttrs = readable({ role: 'navigation', 'aria-label': ariaLabel }); const pageAttrs = derived(page$, () => { return function (item) { return { ...(item.page && !item.disabled ? { 'aria-label': getPageAriaLabel(item.page, !!item.selected) } : {}), ...(item.selected ? { 'aria-current': 'page' } : {}), ...(item.disabled ? { disabled: 'true' } : {}), }; }; }); const start = derived([page$, perPage$, total$], ([$page, $perPage, $total]) => Math.min(Math.max(1 + ((+$page || 1) - 1) * +$perPage, 0), +$total)); const end = derived([start, perPage$, total$], ([$start, $perPage, $total]) => Math.min($start + (+$perPage - 1), +$total)); return { navAttrs, pageAttrs, items, page: page$, start, end, total: total$, perPage: perPage$, }; }; function getPageItem(item, page, count) { if (typeof item === 'number') { return { type: 'page', page: item, selected: item === page, }; } switch (item) { case 'first': return { type: item, page: 1, disabled: page <= 1 }; case 'previous': return { type: item, page: page - 1, disabled: page <= 1 }; case 'next': return { type: item, page: page + 1, disabled: page >= count }; case 'last': return { type: item, page: count, disabled: page >= count }; default: return { type: item, page: null, disabled: true }; } } function getItems({ boundaryCount, count, hideNextButton, hidePrevButton, page, hideFirstButton, hideLastButton, siblingCount, }) { const range = (start, end) => Array.from({ length: end - start + 1 }, (_, i) => start + i); const startPages = range(1, Math.min(boundaryCount, count)); const endPages = range(Math.max(count - boundaryCount + 1, boundaryCount + 1), count); const siblingsStart = Math.max(Math.min( // Natural start page - siblingCount, // Lower boundary when page is high count - boundaryCount - siblingCount * 2 - 1), // Greater than startPages boundaryCount + 2); const siblingsEnd = Math.min(Math.max( // Natural end page + siblingCount, // Upper boundary when page is low boundaryCount + siblingCount * 2 + 2), // Less than endPages endPages.length > 0 ? endPages[0] - 2 : count - 1); // Basic list of items to render return [ ...(hideFirstButton ? [] : ['first']), ...(hidePrevButton ? [] : ['previous']), ...startPages, // Start ellipsis // eslint-disable-next-line no-nested-ternary ...(siblingsStart > boundaryCount + 2 ? ['ellipsis-start'] : boundaryCount + 1 < count - boundaryCount ? [boundaryCount + 1] : []), // Sibling pages ...range(siblingsStart, siblingsEnd), // End ellipsis // eslint-disable-next-line no-nested-ternary ...(siblingsEnd < count - boundaryCount - 1 ? ['ellipsis-end'] : count - boundaryCount > boundaryCount ? [count - boundaryCount] : []), ...endPages, ...(hideNextButton ? [] : ['next']), ...(hideLastButton ? [] : ['last']), ].map((i) => getPageItem(i, page, count)); }