Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions src/components/ui/Pagination.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import type { Meta, StoryObj } from "@storybook/react";
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "./pagination";

const meta = {
title: "UI/Pagination",
component: Pagination,
parameters: { layout: "centered" },
tags: ["autodocs"],
} satisfies Meta<typeof Pagination>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
render: () => (
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="#" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">1</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#" isActive>
2
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">3</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationNext href="#" />
</PaginationItem>
</PaginationContent>
</Pagination>
),
};

export const ManyPages: Story = {
render: () => (
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="#" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">1</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">4</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#" isActive>
5
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">6</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">20</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href="#" />
</PaginationItem>
</PaginationContent>
</Pagination>
),
};

export const FirstPage: Story = {
render: () => (
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationLink href="#" isActive>
1
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">2</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">3</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationNext href="#" />
</PaginationItem>
</PaginationContent>
</Pagination>
),
};

export const LastPage: Story = {
render: () => (
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="#" />
</PaginationItem>
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">8</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">9</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#" isActive>
10
</PaginationLink>
</PaginationItem>
</PaginationContent>
</Pagination>
),
};

export const IconsOnly: Story = {
render: () => (
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious className="[&_span]:hidden" href="#" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">1</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#" isActive>
2
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">3</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext className="[&_span]:hidden" href="#" />
</PaginationItem>
</PaginationContent>
</Pagination>
),
};
12 changes: 12 additions & 0 deletions src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,18 @@ export {
type LayoutContextValue,
} from "../wordpress/layout";

// Pagination
export {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
type PaginationLinkProps,
} from "./pagination";

// Sonner (toast notifications)
export { Toaster } from "./sonner";

Expand Down
128 changes: 128 additions & 0 deletions src/components/ui/pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import * as React from "react";
import {
ChevronLeftIcon,
ChevronRightIcon,
MoreHorizontalIcon,
} from "lucide-react";

import { cn } from "@/lib/utils";
import { buttonVariants, type ButtonProps } from "./button";

function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
return (
<nav
role="navigation"
aria-label="pagination"
data-slot="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
{...props}
/>
);
}

function PaginationContent({
className,
...props
}: React.ComponentProps<"ul">) {
return (
<ul
data-slot="pagination-content"
className={cn("flex flex-row items-center gap-1", className)}
{...props}
/>
);
}

function PaginationItem({ ...props }: React.ComponentProps<"li">) {
return <li data-slot="pagination-item" {...props} />;
}

type PaginationLinkProps = {
isActive?: boolean;
} & Pick<ButtonProps, "size"> &
React.ComponentProps<"a">;

function PaginationLink({
className,
isActive,
size = "icon",
...props
}: PaginationLinkProps) {
return (
<a
aria-current={isActive ? "page" : undefined}
data-slot="pagination-link"
data-active={isActive}
className={cn(
buttonVariants({
variant: isActive ? "outline" : "ghost",
size,
}),
className,
)}
{...props}
/>
);
}

function PaginationPrevious({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) {
return (
<PaginationLink
aria-label="Go to previous page"
size="default"
className={cn("gap-1 px-2.5 sm:pl-2.5", className)}
{...props}
>
<ChevronLeftIcon />
<span className="hidden sm:block">Previous</span>
</PaginationLink>
);
}

function PaginationNext({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) {
return (
<PaginationLink
aria-label="Go to next page"
size="default"
className={cn("gap-1 px-2.5 sm:pr-2.5", className)}
{...props}
>
<span className="hidden sm:block">Next</span>
<ChevronRightIcon />
</PaginationLink>
);
}

function PaginationEllipsis({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
aria-hidden
data-slot="pagination-ellipsis"
className={cn("flex size-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontalIcon className="size-4" />
<span className="sr-only">More pages</span>
</span>
);
}

export {
Pagination,
PaginationContent,
PaginationLink,
PaginationItem,
PaginationPrevious,
PaginationNext,
PaginationEllipsis,
type PaginationLinkProps,
};
2 changes: 1 addition & 1 deletion src/components/ui/smart-multi-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ const SmartMultiSelect = React.forwardRef<
aria-invalid={invalid || undefined}
data-slot="smart-multi-select-trigger"
className={cn(
"cursor-pointer flex h-auto min-h-9 w-[250px] items-center justify-between rounded-md border border-input bg-transparent px-2 py-1 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]",
"cursor-pointer flex h-auto min-h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-2 py-1 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]",
invalid &&
"border-destructive ring-destructive/20 ring-[3px] dark:ring-destructive/40",
disabled && "cursor-not-allowed opacity-50 pointer-events-none",
Expand Down
11 changes: 10 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,14 @@ export {
useComboboxAnchor,
// Rich Text Editor
RichTextEditor,
// Pagination
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
// Sonner (toast notifications)
Toaster,
// Calendar & DatePicker
Expand Down Expand Up @@ -315,7 +323,8 @@ export {
type LayoutMenuItemData,
type LayoutMenuGroupData,
type LayoutMenuProps,
type LayoutMenuSearchProps
type LayoutMenuSearchProps,
type PaginationLinkProps,
} from "./components/ui";

export { MatricsCard, type MatricsCardProps } from './components/matrics-card';
Expand Down
Loading