From dcd56c56ec847be957a719f22804d7a54074f042 Mon Sep 17 00:00:00 2001 From: Yishay Hazan Date: Fri, 24 Feb 2023 23:04:46 +0200 Subject: [PATCH 1/3] feat(component): improve pagination added props to pagination, siblingCount etc 206 --- .../routes/docs/daisy/pagination/index.tsx | 126 ++++++++- .../src/components/pagination/pagination.tsx | 36 +-- .../src/components/pagination/pagination.tsx | 262 ++++++++++++------ 3 files changed, 321 insertions(+), 103 deletions(-) diff --git a/apps/website/src/routes/docs/daisy/pagination/index.tsx b/apps/website/src/routes/docs/daisy/pagination/index.tsx index 971e4d718..3dbfb5070 100644 --- a/apps/website/src/routes/docs/daisy/pagination/index.tsx +++ b/apps/website/src/routes/docs/daisy/pagination/index.tsx @@ -1,20 +1,132 @@ -import { $, component$, useStore } from '@builder.io/qwik'; +import { component$, useSignal, useStore } from '@builder.io/qwik'; import { Pagination } from '@qwik-ui/theme-daisy'; export default component$(() => { - const store = useStore({ page: 665 }); + const store = useStore({ page: 9 }); + const showFirstButton = useSignal(false); + const showLastButton = useSignal(false); + const hideNextButton = useSignal(false); + const hidePrevButton = useSignal(false); + const pages = useSignal(10); + const siblingCount = useSignal(1); + const boundaryCount = useSignal(1); return ( -
+

This is the documentation for the Pagination

+ +
+ + + + + + + + + + + + + +
+
-

Basic Example:

{ + onPaging$={(newValue: number) => { store.page = newValue; - })} + }} + showFirstButton={showFirstButton.value} + showLastButton={showLastButton.value} + hideNextButton={hideNextButton.value} + hidePrevButton={hidePrevButton.value} + siblingCount={siblingCount.value} + boundaryCount={boundaryCount.value} />
diff --git a/packages/daisy/src/components/pagination/pagination.tsx b/packages/daisy/src/components/pagination/pagination.tsx index 0a22733c0..1627d58e4 100644 --- a/packages/daisy/src/components/pagination/pagination.tsx +++ b/packages/daisy/src/components/pagination/pagination.tsx @@ -2,10 +2,11 @@ import { component$, PropFunction } from '@builder.io/qwik'; import { IRenderPaginationItemProps, Pagination as HeadlessPagination, + IPaginationProps, } from '@qwik-ui/headless'; import { Button } from '../button/button'; -export interface PaginationProps { +export interface PaginationProps extends Omit { pages: number; page: number; onPaging$: PropFunction<(index: number) => void>; @@ -46,18 +47,21 @@ export const RenderPaginationItem = component$( } ); -export const Pagination = component$((props: PaginationProps) => { - return ( -
- { - return ...; - })} - /> -
- ); -}); +export const Pagination = component$( + ({ page, pages, onPaging$, ...rest }: PaginationProps) => { + return ( +
+ { + return ...; + })} + /> +
+ ); + } +); diff --git a/packages/headless/src/components/pagination/pagination.tsx b/packages/headless/src/components/pagination/pagination.tsx index d075ce624..89b0c7ed1 100644 --- a/packages/headless/src/components/pagination/pagination.tsx +++ b/packages/headless/src/components/pagination/pagination.tsx @@ -1,7 +1,7 @@ import { $, component$, PropFunction, Component } from '@builder.io/qwik'; import { Button as HeadlessButton } from '../button/button'; -export interface IPaginationProps { +export interface IPaginationProps extends IGetPaginationItemsOptions { pages: number; page: number; onPaging$: PropFunction<(index: number) => void>; @@ -14,50 +14,110 @@ export interface IRenderPaginationItemProps { disabled?: boolean; 'aria-label': string; 'aria-current'?: boolean; - value: PaginationItemValue; + value: TPaginationItemValue; key?: string | number; } -export type PaginationItemValue = 'prev' | 'next' | number; - -export function getPaginationItems(pages: number, page: number) { - // *show which arrows to light up - const canGo = { - prev: page > 1, - next: page < pages, - }; - - // one of first 5 pages -> should show 1, 2, 3, 4, 5 ... [last] - // or if length is less than 5, all pages - if (pages < 4 || page < 5) { - return { - items: Array.from({ length: Math.min(pages, 5) }, (_, i) => i + 1), - after: pages > 5 ? pages : -1, - before: -1, - ...canGo, - }; - } +export type TPaginationItemValue = + | 'prev' + | 'next' + | number + | 'start-ellipsis' + | 'end-ellipsis' + | 'first' + | 'last' + | string; - // one of last 4 pages -> should show [first] ... [6, 7, 8, 9, 10] - if (Math.abs(page - pages) < 4) { - return { - items: Array.from({ length: 5 }, (_, i) => pages - 4 + i), - before: 1, - after: -1, - ...canGo, - }; - } +const range = (start: number, end: number) => { + const length = end - start + 1; + return Array.from({ length }, (_, i) => start + i); +}; + +export type TPaginationItem = + | 'first' + | 'last' + | 'prev' + | 'next' + | 'divider' + | number + | string; + +export interface IGetPaginationItems { + page: number; + count: number; + options: IGetPaginationItemsOptions; +} - // it's somewhere in the middle - // -> [first] ... [4, 5, 6] ... [last] - return { - items: Array.from({ length: 3 }, (_, i) => page - 1 + i), - before: 1, - after: pages, - ...canGo, - }; +export interface IGetPaginationItemsOptions { + boundaryCount?: number; + siblingCount?: number; + hidePrevButton?: boolean; + hideNextButton?: boolean; + showFirstButton?: boolean; + showLastButton?: boolean; } +export const getPaginationItems = ( + page: IGetPaginationItems['page'], + count: IGetPaginationItems['count'], + { + boundaryCount = 1, + siblingCount = 1, + hidePrevButton, + hideNextButton, + showFirstButton, + showLastButton, + }: IGetPaginationItems['options'] +): TPaginationItem[] => { + 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( + page - siblingCount, // Natural start + count - boundaryCount - siblingCount * 2 - 1 // Lower boundary when page is high + ), + boundaryCount + 2 // Greater than startPages + ); + + const siblingsEnd = Math.min( + Math.max( + page + siblingCount, // Natural end + boundaryCount + siblingCount * 2 + 2 // Upper boundary when page is low + ), + endPages.length > 0 ? endPages[0] - 2 : count - 1 // Less than endPages + ); + + const items = [ + ...(showFirstButton ? ['first'] : []), + ...(hidePrevButton ? [] : ['prev']), + ...startPages, + + ...(siblingsStart > boundaryCount + 2 + ? ['divider'] + : boundaryCount + 1 < count - boundaryCount + ? [boundaryCount + 1] + : []), + + ...range(siblingsStart, siblingsEnd), + + ...(siblingsEnd < count - boundaryCount - 1 + ? ['divider'] + : count - boundaryCount > boundaryCount + ? [count - boundaryCount] + : []), + + ...endPages, + ...(hideNextButton ? [] : ['next']), + ...(showLastButton ? ['last'] : []), + ]; + + return items; +}; + export const RenderPaginationItem = component$( ({ 'aria-label': ariaLabel, @@ -99,8 +159,9 @@ export const Pagination = component$( onPaging$, page, pages, + ...rest }: IPaginationProps) => { - const pagi = getPaginationItems(pages, page); + const pagi = getPaginationItems(page, pages, rest); const _onPaging$ = $((page: number) => { if (page < 1 || page > pages) return; @@ -109,48 +170,89 @@ export const Pagination = component$( return ( <> - _onPaging$(page - 1)} - disabled={!pagi.prev} - aria-label="Previous page" - value={'prev'} - /> - {pagi.before !== -1 && ( - <> - _onPaging$(pagi.before)} - aria-label="Page 1" - value={pagi.before} - /> - - - )} - {pagi.items.map((item) => ( - _onPaging$(item)} - aria-label={`Page ${item}`} - aria-current={item === page} - value={item} - /> - ))} - {pagi.after !== -1 && ( - <> - - _onPaging$(pagi.after)} - value={pagi.after} - /> - - )} - _onPaging$(page + 1)} - disabled={!pagi.next} - value={'next'} - /> + {pagi.map((item, i) => { + return ( + <> + {item === 'divider' ? ( + + ) : ( + + _onPaging$( + (() => { + switch (item) { + case 'first': + return 1; + case 'prev': + return page - 1; + case 'next': + return page + 1; + case 'last': + return pages; + default: + if (typeof item === 'number') return item; + return page; + } + })() + ) + } + disabled={ + (['prev', 'first'].includes(item.toString()) && + page === 1) || + (['next', 'last'].includes(item.toString()) && + page === pages) + } + aria-label={`Page ${item}`} + aria-current={item === page} + value={item} + /> + )} + + ); + })} ); + // return ( + // <> + // _onPaging$(page - 1)} + // disabled={!pagi.prev} + // aria-label="Previous page" + // value={'prev'} + // /> + // {typeof pagi.startBound === 'number' ? ( + // _onPaging$(pagi.startBound as number)} + // aria-label="Page 1" + // value={pagi.startBound} + // /> + // ) : null} + // {pagi.startSpacer && } + // {pagi.items.map((item) => ( + // _onPaging$(item)} + // aria-label={`Page ${item}`} + // aria-current={item === page} + // value={item} + // /> + // ))} + // {pagi.endSpacer && } + // {typeof pagi.endBound === 'number' ? ( + // _onPaging$(pagi.endBound as number)} + // value={pagi.endBound} + // /> + // ) : null} + // _onPaging$(page + 1)} + // disabled={!pagi.next} + // value={'next'} + // /> + // + // ); } ); From 9044331811f416ed2c0ab927a2f4ea3c2673c535 Mon Sep 17 00:00:00 2001 From: Yishay Hazan Date: Sat, 25 Feb 2023 09:45:29 +0200 Subject: [PATCH 2/3] fix(pagination): removed comment code --- packages/headless/src/components/pagination/pagination.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/headless/src/components/pagination/pagination.tsx b/packages/headless/src/components/pagination/pagination.tsx index 89b0c7ed1..0e9a2e71a 100644 --- a/packages/headless/src/components/pagination/pagination.tsx +++ b/packages/headless/src/components/pagination/pagination.tsx @@ -213,7 +213,6 @@ export const Pagination = component$( })} ); - // return ( // <> // _onPaging$(page - 1)} From 3e3bc816ba06aa1b751fcb4dd480b760851c827e Mon Sep 17 00:00:00 2001 From: Giorgio Boa <35845425+gioboa@users.noreply.github.com> Date: Sat, 25 Feb 2023 09:15:52 +0100 Subject: [PATCH 3/3] remove commented code --- .../src/components/pagination/pagination.tsx | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/packages/headless/src/components/pagination/pagination.tsx b/packages/headless/src/components/pagination/pagination.tsx index 0e9a2e71a..b5329f3f2 100644 --- a/packages/headless/src/components/pagination/pagination.tsx +++ b/packages/headless/src/components/pagination/pagination.tsx @@ -213,45 +213,5 @@ export const Pagination = component$( })} ); - // <> - // _onPaging$(page - 1)} - // disabled={!pagi.prev} - // aria-label="Previous page" - // value={'prev'} - // /> - // {typeof pagi.startBound === 'number' ? ( - // _onPaging$(pagi.startBound as number)} - // aria-label="Page 1" - // value={pagi.startBound} - // /> - // ) : null} - // {pagi.startSpacer && } - // {pagi.items.map((item) => ( - // _onPaging$(item)} - // aria-label={`Page ${item}`} - // aria-current={item === page} - // value={item} - // /> - // ))} - // {pagi.endSpacer && } - // {typeof pagi.endBound === 'number' ? ( - // _onPaging$(pagi.endBound as number)} - // value={pagi.endBound} - // /> - // ) : null} - // _onPaging$(page + 1)} - // disabled={!pagi.next} - // value={'next'} - // /> - // - // ); } );