mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
refactor(pagination): rtl (#4843)
* refactor(pagination): rtl * chore(changeset): add changeset
This commit is contained in:
parent
09241faa4b
commit
4693fb7b4d
6
.changeset/shiny-ties-tan.md
Normal file
6
.changeset/shiny-ties-tan.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"@heroui/pagination": patch
|
||||
"@heroui/use-pagination": patch
|
||||
---
|
||||
|
||||
fixed pagination in RTL (#2858)
|
||||
@ -40,6 +40,68 @@ const Pagination = forwardRef<"nav", PaginationProps>((props, ref) => {
|
||||
|
||||
const isRTL = direction === "rtl";
|
||||
|
||||
const renderChevronIcon = useCallback(
|
||||
(key: PaginationItemType) => {
|
||||
if (
|
||||
(key === PaginationItemType.PREV && !isRTL) ||
|
||||
(key === PaginationItemType.NEXT && isRTL)
|
||||
) {
|
||||
return <ChevronIcon />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ChevronIcon
|
||||
className={slots.chevronNext({
|
||||
class: classNames?.chevronNext,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[slots, isRTL],
|
||||
);
|
||||
|
||||
const renderPrevItem = useCallback(
|
||||
(value: PaginationItemValue) => {
|
||||
return (
|
||||
<PaginationItem
|
||||
key={PaginationItemType.PREV}
|
||||
className={slots.prev({
|
||||
class: classNames?.prev,
|
||||
})}
|
||||
data-slot="prev"
|
||||
getAriaLabel={getItemAriaLabel}
|
||||
isDisabled={!loop && activePage === 1}
|
||||
value={value}
|
||||
onPress={onPrevious}
|
||||
>
|
||||
{renderChevronIcon(PaginationItemType.PREV)}
|
||||
</PaginationItem>
|
||||
);
|
||||
},
|
||||
[slots, classNames, loop, activePage, isRTL, total, getItemAriaLabel, onPrevious],
|
||||
);
|
||||
|
||||
const renderNextItem = useCallback(
|
||||
(value: PaginationItemValue) => {
|
||||
return (
|
||||
<PaginationItem
|
||||
key={PaginationItemType.NEXT}
|
||||
className={slots.next({
|
||||
class: clsx(classNames?.next),
|
||||
})}
|
||||
data-slot="next"
|
||||
getAriaLabel={getItemAriaLabel}
|
||||
isDisabled={!loop && activePage === total}
|
||||
value={value}
|
||||
onPress={onNext}
|
||||
>
|
||||
{renderChevronIcon(PaginationItemType.NEXT)}
|
||||
</PaginationItem>
|
||||
);
|
||||
},
|
||||
[slots, classNames, loop, activePage, isRTL, total, getItemAriaLabel, onNext],
|
||||
);
|
||||
|
||||
const renderItem = useCallback(
|
||||
(value: PaginationItemValue, index: number) => {
|
||||
const isBefore = index < range.indexOf(activePage);
|
||||
@ -66,14 +128,8 @@ const Pagination = forwardRef<"nav", PaginationProps>((props, ref) => {
|
||||
}
|
||||
|
||||
const itemChildren: Record<PaginationItemType, React.ReactNode> = {
|
||||
[PaginationItemType.PREV]: <ChevronIcon />,
|
||||
[PaginationItemType.NEXT]: (
|
||||
<ChevronIcon
|
||||
className={slots.chevronNext({
|
||||
class: classNames?.chevronNext,
|
||||
})}
|
||||
/>
|
||||
),
|
||||
[PaginationItemType.PREV]: renderChevronIcon(PaginationItemType.PREV),
|
||||
[PaginationItemType.NEXT]: renderChevronIcon(PaginationItemType.NEXT),
|
||||
[PaginationItemType.DOTS]: (
|
||||
<>
|
||||
<EllipsisIcon className={slots?.ellipsis({class: classNames?.ellipsis})} />
|
||||
@ -111,42 +167,10 @@ const Pagination = forwardRef<"nav", PaginationProps>((props, ref) => {
|
||||
}
|
||||
|
||||
if (value === PaginationItemType.PREV) {
|
||||
return (
|
||||
<PaginationItem
|
||||
key={PaginationItemType.PREV}
|
||||
className={slots.prev({
|
||||
class: classNames?.prev,
|
||||
})}
|
||||
data-slot="prev"
|
||||
getAriaLabel={getItemAriaLabel}
|
||||
isDisabled={!loop && activePage === (isRTL ? total : 1)}
|
||||
value={value}
|
||||
onPress={onPrevious}
|
||||
>
|
||||
<ChevronIcon />
|
||||
</PaginationItem>
|
||||
);
|
||||
return renderPrevItem(value);
|
||||
}
|
||||
if (value === PaginationItemType.NEXT) {
|
||||
return (
|
||||
<PaginationItem
|
||||
key={PaginationItemType.NEXT}
|
||||
className={slots.next({
|
||||
class: clsx(classNames?.next),
|
||||
})}
|
||||
data-slot="next"
|
||||
getAriaLabel={getItemAriaLabel}
|
||||
isDisabled={!loop && activePage === (isRTL ? 1 : total)}
|
||||
value={value}
|
||||
onPress={onNext}
|
||||
>
|
||||
<ChevronIcon
|
||||
className={slots.chevronNext({
|
||||
class: classNames?.chevronNext,
|
||||
})}
|
||||
/>
|
||||
</PaginationItem>
|
||||
);
|
||||
return renderNextItem(value);
|
||||
}
|
||||
|
||||
if (value === PaginationItemType.DOTS) {
|
||||
@ -191,6 +215,12 @@ const Pagination = forwardRef<"nav", PaginationProps>((props, ref) => {
|
||||
slots,
|
||||
classNames,
|
||||
total,
|
||||
getItemAriaLabel,
|
||||
onNext,
|
||||
onPrevious,
|
||||
setPage,
|
||||
renderPrevItem,
|
||||
renderNextItem,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@ -3,7 +3,6 @@ import type {Key, ReactNode, Ref} from "react";
|
||||
import type {HTMLHeroUIProps, PropGetter} from "@heroui/system";
|
||||
|
||||
import {objectToDeps, Timer} from "@heroui/shared-utils";
|
||||
import {useLocale} from "@react-aria/i18n";
|
||||
import {
|
||||
UsePaginationProps as UseBasePaginationProps,
|
||||
PaginationItemValue,
|
||||
@ -195,10 +194,6 @@ export function usePagination(originalProps: UsePaginationProps) {
|
||||
|
||||
const cursorTimer = useRef<Timer>();
|
||||
|
||||
const {direction} = useLocale();
|
||||
|
||||
const isRTL = direction === "rtl";
|
||||
|
||||
const disableAnimation =
|
||||
originalProps?.disableAnimation ?? globalContext?.disableAnimation ?? false;
|
||||
const disableCursorAnimation = originalProps?.disableCursorAnimation ?? disableAnimation ?? false;
|
||||
@ -321,7 +316,7 @@ export function usePagination(originalProps: UsePaginationProps) {
|
||||
const baseStyles = clsx(classNames?.base, className);
|
||||
|
||||
const onNext = () => {
|
||||
if (loop && activePage === (isRTL ? 1 : total)) {
|
||||
if (loop && activePage === total) {
|
||||
return first();
|
||||
}
|
||||
|
||||
@ -329,7 +324,7 @@ export function usePagination(originalProps: UsePaginationProps) {
|
||||
};
|
||||
|
||||
const onPrevious = () => {
|
||||
if (loop && activePage === (isRTL ? total : 1)) {
|
||||
if (loop && activePage === 1) {
|
||||
return last();
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import {Meta} from "@storybook/react";
|
||||
import {button, pagination} from "@heroui/theme";
|
||||
import {cn} from "@heroui/theme";
|
||||
import {ChevronIcon} from "@heroui/shared-icons";
|
||||
import {useLocale} from "@react-aria/i18n";
|
||||
|
||||
import {Pagination, PaginationItemRenderProps, PaginationItemType, usePagination} from "../src";
|
||||
|
||||
@ -138,6 +139,10 @@ export const Controlled = () => {
|
||||
};
|
||||
|
||||
export const CustomItems = () => {
|
||||
const {direction} = useLocale();
|
||||
|
||||
const isRTL = direction === "rtl";
|
||||
|
||||
const renderItem = ({
|
||||
ref,
|
||||
value,
|
||||
@ -150,7 +155,7 @@ export const CustomItems = () => {
|
||||
if (value === PaginationItemType.NEXT) {
|
||||
return (
|
||||
<button className={cn(className, "bg-default-200")} onClick={onNext}>
|
||||
<ChevronIcon className="rotate-180" />
|
||||
<ChevronIcon className={cn({"rotate-180": !isRTL})} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@ -158,7 +163,7 @@ export const CustomItems = () => {
|
||||
if (value === PaginationItemType.PREV) {
|
||||
return (
|
||||
<button className={cn(className, "bg-default-200")} onClick={onPrevious}>
|
||||
<ChevronIcon />
|
||||
<ChevronIcon className={cn({"rotate-180": isRTL})} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@ -217,6 +222,10 @@ export const CustomWithHooks = () => {
|
||||
boundaries: 10,
|
||||
});
|
||||
|
||||
const {direction} = useLocale();
|
||||
|
||||
const isRTL = direction === "rtl";
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>Active page: {activePage}</p>
|
||||
@ -226,7 +235,7 @@ export const CustomWithHooks = () => {
|
||||
return (
|
||||
<li key={page} aria-label="next page" className="w-4 h-4">
|
||||
<button className="w-full h-full bg-default-200 rounded-full" onClick={onNext}>
|
||||
<ChevronIcon className="rotate-180" />
|
||||
<ChevronIcon className={cn({"rotate-180": !isRTL})} />
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
@ -236,7 +245,7 @@ export const CustomWithHooks = () => {
|
||||
return (
|
||||
<li key={page} aria-label="previous page" className="w-4 h-4">
|
||||
<button className="w-full h-full bg-default-200 rounded-full" onClick={onPrevious}>
|
||||
<ChevronIcon />
|
||||
<ChevronIcon className={cn({"rotate-180": isRTL})} />
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
|
||||
@ -85,17 +85,15 @@ export function usePagination(props: UsePaginationProps) {
|
||||
[total, activePage, onChangeActivePage],
|
||||
);
|
||||
|
||||
const next = () => (isRTL ? setPage(activePage - 1) : setPage(activePage + 1));
|
||||
const previous = () => (isRTL ? setPage(activePage + 1) : setPage(activePage - 1));
|
||||
const first = () => (isRTL ? setPage(total) : setPage(1));
|
||||
const last = () => (isRTL ? setPage(1) : setPage(total));
|
||||
const next = () => setPage(activePage + 1);
|
||||
const previous = () => setPage(activePage - 1);
|
||||
const first = () => setPage(1);
|
||||
const last = () => setPage(total);
|
||||
|
||||
const formatRange = useCallback(
|
||||
(range: PaginationItemValue[]) => {
|
||||
if (showControls) {
|
||||
return isRTL
|
||||
? [PaginationItemType.NEXT, ...range, PaginationItemType.PREV]
|
||||
: [PaginationItemType.PREV, ...range, PaginationItemType.NEXT];
|
||||
return [PaginationItemType.PREV, ...range, PaginationItemType.NEXT];
|
||||
}
|
||||
|
||||
return range;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user