{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"button","title":"Button","type":"registry:ui","description":"Standard button with multiple variants with built in state handling.","dependencies":["@radix-ui/react-slot","class-variance-authority"],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json","https://fysk.dev/r/fysk-hooks.json"],"files":[{"path":"fysk/button.tsx","type":"registry:ui","content":"\"use client\"\r\nimport * as React from \"react\"\r\nimport { Slot } from \"@radix-ui/react-slot\"\r\nimport { cva, type VariantProps } from \"class-variance-authority\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\nimport { useFyskConfig, type FyskIconPosition } from \"@/components/fysk-provider\"\r\nimport { useFyskAnimation } from \"@/components/hooks/useFyskAnimation\"\r\n\r\nconst buttonVariants = cva(\r\n    \"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none overflow-hidden active:scale-95\",\r\n    {\r\n        variants: {\r\n            variant: {\r\n                default: \"bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs\",\r\n                outline: \"border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground\",\r\n                secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground\",\r\n                ghost: \"hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground\",\r\n                destructive: \"bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90\",\r\n                link: \"text-primary underline-offset-4 hover:underline\",\r\n\r\n                glass: \"bg-foreground/10 backdrop-blur-md border-border/50 text-foreground hover:bg-foreground/15 shadow-xs\",\r\n                success: \"bg-green-600 text-white hover:bg-green-600/90 shadow-xs\",\r\n                warning: \"bg-yellow-600 text-white hover:bg-yellow-600/90 shadow-xs\",\r\n                info: \"bg-blue-600 text-white hover:bg-blue-600/90 shadow-xs\",\r\n\r\n                \"success-outline\": \"bg-green-500/10 border border-green-500/50 text-green-700 dark:text-green-400 hover:bg-green-500/20 shadow-xs\",\r\n                \"warning-outline\": \"bg-yellow-500/10 border border-yellow-500/50 text-yellow-700 dark:text-yellow-400 hover:bg-yellow-500/20 shadow-xs\",\r\n                \"info-outline\": \"bg-blue-500/10 border border-blue-500/50 text-blue-700 dark:text-blue-400 hover:bg-blue-500/20 shadow-xs\",\r\n                \"destructive-outline\": \"bg-destructive/10 border border-destructive/50 text-destructive hover:bg-destructive/20 shadow-xs\",\r\n            },\r\n            size: {\r\n                default: \"h-8 gap-1.5 px-2.5 data-[icon-position=end]:pr-2 data-[icon-position=start]:pl-2\",\r\n                xs: \"h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg data-[icon-position=end]:pr-1.5 data-[icon-position=start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3\",\r\n                sm: \"h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg data-[icon-position=end]:pr-1.5 data-[icon-position=start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5\",\r\n                md: \"h-8 gap-1.5 px-3 data-[icon-position=end]:pr-2.5 data-[icon-position=start]:pl-2.5\",\r\n                lg: \"h-9 gap-1.5 px-3 data-[icon-position=end]:pr-3 data-[icon-position=start]:pl-3\",\r\n                xl: \"h-10 gap-2 px-5 text-base data-[icon-position=end]:pr-4 data-[icon-position=start]:pl-4\",\r\n                icon: \"size-8\",\r\n                \"icon-xs\": \"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3\",\r\n                \"icon-sm\": \"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3.5\",\r\n                \"icon-lg\": \"size-9\",\r\n            },\r\n        },\r\n        defaultVariants: {\r\n            variant: \"default\",\r\n            size: \"md\",\r\n        },\r\n    }\r\n)\r\n\r\nexport interface ButtonProps\r\n    extends React.ButtonHTMLAttributes<HTMLButtonElement>,\r\n    VariantProps<typeof buttonVariants> {\r\n    autoStateVariant?: boolean\r\n    asChild?: boolean\r\n    state?: \"idle\" | \"loading\" | \"success\" | \"error\"\r\n    loadingText?: string\r\n    successText?: string\r\n    errorText?: string\r\n    icon?: React.ReactNode\r\n    iconLoading?: React.ReactNode\r\n    iconSuccess?: React.ReactNode\r\n    iconError?: React.ReactNode\r\n    iconPosition?: FyskIconPosition\r\n}\r\n\r\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\r\n    (\r\n        {\r\n            className,\r\n            autoStateVariant = false,\r\n            variant = \"default\",\r\n            size = \"md\",\r\n            asChild = false,\r\n            state = \"idle\",\r\n            loadingText,\r\n            successText,\r\n            errorText,\r\n            icon,\r\n            iconLoading: propIconLoading,\r\n            iconSuccess: propIconSuccess,\r\n            iconError: propIconError,\r\n            iconPosition: propIconPosition,\r\n            children,\r\n            ...props\r\n        },\r\n        ref\r\n    ) => {\r\n        const config = useFyskConfig()\r\n        const { isEnabled, motion, AnimatePresence, transitions, variants } = useFyskAnimation()\r\n\r\n        const isLoading = state === \"loading\"\r\n        const isSuccess = state === \"success\"\r\n        const isError = state === \"error\"\r\n\r\n        const activeIconPosition = propIconPosition || config.iconPosition || \"start\"\r\n\r\n        const finalIconLoading = propIconLoading || config.icons?.loading\r\n        const finalIconSuccess = propIconSuccess || config.icons?.success\r\n        const finalIconError = propIconError || config.icons?.error\r\n\r\n        // Icon Size Detection\r\n        const isIconSize = size?.toString().startsWith(\"icon\")\r\n\r\n        // Content Logic\r\n        // For icon sizes, we treat children as the icon if no icon prop is provided.\r\n        // We also don't render text in icon-sizes to maintain the square aspect ratio.\r\n        const idleIcon = isIconSize ? (icon || children) : icon\r\n        const idleText = isIconSize ? null : children\r\n\r\n        const activeIcon = isLoading ? finalIconLoading : isSuccess ? finalIconSuccess : isError ? finalIconError : idleIcon\r\n        const activeText = isLoading ? (loadingText || children) : isSuccess ? (successText || children) : isError ? (errorText || children) : idleText\r\n\r\n        const MotionButton = isEnabled && motion ? motion.button : \"button\"\r\n        const Comp = asChild ? Slot : (MotionButton as any)\r\n        const MotionSpan = isEnabled && motion ? motion.span : \"span\"\r\n\r\n        // Final variant based on the state\r\n        variant = autoStateVariant ? isSuccess ? \"success-outline\" : isError ? \"destructive-outline\" : variant : variant;\r\n\r\n        return (\r\n            <Comp\r\n                data-slot=\"button\"\r\n                data-variant={variant}\r\n                data-size={size}\r\n                data-icon-position={activeIconPosition}\r\n                className={cn(buttonVariants({ variant, size, className }))}\r\n                ref={ref}\r\n                disabled={isLoading || props.disabled}\r\n                data-state={state}\r\n                {...(isEnabled && motion && !asChild ? {\r\n                    layout: true,\r\n                    transition: transitions?.layoutEnter\r\n                } : {})}\r\n                {...props}\r\n            >\r\n                {asChild ? children : (\r\n                    <AnimatePresence {...(isEnabled && motion ? { mode: \"wait\", initial: false } : {})}>\r\n                        <MotionSpan\r\n                            key={state}\r\n                            className={cn(\r\n                                \"inline-flex items-center justify-center gap-[inherit]\",\r\n                                activeIconPosition === \"end\" && \"flex-row-reverse\"\r\n                            )}\r\n                            {...(isEnabled && motion && variants ? {\r\n                                layout: true,\r\n                                ...variants.fadeSlide,\r\n                                transition: transitions?.content\r\n                            } : {})}\r\n                        >\r\n                            {activeIcon && (\r\n                                <MotionSpan\r\n                                    className={cn(\r\n                                        \"inline-flex shrink-0 items-center justify-center\",\r\n                                        // Auto-scale SVGs for standard sizes\r\n                                        !isIconSize && (\r\n                                            size === \"xs\" ? \"[&_svg]:size-3\" :\r\n                                                size === \"sm\" ? \"[&_svg]:size-3.5\" :\r\n                                                    size === \"xl\" ? \"[&_svg]:size-5\" :\r\n                                                        \"[&_svg]:size-4\"\r\n                                        )\r\n                                    )}\r\n                                    {...(isEnabled && motion && variants ? {\r\n                                        layout: true,\r\n                                        ...variants.iconPop\r\n                                    } : {})}\r\n                                >\r\n                                    {activeIcon}\r\n                                </MotionSpan>\r\n                            )}\r\n                            {!isIconSize && activeText}\r\n                        </MotionSpan>\r\n                    </AnimatePresence>\r\n                )}\r\n            </Comp>\r\n        )\r\n    }\r\n)\r\nButton.displayName = \"Button\"\r\n\r\nexport { Button, buttonVariants }\r\n"}]}