Skip to content

Commit

Permalink
fix(Pagination): work on responsive
Browse files Browse the repository at this point in the history
  • Loading branch information
eirikbacker committed Sep 19, 2024
1 parent 48fb347 commit 8f2ed28
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 36 deletions.
21 changes: 14 additions & 7 deletions packages/react/src/components/Pagination/Pagination.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ export default {
// itemLabel: (num) => `Side ${num}`,
// };

export const Preview: StoryFn<typeof Pagination> = ({ totalPages = 10 }) => {
const { pages, goNext, goPrevious, hasNext, hasPrevious } = usePagination({
currentPage: 4,
totalPages,
});
export const Preview: StoryFn<typeof Pagination> = () => {
const { currentPage, pages, goNext, goPrevious, hasNext, hasPrevious } =
usePagination({
currentPage: 4,
totalPages: 10,
show: 7,
});

return (
<Pagination aria-label='Sidenavigering'>
Expand All @@ -45,8 +47,8 @@ export const Preview: StoryFn<typeof Pagination> = ({ totalPages = 10 }) => {
Forrige
</Pagination.Button>
</Pagination.Item>
{pages.map(({ page, ...rest }, index) => (
<Pagination.Item key={index}>
{pages.map(({ key, page, ...rest }) => (
<Pagination.Item key={key}>
<Pagination.Button {...rest}>{page}</Pagination.Button>
</Pagination.Item>
))}
Expand All @@ -60,6 +62,11 @@ export const Preview: StoryFn<typeof Pagination> = ({ totalPages = 10 }) => {
);
};

Preview.args = {
currentPage: 4,
totalPages: 10,
};

// export const WithAnchor: StoryFn<typeof Pagination> = (args) => {
// const { totalPages = 4 } = args;
// const { pages, currentPage } = usePagination({
Expand Down
28 changes: 6 additions & 22 deletions packages/react/src/components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import { useMergeRefs } from '@floating-ui/react';
import { Slot } from '@radix-ui/react-slot';
import cl from 'clsx/lite';
import { type HTMLAttributes, createContext, forwardRef, useRef } from 'react';
import { createContext, forwardRef, useState } from 'react';
import type { Dispatch, HTMLAttributes, SetStateAction } from 'react';

type PaginationContextProps = {
size: NonNullable<PaginationProps['size']>;
compact: boolean;
};

export const PaginationContext = createContext<PaginationContextProps>({
size: 'md',
compact: false,
export const PaginationContext = createContext({
size: 'md' as NonNullable<PaginationProps['size']>,
});

export type PaginationProps = {
Expand All @@ -23,11 +17,6 @@ export type PaginationProps = {
* @default md
*/
size?: 'sm' | 'md' | 'lg';
/**
* Sets how compact the component will be. If true, only 5 steps will show.
* @default false
*/
compact?: boolean;
/** Sets the current page
* @default 1
*/
Expand All @@ -49,24 +38,19 @@ export const Pagination = forwardRef<HTMLElement, PaginationProps>(
'aria-label': ariaLabel = 'Sidenavigering',
className,
asChild,
compact = false,
size = 'md',
...rest
},
ref,
) {
const Component = asChild ? Slot : 'nav';
const paginationRef = useRef<HTMLElement>(null);
const mergedRef = useMergeRefs([paginationRef, ref]);

// TODO: ResizeObserver

return (
<PaginationContext.Provider value={{ size, compact }}>
<PaginationContext.Provider value={{ size }}>
<Component
aria-label={ariaLabel}
className={cl('ds-pagination', className)}
ref={mergedRef}
ref={ref}
{...rest}
/>
</PaginationContext.Provider>
Expand Down
89 changes: 87 additions & 2 deletions packages/react/src/components/Pagination/PaginationList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { useMergeRefs } from '@floating-ui/react';
import { Slot } from '@radix-ui/react-slot';
import { type HTMLAttributes, forwardRef } from 'react';
import { forwardRef, useContext, useEffect, useRef } from 'react';
import type { HTMLAttributes } from 'react';
import { PaginationContext } from './Pagination';

export type PaginationListProps = {
/**
Expand All @@ -9,10 +12,92 @@ export type PaginationListProps = {
asChild?: boolean;
} & Omit<HTMLAttributes<HTMLUListElement>, 'size'>;

// TODO: Browsertest
export const PaginationList = forwardRef<HTMLUListElement, PaginationListProps>(
function PaginationList({ asChild, ...rest }, ref) {
const Component = asChild ? Slot : 'ul';
const listRef = useRef<HTMLUListElement>(null);
const mergedRef = useMergeRefs([listRef, ref]);

return <Component ref={ref} {...rest} />;
useEffect(() => {
const state = getState({}, listRef.current);
const handleResize = () => {
const { current, pages, prev, next, total } = state;
let visible = total + 1;

for (const page of pages) page.hidden = false; // Show all items
while (visible && --visible) {
const isWrapping = (prev?.offsetTop || 0) < (next?.offsetTop || 0);
if (isWrapping) pages[visible - 1].hidden = true;
else break; // Break when not wrapping
}

const offset = (visible - 1) / 2;
const start = Math.min(
Math.max(current - Math.floor(offset), 1),
total - visible + 1,
);
const end = Math.min(
Math.max(current + Math.ceil(offset), visible),
total,
);
const startEllipsis = visible > 4 && start > 1;
const endEllipsis = visible > 3 && end < total;
console.log(current, start, end, visible);

pages.forEach((page, index) => {
page.hidden = index + 1 < start || index + 1 > end;

// page.removeAttribute('aria-hidden');
// if (startEllipsis && index === 0) page.hidden = false;
// else if (startEllipsis && index === 1) {
// page.hidden = false;
// page.setAttribute('aria-hidden', 'true');
// } else {
// page.hidden = index + 1 < start || index + 1 > end;
// }
// console.log(now, start, index, end);
});
};

const resizeObserver = new ResizeObserver(handleResize);
const mutationObserver = new MutationObserver(() => {
getState(state, listRef.current);
handleResize();
});

handleResize();
if (state.list) {
window.addEventListener('resize', handleResize);
resizeObserver.observe(state.list);
mutationObserver.observe(state.list, {
attributeFilter: ['aria-current'],
attributes: true,
childList: true,
subtree: true,
});
}
return () => {
window.removeEventListener('resize', handleResize);
resizeObserver.disconnect();
mutationObserver.disconnect();
};
}, []);

return <Component ref={mergedRef} {...rest} />;
},
);

const getState = (state = {}, list?: HTMLElement | null) => {
const pages = Array.from(list?.children || []).slice(1, -1) as HTMLElement[];
const current = list?.querySelector('[aria-current="page"]');

return Object.assign(state, {
current: pages.findIndex((page) => page.contains(current as Node)) + 1,
pages,
list,
next: list?.lastElementChild as HTMLElement | null,
prev: list?.firstElementChild as HTMLElement | null,
total: pages.length,
});
};
14 changes: 9 additions & 5 deletions packages/react/src/components/Pagination/usePagination.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import type { MouseEvent } from 'react';

const getSteps = (now: number, max: number, show: number) => {
const offset = (show - 1) / 2;
Expand All @@ -8,7 +9,9 @@ const getSteps = (now: number, max: number, show: number) => {

if (show > 4 && start > 1) pages.splice(0, 2, 1, 0);
if (show > 3 && end < max) pages.splice(-2, 2, 0, max);
return pages;

return Array.from({ length: max }, (_, i) => i + 1);
// return pages;
};

export type UsePaginationProps = {
Expand All @@ -25,12 +28,13 @@ export const usePagination = ({
}: UsePaginationProps) => {
const [currentPage, setCurrentPage] = useState(currentProp);
const pages = useMemo(() => {
return getSteps(currentPage, totalPages, show).map((page) => ({
return getSteps(currentPage, totalPages, show).map((page, index) => ({
'aria-current': page === currentPage ? ('page' as const) : undefined,
'aria-hidden': !page || undefined,
'aria-hidden': !page || undefined, // Hide ellipsis from screen reader
onClick: page ? () => setCurrentPage(page) : undefined,
tabIndex: page ? undefined : -1,
page: page || '', // Replace 0 with '' to prevent React from rendering "0" as a child
key: page ? `page-${page}` : `ellipsis-${index}`, // React key utility
page: page || '',
tabIndex: page ? undefined : -1, // Hide ellipsis keyboard
}));
}, [currentPage, totalPages, show]);

Expand Down

0 comments on commit 8f2ed28

Please sign in to comment.