Fortura/apps/www/public/r/input-button.json
2025-08-20 04:12:49 -06:00

18 lines
6.5 KiB
JSON

{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "input-button",
"type": "registry:ui",
"title": "Input Button",
"description": "A button that shows an input when clicked.",
"dependencies": [
"motion"
],
"files": [
{
"path": "registry/buttons/input/index.tsx",
"content": "'use client';\n\nimport * as React from 'react';\nimport {\n AnimatePresence,\n HTMLMotionProps,\n motion,\n Transition,\n} from 'motion/react';\nimport { ArrowRight } from 'lucide-react';\nimport { cn } from '@/lib/utils';\n\ntype InputButtonContextType = {\n showInput: boolean;\n setShowInput: React.Dispatch<React.SetStateAction<boolean>>;\n transition: Transition;\n id: string;\n};\nconst InputButtonContext = React.createContext<\n InputButtonContextType | undefined\n>(undefined);\n\nconst useInputButton = (): InputButtonContextType => {\n const context = React.useContext(InputButtonContext);\n if (!context) {\n throw new Error('useInputButton must be used within a InputButton');\n }\n return context;\n};\n\ntype InputButtonProviderProps = React.ComponentProps<'div'> &\n Partial<InputButtonContextType>;\n\nfunction InputButtonProvider({\n className,\n transition = { type: 'spring', stiffness: 300, damping: 20 },\n showInput,\n setShowInput,\n id,\n ...props\n}: InputButtonProviderProps) {\n const localId = React.useId();\n const [localShowInput, setLocalShowInput] = React.useState(false);\n\n return (\n <InputButtonContext.Provider\n value={{\n showInput: showInput ?? localShowInput,\n setShowInput: setShowInput ?? setLocalShowInput,\n transition,\n id: id ?? localId,\n }}\n >\n <div\n data-slot=\"input-button-provider\"\n className={cn(\n 'relative w-fit flex items-center justify-center h-10',\n (showInput || localShowInput) && 'w-full max-w-[400px]',\n className,\n )}\n {...props}\n />\n </InputButtonContext.Provider>\n );\n}\n\ntype InputButtonProps = HTMLMotionProps<'div'>;\n\nfunction InputButton({ className, ...props }: InputButtonProps) {\n return (\n <motion.div\n data-slot=\"input-button\"\n className={cn('flex size-full', className)}\n {...props}\n />\n );\n}\n\ntype InputButtonActionProps = HTMLMotionProps<'button'>;\n\nfunction InputButtonAction({ className, ...props }: InputButtonActionProps) {\n const { transition, setShowInput, id } = useInputButton();\n\n return (\n <motion.button\n data-slot=\"input-button-action\"\n className={cn(\n 'bg-background text-sm whitespace-nowrap disabled:pointer-events-none disabled:opacity-50 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive rounded-full border text-background-foreground cursor-pointer pl-4 pr-12 size-full font-medium',\n className,\n )}\n layoutId={`input-button-action-${id}`}\n transition={transition}\n onClick={() => setShowInput((prev) => !prev)}\n {...props}\n />\n );\n}\n\ntype InputButtonSubmitProps = HTMLMotionProps<'button'> & {\n icon?: React.ElementType;\n};\n\nfunction InputButtonSubmit({\n className,\n children,\n icon: Icon = ArrowRight,\n ...props\n}: InputButtonSubmitProps) {\n const { transition, showInput, setShowInput, id } = useInputButton();\n\n return (\n <motion.button\n data-slot=\"input-button-submit\"\n layoutId={`input-button-submit-${id}`}\n transition={transition}\n className={cn(\n \"z-[1] [&_svg:not([class*='size-'])]:size-4 cursor-pointer disabled:pointer-events-none disabled:opacity-50 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap bg-primary hover:bg-primary/90 transition-colors text-primary-foreground rounded-full text-sm flex items-center justify-center font-medium absolute inset-y-1 right-1\",\n showInput ? 'px-4' : 'aspect-square',\n className,\n )}\n onClick={() => setShowInput((prev) => !prev)}\n {...props}\n >\n {showInput ? (\n <motion.span\n key=\"show-button\"\n initial={{ opacity: 0, scale: 0 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.2 }}\n >\n {children}\n </motion.span>\n ) : (\n <motion.span\n key=\"show-icon\"\n initial={{ opacity: 0, scale: 0 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.2 }}\n >\n <Icon className=\"size-4\" />\n </motion.span>\n )}\n </motion.button>\n );\n}\n\ntype InputButtonInputProps = React.ComponentProps<'input'>;\n\nfunction InputButtonInput({ className, ...props }: InputButtonInputProps) {\n const { transition, showInput, id } = useInputButton();\n\n return (\n <AnimatePresence>\n {showInput && (\n <div className=\"absolute inset-0 size-full flex items-center justify-center\">\n <motion.div\n layoutId={`input-button-input-${id}`}\n className=\"size-full flex items-center bg-background rounded-full relative\"\n transition={transition}\n >\n <input\n data-slot=\"input-button-input\"\n className={cn(\n 'size-full selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground inset-0 pl-4 focus-visible:border-ring border focus-visible:ring-ring/50 focus-visible:ring-[3px] pr-32 py-2 text-sm bg-background rounded-full focus:outline-none absolute shrink-0 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive disabled:pointer-events-none disabled:cursor-not-allowed',\n className,\n )}\n {...props}\n />\n </motion.div>\n </div>\n )}\n </AnimatePresence>\n );\n}\n\nexport {\n InputButton,\n InputButtonProvider,\n InputButtonAction,\n InputButtonSubmit,\n InputButtonInput,\n useInputButton,\n type InputButtonProps,\n type InputButtonProviderProps,\n type InputButtonActionProps,\n type InputButtonSubmitProps,\n type InputButtonInputProps,\n};\n",
"type": "registry:ui",
"target": "components/animate-ui/buttons/input.tsx"
}
]
}