{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"pagination","title":"Pagination","type":"registry:ui","description":"Pagination with page navigation, next and previous links.","registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json","https://fysk.dev/r/button.json"],"files":[{"path":"fysk/pagination.tsx","type":"registry:ui","content":"\"use client\"\r\n\r\nimport * as React from \"react\"\r\nimport { cn } from \"@/lib/utils\"\r\nimport { buttonVariants } from \"@/components/fysk/button\"\r\nimport { useFyskConfig } from \"@/components/fysk-provider\"\r\n\r\n// --- Context ---\r\ntype PaginationContextValue = {\r\n    variant?: \"default\" | \"glass\" | \"minimal\"\r\n    shape?: \"circle\" | \"square\" | \"rounded\"\r\n    size?: \"sm\" | \"md\" | \"lg\"\r\n}\r\n\r\nconst PaginationContext = React.createContext<PaginationContextValue>({\r\n    variant: \"default\",\r\n    shape: \"circle\",\r\n    size: \"md\",\r\n})\r\n\r\nconst usePaginationContext = () => React.useContext(PaginationContext)\r\n\r\n// --- Components ---\r\n\r\nconst Pagination = ({\r\n    className,\r\n    variant = \"default\",\r\n    shape = \"circle\",\r\n    size = \"md\",\r\n    ...props\r\n}: React.ComponentProps<\"nav\"> & PaginationContextValue) => (\r\n    <PaginationContext.Provider value={{ variant, shape, size }}>\r\n        <nav\r\n            role=\"navigation\"\r\n            aria-label=\"pagination\"\r\n            className={cn(\"mx-auto flex w-full justify-center\", className)}\r\n            {...props}\r\n        />\r\n    </PaginationContext.Provider>\r\n)\r\nPagination.displayName = \"Pagination\"\r\n\r\nconst PaginationContent = React.forwardRef<\r\n    HTMLUListElement,\r\n    React.ComponentProps<\"ul\">\r\n>(({ className, ...props }, ref) => {\r\n    const { variant } = usePaginationContext()\r\n    return (\r\n        <ul\r\n            ref={ref}\r\n            className={cn(\r\n                \"flex flex-row items-center gap-1\",\r\n                variant === \"glass\" &&\r\n                \"rounded-full border border-border/50 bg-background/25 p-1 backdrop-blur-md\",\r\n                variant === \"minimal\" && \"gap-2\",\r\n                className\r\n            )}\r\n            {...props}\r\n        />\r\n    )\r\n})\r\nPaginationContent.displayName = \"PaginationContent\"\r\n\r\nconst PaginationItem = React.forwardRef<\r\n    HTMLLIElement,\r\n    React.ComponentProps<\"li\">\r\n>(({ className, ...props }, ref) => (\r\n    <li ref={ref} className={cn(\"\", className)} {...props} />\r\n))\r\nPaginationItem.displayName = \"PaginationItem\"\r\n\r\ntype PaginationLinkProps = {\r\n    isActive?: boolean\r\n    fixedSize?: boolean\r\n} & React.ComponentProps<\"a\">\r\n\r\nconst PaginationLink = ({\r\n    className,\r\n    isActive,\r\n    size: sizeProp,\r\n    fixedSize = true, // Default to true (standard for numbers/icons)\r\n    ...props\r\n}: PaginationLinkProps & { size?: \"sm\" | \"md\" | \"lg\" | \"icon\" }) => {\r\n    const { variant, shape, size: ctxSize } = usePaginationContext()\r\n    const size = sizeProp || ctxSize\r\n\r\n    return (\r\n        <a\r\n            aria-current={isActive ? \"page\" : undefined}\r\n            className={cn(\r\n                buttonVariants({\r\n                    variant: isActive\r\n                        ? variant === \"glass\"\r\n                            ? \"default\"\r\n                            : \"outline\"\r\n                        : \"ghost\",\r\n                    size: size === \"icon\" ? \"icon\" : size,\r\n                }),\r\n                // Shape overrides\r\n                shape === \"circle\" && \"rounded-full\",\r\n                shape === \"square\" && \"rounded-none\",\r\n                shape === \"rounded\" && \"rounded-md\",\r\n                // Variant overrides\r\n                variant === \"glass\" &&\r\n                isActive &&\r\n                \"bg-glass-bg/20 text-foreground backdrop-blur-sm hover:bg-glass-bg/30 border-transparent\",\r\n                variant === \"glass\" &&\r\n                !isActive &&\r\n                \"text-foreground/80 hover:bg-glass-bg/10 hover:text-foreground\",\r\n                variant === \"minimal\" &&\r\n                \"border-0 bg-transparent hover:bg-secondary\",\r\n                variant === \"minimal\" &&\r\n                isActive &&\r\n                \"font-bold text-primary bg-primary/10 hover:bg-primary/20\",\r\n                // Size overrides only if fixedSize is true\r\n                fixedSize && size === \"icon\" && \"h-9 w-9\",\r\n                fixedSize && size === \"sm\" && \"h-8 w-8 p-0\",\r\n                fixedSize && size === \"md\" && \"h-9 w-9 p-0\",\r\n                fixedSize && size === \"lg\" && \"h-10 w-10 p-0\",\r\n                className\r\n            )}\r\n            {...props}\r\n        />\r\n    )\r\n}\r\nPaginationLink.displayName = \"PaginationLink\"\r\n\r\nconst PaginationPrevious = ({\r\n    className,\r\n    ...props\r\n}: React.ComponentProps<typeof PaginationLink>) => {\r\n    const config = useFyskConfig()\r\n    return (\r\n        <PaginationLink\r\n            aria-label=\"Go to previous page\"\r\n            size=\"md\"\r\n            fixedSize={false}\r\n            className={cn(\"gap-1 pl-2.5\", className)}\r\n            {...props}\r\n        >\r\n            <span className=\"h-4 w-4 flex items-center justify-center [&_svg]:size-full\">\r\n                {config.icons?.chevronLeft}\r\n            </span>\r\n            <span className=\"hidden sm:block\">Previous</span>\r\n        </PaginationLink>\r\n    )\r\n}\r\nPaginationPrevious.displayName = \"PaginationPrevious\"\r\n\r\nconst PaginationNext = ({\r\n    className,\r\n    ...props\r\n}: React.ComponentProps<typeof PaginationLink>) => {\r\n    const config = useFyskConfig()\r\n    return (\r\n        <PaginationLink\r\n            aria-label=\"Go to next page\"\r\n            size=\"md\"\r\n            fixedSize={false}\r\n            className={cn(\"gap-1 pr-2.5\", className)}\r\n            {...props}\r\n        >\r\n            <span className=\"hidden sm:block\">Next</span>\r\n            <span className=\"h-4 w-4 flex items-center justify-center [&_svg]:size-full\">\r\n                {config.icons?.chevronRight}\r\n            </span>\r\n        </PaginationLink>\r\n    )\r\n}\r\nPaginationNext.displayName = \"PaginationNext\"\r\n\r\nconst PaginationFirst = ({\r\n    className,\r\n    ...props\r\n}: React.ComponentProps<typeof PaginationLink>) => {\r\n    const config = useFyskConfig()\r\n    return (\r\n        <PaginationLink\r\n            aria-label=\"Go to first page\"\r\n            size=\"icon\"\r\n            className={cn(\"\", className)}\r\n            {...props}\r\n        >\r\n            <span className=\"h-4 w-4 flex items-center justify-center [&_svg]:size-full\">\r\n                {config.icons?.chevronsLeft}\r\n            </span>\r\n        </PaginationLink>\r\n    )\r\n}\r\nPaginationFirst.displayName = \"PaginationFirst\"\r\n\r\nconst PaginationLast = ({\r\n    className,\r\n    ...props\r\n}: React.ComponentProps<typeof PaginationLink>) => {\r\n    const config = useFyskConfig()\r\n    return (\r\n        <PaginationLink\r\n            aria-label=\"Go to last page\"\r\n            size=\"icon\"\r\n            className={cn(\"\", className)}\r\n            {...props}\r\n        >\r\n            <span className=\"h-4 w-4 flex items-center justify-center [&_svg]:size-full\">\r\n                {config.icons?.chevronsRight}\r\n            </span>\r\n        </PaginationLink>\r\n    )\r\n}\r\nPaginationLast.displayName = \"PaginationLast\"\r\n\r\nconst PaginationEllipsis = ({\r\n    className,\r\n    ...props\r\n}: React.ComponentProps<\"span\">) => {\r\n    const config = useFyskConfig()\r\n    const { size } = usePaginationContext()\r\n    return (\r\n        <span\r\n            aria-hidden\r\n            className={cn(\r\n                \"flex items-center justify-center\",\r\n                size === \"sm\" && \"h-8 w-8\",\r\n                size === \"md\" && \"h-9 w-9\",\r\n                size === \"lg\" && \"h-10 w-10\",\r\n                className\r\n            )}\r\n            {...props}\r\n        >\r\n            <span className=\"h-4 w-4 flex items-center justify-center [&_svg]:size-full\">\r\n                {config.icons?.moreHorizontal}\r\n            </span>\r\n            <span className=\"sr-only\">More pages</span>\r\n        </span>\r\n    )\r\n}\r\nPaginationEllipsis.displayName = \"PaginationEllipsis\"\r\n\r\n// --- Stateful Pagination ---\r\n\r\ninterface StatefulPaginationProps extends Omit<React.ComponentProps<typeof Pagination>, 'onChange'> {\r\n    total: number\r\n    page: number\r\n    pageSize?: number\r\n    siblingCount?: number\r\n    onChange?: (page: number) => void\r\n    showFirstLast?: boolean\r\n}\r\n\r\nconst StatefulPagination = ({\r\n    total,\r\n    page,\r\n    pageSize = 10,\r\n    siblingCount = 1,\r\n    onChange,\r\n    showFirstLast = false,\r\n    ...props\r\n}: StatefulPaginationProps) => {\r\n    const totalPages = Math.ceil(total / pageSize)\r\n\r\n    // Helper to generate page numbers\r\n    const getPageNumbers = () => {\r\n        const totalNumbers = siblingCount + 5\r\n        const totalBlocks = totalNumbers + 2\r\n\r\n        if (totalPages > totalBlocks) {\r\n            const startPage = Math.max(2, page - siblingCount)\r\n            const endPage = Math.min(totalPages - 1, page + siblingCount)\r\n            let pages: (number | string)[] = range(startPage, endPage)\r\n\r\n            const hasLeftSpill = startPage > 2\r\n            const hasRightSpill = totalPages - endPage > 1\r\n            const spillOffset = totalNumbers - (pages.length + 1)\r\n\r\n            if (hasLeftSpill && !hasRightSpill) {\r\n                const extraPages = range(startPage - spillOffset, startPage - 1)\r\n                pages = [\"...\", ...extraPages, ...pages]\r\n            } else if (!hasLeftSpill && hasRightSpill) {\r\n                const extraPages = range(endPage + 1, endPage + spillOffset)\r\n                pages = [...pages, ...extraPages, \"...\"]\r\n            } else if (hasLeftSpill && hasRightSpill) {\r\n                pages = [\"...\", ...pages, \"...\"]\r\n            }\r\n\r\n            return [1, ...pages, totalPages]\r\n        }\r\n\r\n        return range(1, totalPages)\r\n    }\r\n\r\n    const range = (start: number, end: number) => {\r\n        const length = end - start + 1\r\n        return Array.from({ length }, (_, i) => start + i)\r\n    }\r\n\r\n    const pages = getPageNumbers()\r\n\r\n    return (\r\n        <Pagination {...props}>\r\n            <PaginationContent>\r\n                {showFirstLast && (\r\n                    <PaginationItem>\r\n                        <PaginationFirst\r\n                            href=\"#\"\r\n                            onClick={(e) => {\r\n                                e.preventDefault()\r\n                                onChange?.(1)\r\n                            }}\r\n                            aria-disabled={page === 1}\r\n                            className={page === 1 ? \"pointer-events-none opacity-50\" : undefined}\r\n                        />\r\n                    </PaginationItem>\r\n                )}\r\n\r\n                <PaginationItem>\r\n                    <PaginationPrevious\r\n                        href=\"#\"\r\n                        onClick={(e) => {\r\n                            e.preventDefault()\r\n                            if (page > 1) onChange?.(page - 1)\r\n                        }}\r\n                        aria-disabled={page === 1}\r\n                        className={page === 1 ? \"pointer-events-none opacity-50\" : undefined}\r\n                    />\r\n                </PaginationItem>\r\n\r\n                {pages.map((p, i) => (\r\n                    <PaginationItem key={i}>\r\n                        {p === \"...\" ? (\r\n                            <PaginationEllipsis />\r\n                        ) : (\r\n                            <PaginationLink\r\n                                href=\"#\"\r\n                                isActive={page === p}\r\n                                onClick={(e) => {\r\n                                    e.preventDefault()\r\n                                    if (typeof p === \"number\") onChange?.(p)\r\n                                }}\r\n                            >\r\n                                {p}\r\n                            </PaginationLink>\r\n                        )}\r\n                    </PaginationItem>\r\n                ))}\r\n\r\n                <PaginationItem>\r\n                    <PaginationNext\r\n                        href=\"#\"\r\n                        onClick={(e) => {\r\n                            e.preventDefault()\r\n                            if (page < totalPages) onChange?.(page + 1)\r\n                        }}\r\n                        aria-disabled={page === totalPages}\r\n                        className={page === totalPages ? \"pointer-events-none opacity-50\" : undefined}\r\n                    />\r\n                </PaginationItem>\r\n\r\n                {showFirstLast && (\r\n                    <PaginationItem>\r\n                        <PaginationLast\r\n                            href=\"#\"\r\n                            onClick={(e) => {\r\n                                e.preventDefault()\r\n                                onChange?.(totalPages)\r\n                            }}\r\n                            aria-disabled={page === totalPages}\r\n                            className={page === totalPages ? \"pointer-events-none opacity-50\" : undefined}\r\n                        />\r\n                    </PaginationItem>\r\n                )}\r\n            </PaginationContent>\r\n        </Pagination>\r\n    )\r\n}\r\nStatefulPagination.displayName = \"StatefulPagination\"\r\n\r\nexport {\r\n    Pagination,\r\n    PaginationContent,\r\n    PaginationEllipsis,\r\n    PaginationItem,\r\n    PaginationLink,\r\n    PaginationNext,\r\n    PaginationPrevious,\r\n    PaginationFirst,\r\n    PaginationLast,\r\n    StatefulPagination,\r\n}\r\n\r\n"}]}