19 lines
7.0 KiB
JSON
19 lines
7.0 KiB
JSON
{
|
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
"name": "headless-dialog",
|
|
"type": "registry:ui",
|
|
"title": "Headless Dialog",
|
|
"description": "A fully-managed, renderless dialog component jam-packed with accessibility and keyboard features, perfect for building completely custom dialogs and alerts.",
|
|
"dependencies": [
|
|
"@headlessui/react",
|
|
"motion"
|
|
],
|
|
"files": [
|
|
{
|
|
"path": "registry/headless/dialog/index.tsx",
|
|
"content": "'use client';\n\nimport * as React from 'react';\nimport {\n Dialog as DialogPrimitive,\n DialogBackdrop as DialogBackdropPrimitive,\n DialogPanel as DialogPanelPrimitive,\n DialogTitle as DialogTitlePrimitive,\n Description as DialogDescriptionPrimitive,\n type DialogProps as DialogPrimitiveProps,\n type DialogBackdropProps as DialogBackdropPrimitiveProps,\n type DialogPanelProps as DialogPanelPrimitiveProps,\n type DialogTitleProps as DialogTitlePrimitiveProps,\n CloseButton,\n} from '@headlessui/react';\nimport {\n motion,\n AnimatePresence,\n type Transition,\n type HTMLMotionProps,\n} from 'motion/react';\n\nimport { cn } from '@/lib/utils';\nimport { X } from 'lucide-react';\n\ntype DialogProps<TTag extends React.ElementType = 'div'> = Omit<\n DialogPrimitiveProps<TTag>,\n 'static'\n> & {\n className?: string;\n as?: TTag;\n};\n\nfunction Dialog<TTag extends React.ElementType = 'div'>({\n className,\n ...props\n}: DialogProps<TTag>) {\n return (\n <AnimatePresence>\n {props?.open && (\n <DialogPrimitive\n data-slot=\"dialog\"\n className={cn('relative z-50', className)}\n {...props}\n static\n />\n )}\n </AnimatePresence>\n );\n}\n\ntype DialogBackdropProps<TTag extends React.ElementType = typeof motion.div> =\n DialogBackdropPrimitiveProps<TTag> & {\n className?: string;\n as?: TTag;\n };\n\nfunction DialogBackdrop<TTag extends React.ElementType = typeof motion.div>(\n props: DialogBackdropProps<TTag>,\n) {\n const { className, as = motion.div, ...rest } = props;\n\n return (\n <DialogBackdropPrimitive\n key=\"dialog-backdrop\"\n data-slot=\"dialog-backdrop\"\n className={cn('fixed inset-0 z-50 bg-black/80', className)}\n as={as as React.ElementType}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n {...rest}\n />\n );\n}\n\ntype FlipDirection = 'top' | 'bottom' | 'left' | 'right';\n\ntype DialogPanelProps<TTag extends React.ElementType = typeof motion.div> =\n Omit<DialogPanelPrimitiveProps<typeof motion.div>, 'transition'> &\n Omit<HTMLMotionProps<'div'>, 'children'> & {\n from?: FlipDirection;\n transition?: Transition;\n as?: TTag;\n };\n\nfunction DialogPanel<TTag extends React.ElementType = typeof motion.div>(\n props: DialogPanelProps<TTag>,\n) {\n const {\n children,\n className,\n as = motion.div,\n from = 'top',\n transition = { type: 'spring', stiffness: 150, damping: 25 },\n ...rest\n } = props;\n\n const initialRotation =\n from === 'top' || from === 'left' ? '20deg' : '-20deg';\n const isVertical = from === 'top' || from === 'bottom';\n const rotateAxis = isVertical ? 'rotateX' : 'rotateY';\n\n return (\n <DialogPanelPrimitive\n key=\"dialog-panel\"\n data-slot=\"dialog-panel\"\n className={cn(\n 'fixed left-[50%] top-[50%] z-50 grid w-[calc(100%-2rem)] max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg rounded-xl',\n className,\n )}\n as={as as React.ElementType}\n initial={{\n opacity: 0,\n filter: 'blur(4px)',\n transform: `perspective(500px) ${rotateAxis}(${initialRotation}) scale(0.8)`,\n transition,\n }}\n animate={{\n opacity: 1,\n filter: 'blur(0px)',\n transform: `perspective(500px) ${rotateAxis}(0deg) scale(1)`,\n transition,\n }}\n exit={{\n opacity: 0,\n filter: 'blur(4px)',\n transform: `perspective(500px) ${rotateAxis}(${initialRotation}) scale(0.8)`,\n transition,\n }}\n {...rest}\n >\n {(bag) => (\n <>\n {typeof children === 'function' ? children(bag) : children}\n\n <CloseButton\n data-slot=\"dialog-panel-close\"\n className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\"\n >\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </CloseButton>\n </>\n )}\n </DialogPanelPrimitive>\n );\n}\n\ntype DialogHeaderProps<TTag extends React.ElementType = 'div'> =\n React.ComponentProps<TTag> & {\n as?: TTag;\n };\n\nfunction DialogHeader<TTag extends React.ElementType = 'div'>({\n className,\n as: Component = 'div',\n ...props\n}: DialogHeaderProps<TTag>) {\n return (\n <Component\n data-slot=\"dialog-header\"\n className={cn(\n 'flex flex-col space-y-1.5 text-center sm:text-left',\n className,\n )}\n {...props}\n />\n );\n}\n\ntype DialogFooterProps<TTag extends React.ElementType = 'div'> =\n React.ComponentProps<TTag> & {\n as?: TTag;\n };\n\nfunction DialogFooter({\n className,\n as: Component = 'div',\n ...props\n}: DialogFooterProps) {\n return (\n <Component\n data-slot=\"dialog-footer\"\n className={cn(\n 'flex flex-col-reverse sm:flex-row sm:justify-end gap-2',\n className,\n )}\n {...props}\n />\n );\n}\n\ntype DialogTitleProps<TTag extends React.ElementType = 'h2'> =\n DialogTitlePrimitiveProps<TTag> & {\n className?: string;\n as?: TTag;\n };\n\nfunction DialogTitle<TTag extends React.ElementType = 'h2'>({\n className,\n ...props\n}: DialogTitleProps<TTag>) {\n return (\n <DialogTitlePrimitive\n data-slot=\"dialog-title\"\n className={cn(\n 'text-lg font-semibold leading-none tracking-tight',\n className,\n )}\n {...props}\n />\n );\n}\n\ntype DialogDescriptionProps<TTag extends React.ElementType = 'div'> =\n React.ComponentProps<typeof DialogDescriptionPrimitive<TTag>> & {\n className?: string;\n as?: TTag;\n };\n\nfunction DialogDescription<TTag extends React.ElementType = 'div'>({\n className,\n ...props\n}: DialogDescriptionProps<TTag>) {\n return (\n <DialogDescriptionPrimitive\n data-slot=\"dialog-description\"\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogBackdrop,\n DialogPanel,\n DialogTitle,\n DialogDescription,\n DialogHeader,\n DialogFooter,\n type DialogProps,\n type DialogBackdropProps,\n type DialogPanelProps,\n type DialogTitleProps,\n type DialogDescriptionProps,\n type DialogHeaderProps,\n type DialogFooterProps,\n};\n",
|
|
"type": "registry:ui",
|
|
"target": "components/animate-ui/headless/dialog.tsx"
|
|
}
|
|
]
|
|
} |