25 lines
9.7 KiB
JSON
25 lines
9.7 KiB
JSON
{
|
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
"name": "carousel",
|
|
"type": "registry:component",
|
|
"title": "Carousel",
|
|
"description": "A customizable carousel component to show animated cards",
|
|
"dependencies": [
|
|
"embla-carousel-react",
|
|
"lucide-react"
|
|
],
|
|
"files": [
|
|
{
|
|
"path": "components/retroui/Carousel.tsx",
|
|
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport useEmblaCarousel, {\n type UseEmblaCarouselType,\n} from \"embla-carousel-react\"\nimport { ArrowLeft, ArrowRight } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/retroui/Button\"\n\ntype CarouselApi = UseEmblaCarouselType[1]\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>\ntype CarouselOptions = UseCarouselParameters[0]\ntype CarouselPlugin = UseCarouselParameters[1]\n\ntype CarouselProps = {\n opts?: CarouselOptions\n plugins?: CarouselPlugin\n orientation?: \"horizontal\" | \"vertical\"\n setApi?: (api: CarouselApi) => void\n}\n\ntype CarouselContextProps = {\n carouselRef: ReturnType<typeof useEmblaCarousel>[0]\n api: ReturnType<typeof useEmblaCarousel>[1]\n scrollPrev: () => void\n scrollNext: () => void\n canScrollPrev: boolean\n canScrollNext: boolean\n} & CarouselProps\n\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null)\n\nfunction useCarousel() {\n const context = React.useContext(CarouselContext)\n\n if (!context) {\n throw new Error(\"useCarousel must be used within a <Carousel />\")\n }\n\n return context\n}\n\nfunction Carousel({\n orientation = \"horizontal\",\n opts,\n setApi,\n plugins,\n className,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & CarouselProps) {\n const [carouselRef, api] = useEmblaCarousel(\n {\n ...opts,\n axis: orientation === \"horizontal\" ? \"x\" : \"y\",\n },\n plugins\n )\n const [canScrollPrev, setCanScrollPrev] = React.useState(false)\n const [canScrollNext, setCanScrollNext] = React.useState(false)\n\n const onSelect = React.useCallback((api: CarouselApi) => {\n if (!api) return\n setCanScrollPrev(api.canScrollPrev())\n setCanScrollNext(api.canScrollNext())\n }, [])\n\n const scrollPrev = React.useCallback(() => {\n api?.scrollPrev()\n }, [api])\n\n const scrollNext = React.useCallback(() => {\n api?.scrollNext()\n }, [api])\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault()\n scrollPrev()\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault()\n scrollNext()\n }\n },\n [scrollPrev, scrollNext]\n )\n\n React.useEffect(() => {\n if (!api || !setApi) return\n setApi(api)\n }, [api, setApi])\n\n React.useEffect(() => {\n if (!api) return\n onSelect(api)\n api.on(\"reInit\", onSelect)\n api.on(\"select\", onSelect)\n\n return () => {\n api?.off(\"select\", onSelect)\n }\n }, [api, onSelect])\n\n return (\n <CarouselContext.Provider\n value={{\n carouselRef,\n api: api,\n opts,\n orientation:\n orientation || (opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext,\n }}\n >\n <div\n onKeyDownCapture={handleKeyDown}\n className={cn(\"relative\", className)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n data-slot=\"carousel\"\n {...props}\n >\n {children}\n </div>\n </CarouselContext.Provider>\n )\n}\n\nfunction CarouselContent({ className, ...props }: React.ComponentProps<\"div\">) {\n const { carouselRef, orientation } = useCarousel()\n\n return (\n <div\n ref={carouselRef}\n className=\"overflow-hidden\"\n data-slot=\"carousel-content\"\n >\n <div\n className={cn(\n \"flex\",\n orientation === \"horizontal\" ? \"-ml-4\" : \"-mt-4 flex-col\",\n className\n )}\n {...props}\n />\n </div>\n )\n}\n\nfunction CarouselItem({ className, ...props }: React.ComponentProps<\"div\">) {\n const { orientation } = useCarousel()\n\n return (\n <div\n role=\"group\"\n aria-roledescription=\"slide\"\n data-slot=\"carousel-item\"\n className={cn(\n \"min-w-0 shrink-0 grow-0 basis-full\",\n orientation === \"horizontal\" ? \"pl-4\" : \"pt-4\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CarouselPrevious({\n className,\n variant = \"outline\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollPrev, canScrollPrev } = useCarousel()\n\n return (\n <Button\n data-slot=\"carousel-previous\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded\",\n orientation === \"horizontal\"\n ? \"top-1/2 -left-12 -translate-y-1/2 hover:-translate-y-[calc(50%-2px)] active:-translate-y-[calc(50%-4px)]\"\n : \"-top-12 left-1/2 -translate-x-1/2 rotate-90 hover:-translate-x-[calc(50%-2px)] active:-translate-x-[calc(50%-4px)]\",\n className\n )}\n disabled={!canScrollPrev}\n onClick={scrollPrev}\n {...props}\n >\n <ArrowLeft />\n <span className=\"sr-only\">Previous slide</span>\n </Button>\n )\n}\n\nfunction CarouselNext({\n className,\n variant = \"outline\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollNext, canScrollNext } = useCarousel()\n\n return (\n <Button\n data-slot=\"carousel-next\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded\",\n orientation === \"horizontal\"\n ? \"top-1/2 -right-12 -translate-y-1/2 hover:-translate-y-[calc(50%-2px)] active:-translate-y-[calc(50%-4px)]\"\n : \"-bottom-12 left-1/2 -translate-x-1/2 rotate-90 hover:-translate-x-[calc(50%-2px)] active:-translate-x-[calc(50%-4px)]\",\n className\n )}\n disabled={!canScrollNext}\n onClick={scrollNext}\n {...props}\n >\n <ArrowRight />\n <span className=\"sr-only\">Next slide</span>\n </Button>\n )\n}\n\nconst CarouselObject = Object.assign(Carousel, {\n Content: CarouselContent,\n Item: CarouselItem,\n Previous: CarouselPrevious,\n Next: CarouselNext,\n})\n\nexport { CarouselObject as Carousel }",
|
|
"type": "registry:component",
|
|
"target": "components/retroui/Carousel.tsx"
|
|
},
|
|
{
|
|
"path": "components/retroui/Button.tsx",
|
|
"content": "import { cn } from \"@/lib/utils\";\nimport { cva, VariantProps } from \"class-variance-authority\";\nimport React, { ButtonHTMLAttributes } from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\n\nexport const buttonVariants = cva(\n \"font-head transition-all rounded outline-hidden cursor-pointer duration-200 font-medium flex items-center\",\n {\n variants: {\n variant: {\n default:\n \"shadow-md hover:shadow active:shadow-none bg-primary text-primary-foreground border-2 border-black transition hover:translate-y-1 active:translate-y-2 active:translate-x-1 hover:bg-primary-hover\",\n secondary:\n \"shadow-md hover:shadow active:shadow-none bg-secondary shadow-primary text-secondary-foreground border-2 border-black transition hover:translate-y-1 active:translate-y-2 active:translate-x-1 hover:bg-secondary-hover\",\n outline:\n \"shadow-md hover:shadow active:shadow-none bg-transparent border-2 transition hover:translate-y-1 active:translate-y-2 active:translate-x-1\",\n link: \"bg-transparent hover:underline\",\n ghost: \"bg-transparent hover:bg-accent\"\n },\n size: {\n sm: \"px-3 py-1 text-sm shadow hover:shadow-none\",\n md: \"px-4 py-1.5 text-base\",\n lg: \"px-6 lg:px-8 py-2 lg:py-3 text-md lg:text-lg\",\n icon: \"p-2\",\n },\n },\n defaultVariants: {\n size: \"md\",\n variant: \"default\",\n },\n },\n);\n\nexport interface IButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nexport const Button = React.forwardRef<HTMLButtonElement, IButtonProps>(\n (\n {\n children,\n size = \"md\",\n className = \"\",\n variant = \"default\",\n asChild = false,\n ...props\n }: IButtonProps,\n forwardedRef,\n ) => {\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n ref={forwardedRef}\n className={cn(buttonVariants({ variant, size }), className)}\n {...props}\n >\n {children}\n </Comp>\n );\n },\n);\n\nButton.displayName = \"Button\";",
|
|
"type": "registry:component",
|
|
"target": "components/retroui/Button.tsx"
|
|
}
|
|
]
|
|
} |