@humain/ui 2.2.4 → 2.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/index11.js +4 -4
  2. package/dist/index11.js.map +1 -1
  3. package/dist/index110.js +4 -4
  4. package/dist/index110.js.map +1 -1
  5. package/dist/index134.js +4 -4
  6. package/dist/index134.js.map +1 -1
  7. package/dist/index135.js +2 -2
  8. package/dist/index135.js.map +1 -1
  9. package/dist/index151.js +3 -3
  10. package/dist/index151.js.map +1 -1
  11. package/dist/index152.js +1 -1
  12. package/dist/index152.js.map +1 -1
  13. package/dist/index155.js +1 -1
  14. package/dist/index155.js.map +1 -1
  15. package/dist/index188.js +4 -4
  16. package/dist/index188.js.map +1 -1
  17. package/dist/index201.js +1 -1
  18. package/dist/index201.js.map +1 -1
  19. package/dist/index228.js +1 -1
  20. package/dist/index228.js.map +1 -1
  21. package/dist/index24.js +1 -1
  22. package/dist/index24.js.map +1 -1
  23. package/dist/index25.js +4 -4
  24. package/dist/index25.js.map +1 -1
  25. package/dist/index39.js +2 -2
  26. package/dist/index39.js.map +1 -1
  27. package/dist/index5.js +8 -8
  28. package/dist/index5.js.map +1 -1
  29. package/dist/styles.css +1 -1
  30. package/dist/wc/AIScrollButton.js +8 -8
  31. package/dist/wc/AIScrollButton.js.map +1 -1
  32. package/dist/wc/Badge.js +9 -9
  33. package/dist/wc/Badge.js.map +1 -1
  34. package/dist/wc/Checkbox.js +22 -22
  35. package/dist/wc/Checkbox.js.map +1 -1
  36. package/dist/wc/Pagination.js +4 -4
  37. package/dist/wc/Pagination.js.map +1 -1
  38. package/dist/wc/Switch.js +23 -23
  39. package/dist/wc/Switch.js.map +1 -1
  40. package/dist/wc/Tabs.js +110 -110
  41. package/dist/wc/Tabs.js.map +1 -1
  42. package/dist/wc/Tooltip.js +28 -28
  43. package/dist/wc/Tooltip.js.map +1 -1
  44. package/dist/wc/button.js +33 -33
  45. package/dist/wc/button.js.map +1 -1
  46. package/dist/wc/hf-alert.js +10 -10
  47. package/dist/wc/hf-alert.js.map +1 -1
  48. package/dist/wc/hf-nav-account-menu.js +8 -8
  49. package/dist/wc/hf-nav-account-menu.js.map +1 -1
  50. package/dist/wc/hf-steps.js +8 -8
  51. package/dist/wc/hf-steps.js.map +1 -1
  52. package/dist/wc/markdown-renderer.js +4 -4
  53. package/dist/wc/markdown-renderer.js.map +1 -1
  54. package/dist/wc/selection-group.js +6 -6
  55. package/dist/wc/selection-group.js.map +1 -1
  56. package/dist/wc-react/AIScrollButton.js +18 -18
  57. package/dist/wc-react/AIScrollButton.js.map +1 -1
  58. package/dist/wc-react/Badge.js +16 -16
  59. package/dist/wc-react/Badge.js.map +1 -1
  60. package/dist/wc-react/Checkbox.js +14 -14
  61. package/dist/wc-react/Checkbox.js.map +1 -1
  62. package/dist/wc-react/Pagination.js +130 -130
  63. package/dist/wc-react/Pagination.js.map +1 -1
  64. package/dist/wc-react/Switch.js +2 -2
  65. package/dist/wc-react/Switch.js.map +1 -1
  66. package/dist/wc-react/Tabs.js +38 -38
  67. package/dist/wc-react/Tabs.js.map +1 -1
  68. package/dist/wc-react/Tooltip.js +22 -22
  69. package/dist/wc-react/Tooltip.js.map +1 -1
  70. package/dist/wc-react/button.js +8 -8
  71. package/dist/wc-react/button.js.map +1 -1
  72. package/dist/wc-react/hf-alert.js +28 -28
  73. package/dist/wc-react/hf-alert.js.map +1 -1
  74. package/dist/wc-react/hf-nav-account-menu.js +7 -7
  75. package/dist/wc-react/hf-nav-account-menu.js.map +1 -1
  76. package/dist/wc-react/hf-steps.js +16 -16
  77. package/dist/wc-react/hf-steps.js.map +1 -1
  78. package/dist/wc-react/markdown-renderer.js +4 -4
  79. package/dist/wc-react/markdown-renderer.js.map +1 -1
  80. package/dist/wc-react/selection-group.js +1 -1
  81. package/dist/wc-react/selection-group.js.map +1 -1
  82. package/package.json +1 -1
  83. package/skills/humain-ui/SKILL.md +1 -1
  84. package/skills/humain-ui-wc/SKILL.md +1 -1
  85. package/skills/humain-ui-wc.skill +0 -0
  86. package/skills/humain-ui.skill +0 -0
  87. package/src/tokens.css +10 -2
package/dist/index228.js CHANGED
@@ -217,7 +217,7 @@ function NavAccountMenuAccount({ avatar, name, email, selected = false, onSelect
217
217
  /* @__PURE__ */ jsx("div", {
218
218
  "aria-hidden": "true",
219
219
  className: cn("size-4 rounded-full border-2 shrink-0 flex items-center justify-center transition-colors duration-200 ease-smooth motion-reduce:transition-none", selected ? "bg-primary border-primary" : "border-border bg-transparent"),
220
- children: selected && /* @__PURE__ */ jsx("div", { className: "size-1.5 rounded-full bg-primary-foreground delight-check-bounce" })
220
+ children: selected && /* @__PURE__ */ jsx("div", { className: "size-1.5 rounded-full bg-background delight-check-bounce" })
221
221
  })
222
222
  ]
223
223
  })
@@ -1 +1 @@
1
- {"version":3,"file":"index228.js","names":[],"sources":["../src/components/templates/NavAccountMenu/NavAccountMenu.tsx"],"sourcesContent":["'use client';\n\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { ChevronRight, LogOut } from 'lucide-react';\nimport * as React from 'react';\nimport { Switch } from '@/components/atoms/forms/Switch/Switch';\nimport { Kbd } from '@/components/molecules/data/Kbd';\nimport {\n PopoverRoot as PopoverBase,\n PopoverPopup as PopoverContentBase,\n PopoverTrigger as PopoverTriggerBase,\n} from '@/components/organisms/overlays/Popover';\nimport { useControllableState } from '@/hooks/use-controllable-state';\nimport { createSafeContext } from '@/lib/create-safe-context';\nimport { cn } from '@/lib/utils';\n\n// ============================================\n// Context for NavAccountMenu\n// ============================================\n\ninterface NavAccountMenuContextValue {\n type: 'card' | 'simple';\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\nconst [NavAccountMenuContext, useNavAccountMenuContext] =\n createSafeContext<NavAccountMenuContextValue>('NavAccountMenu');\n\n// ============================================\n// NavAccountMenu (Root)\n// ============================================\n\nexport interface NavAccountMenuProps {\n /** Trigger variant style */\n type?: 'card' | 'simple';\n /** Controlled open state */\n open?: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Initial open state for uncontrolled mode */\n defaultOpen?: boolean;\n /** Children (trigger + content) */\n children: React.ReactNode;\n}\n\nfunction NavAccountMenu({\n type = 'card',\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n children,\n}: NavAccountMenuProps) {\n const [open, handleOpenChange] = useControllableState({\n value: controlledOpen,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n\n const contextValue = React.useMemo(\n () => ({\n type,\n open,\n onOpenChange: handleOpenChange,\n }),\n [type, open, handleOpenChange],\n );\n\n return (\n <NavAccountMenuContext.Provider value={contextValue}>\n <PopoverBase open={open} onOpenChange={handleOpenChange}>\n <div data-slot=\"nav-account-menu\">{children}</div>\n </PopoverBase>\n </NavAccountMenuContext.Provider>\n );\n}\n\n// ============================================\n// NavAccountMenuTrigger\n// ============================================\n\nconst triggerVariants = cva(\n 'flex items-center gap-4 w-full text-left cursor-pointer transition-colors duration-200 ease-smooth outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none',\n {\n variants: {\n type: {\n card: 'bg-surface-1 border border-border rounded-xl shadow-xs p-3 hover:bg-surface-2',\n simple: 'border border-border px-2 py-3 hover:bg-surface-2 rounded-lg',\n },\n },\n defaultVariants: {\n type: 'card',\n },\n },\n);\n\nexport interface NavAccountMenuTriggerProps\n extends VariantProps<typeof triggerVariants> {\n /** Avatar element (use Avatar component) */\n avatar?: React.ReactNode;\n /** User display name */\n name: string;\n /** User email address */\n email?: string;\n /** Optional badge after name (e.g., \"External\" tag) */\n badge?: React.ReactNode;\n /** Show logout button instead of chevron (for 'simple' type) */\n showLogoutButton?: boolean;\n /** Logout callback for simple variant */\n onLogout?: () => void;\n /** Show only avatar without name/email - useful for collapsed sidebar */\n compact?: boolean;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuTrigger({\n avatar,\n name,\n email,\n badge,\n showLogoutButton,\n onLogout,\n compact = false,\n className,\n}: NavAccountMenuTriggerProps) {\n const { type, open } = useNavAccountMenuContext();\n\n const handleLogoutClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n onLogout?.();\n };\n\n const avatarContent = avatar ? (\n <div className=\"shrink-0\">{avatar}</div>\n ) : null;\n const accountDetails = !compact ? (\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"min-w-0 truncate text-sm font-semibold text-foreground\">\n {name}\n </span>\n {badge && <span className=\"shrink-0\">{badge}</span>}\n </div>\n {email && (\n <span className=\"truncate text-sm text-muted-foreground\">{email}</span>\n )}\n </div>\n ) : null;\n\n if (type === 'simple' && showLogoutButton && !compact) {\n return (\n <div\n data-slot=\"nav-account-menu-trigger\"\n className={cn(triggerVariants({ type }), 'group/trigger', className)}\n >\n <PopoverTriggerBase\n render={\n <button\n type=\"button\"\n aria-haspopup=\"true\"\n className=\"flex min-w-0 flex-1 items-center gap-4 text-left outline-none\"\n />\n }\n >\n {avatarContent}\n {accountDetails}\n </PopoverTriggerBase>\n <button\n type=\"button\"\n onClick={handleLogoutClick}\n className=\"shrink-0 rounded-md p-1.5 text-text-quaternary transition-colors duration-200 ease-smooth hover:bg-muted hover:text-secondary-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none\"\n aria-label=\"Sign out\"\n >\n <LogOut className=\"size-5\" />\n </button>\n </div>\n );\n }\n\n return (\n <PopoverTriggerBase\n render={\n <button\n type=\"button\"\n data-slot=\"nav-account-menu-trigger\"\n aria-haspopup=\"true\"\n aria-label={compact ? `${name} account menu` : undefined}\n className={cn(\n triggerVariants({ type }),\n 'group/trigger',\n compact ? 'w-auto justify-center' : 'max-w-[280px]',\n className,\n )}\n />\n }\n >\n {avatarContent}\n {!compact && (\n <>\n {accountDetails}\n <ChevronRight\n className={cn(\n 'size-4 shrink-0 text-muted-foreground transition-transform duration-200 ease-smooth motion-reduce:transition-none',\n open ? 'rotate-180' : 'rtl:rotate-180',\n )}\n />\n </>\n )}\n </PopoverTriggerBase>\n );\n}\n\n// ============================================\n// NavAccountMenuContent\n// ============================================\n\nexport interface NavAccountMenuContentProps {\n /** Popover side positioning */\n side?: 'top' | 'right' | 'bottom' | 'left';\n /** Popover alignment */\n align?: 'start' | 'center' | 'end';\n /** Side offset */\n sideOffset?: number;\n /** Children */\n children: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuContent({\n side = 'right',\n align = 'end',\n sideOffset = 8,\n children,\n className,\n}: NavAccountMenuContentProps) {\n return (\n <PopoverContentBase\n side={side}\n align={align}\n sideOffset={sideOffset}\n aria-label=\"Account menu\"\n data-slot=\"nav-account-menu-content\"\n className={cn(\n 'w-[300px] max-w-[calc(100vw-2rem)] p-0 gap-0 bg-popover border border-border rounded-[14px] overflow-hidden',\n 'shadow-[0px_12px_16px_-4px_color-mix(in_srgb,var(--foreground)_8%,transparent),0px_4px_6px_-2px_color-mix(in_srgb,var(--foreground)_3%,transparent),0px_2px_2px_-1px_color-mix(in_srgb,var(--foreground)_4%,transparent)]',\n 'data-open:animate-in data-closed:animate-out',\n 'data-closed:fade-out-0 data-open:fade-in-0',\n 'data-closed:zoom-out-95 data-open:zoom-in-95',\n 'data-[side=bottom]:slide-in-from-top-2',\n 'data-[side=left]:slide-in-from-right-2',\n 'data-[side=right]:slide-in-from-left-2',\n 'data-[side=top]:slide-in-from-bottom-2',\n 'duration-200 motion-reduce:animate-none',\n className,\n )}\n >\n <div className=\"flex flex-col py-2\" role=\"menu\" aria-label=\"Account menu\">\n {children}\n </div>\n </PopoverContentBase>\n );\n}\n\n// ============================================\n// NavAccountMenuHeader\n// ============================================\n\nexport interface NavAccountMenuHeaderProps {\n /** Header title (e.g. \"Profile\") */\n title: string;\n /** Subtitle text or custom React node (e.g. \"Last login 22 May, 2026 11:30pm\") */\n subtitle?: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuHeader({\n title,\n subtitle,\n className,\n}: NavAccountMenuHeaderProps) {\n return (\n <div\n data-slot=\"nav-account-menu-header\"\n className={cn('px-6 pb-3 delight-fade-in', className)}\n >\n <p className=\"text-base font-semibold text-foreground\">{title}</p>\n {subtitle &&\n (typeof subtitle === 'string' ? (\n <p className=\"mt-2 text-sm text-muted-foreground\">{subtitle}</p>\n ) : (\n <div className=\"mt-2\">{subtitle}</div>\n ))}\n </div>\n );\n}\n\n// ============================================\n// NavAccountMenuSection\n// ============================================\n\nexport interface NavAccountMenuSectionProps {\n /** Optional section header label */\n label?: string;\n /** Show top border separator between sections */\n bordered?: boolean;\n /** Section content */\n children: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuSection({\n label,\n bordered = false,\n children,\n className,\n}: NavAccountMenuSectionProps) {\n return (\n <section\n data-slot=\"nav-account-menu-section\"\n aria-label={label}\n className={cn(\n 'flex flex-col gap-1 py-1.5',\n bordered && 'border-t border-border',\n className,\n )}\n >\n {label && (\n <div className=\"px-3 pt-1.5 pb-1\">\n <span className=\"text-xs font-semibold text-muted-foreground\">\n {label}\n </span>\n </div>\n )}\n {children}\n </section>\n );\n}\n\n// ============================================\n// NavAccountMenuItem\n// ============================================\n\n/**\n * Props passed to the render function for custom menu item rendering.\n */\nexport interface NavAccountMenuItemRenderProps {\n /** Combined className for styling */\n className: string;\n /** Click handler that closes menu and triggers onSelect */\n onClick: () => void;\n /** Menu item content (icon, label, shortcut) */\n children: React.ReactNode;\n}\n\n/**\n * Render prop type for polymorphic rendering.\n * Accepts a function that receives props and returns an element.\n */\ntype NavAccountMenuItemRenderFn = (\n props: NavAccountMenuItemRenderProps,\n) => React.ReactElement;\n\nexport interface NavAccountMenuItemProps {\n /** Left icon (20px) */\n icon?: React.ReactNode;\n /** Keyboard shortcut display */\n shortcut?: string;\n /** Destructive styling (red) */\n destructive?: boolean;\n /** Disabled state */\n disabled?: boolean;\n /** Selection callback */\n onSelect?: () => void;\n /**\n * Render prop for polymorphic rendering.\n * Use this to render the menu item as a different element (e.g., link).\n * @example\n * <NavAccountMenuItem\n * icon={<User01 />}\n * render={(props) => <a {...props} href=\"/profile\" />}\n * >\n * View profile\n * </NavAccountMenuItem>\n */\n render?: NavAccountMenuItemRenderFn;\n /** Label text */\n children: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuItem({\n icon,\n shortcut,\n destructive = false,\n disabled = false,\n onSelect,\n render,\n children,\n className,\n}: NavAccountMenuItemProps) {\n const { onOpenChange } = useNavAccountMenuContext();\n\n const handleClick = () => {\n if (disabled) return;\n onSelect?.();\n onOpenChange(false);\n };\n\n const content = (\n <div className=\"flex flex-1 gap-2 items-center overflow-hidden p-2 rounded-md transition-[background-color] duration-200 ease-smooth delight-selection-press motion-reduce:transition-none\">\n <div className=\"flex flex-1 gap-2 items-center min-w-0\">\n {icon && (\n <span\n className={cn(\n 'shrink-0 [&>svg]:size-4 transition-transform duration-200 ease-smooth group-hover/menuitem:translate-x-0.5 motion-reduce:transition-none',\n destructive ? 'text-destructive' : 'text-foreground',\n )}\n >\n {icon}\n </span>\n )}\n <span\n className={cn(\n 'flex-1 text-sm truncate',\n destructive ? 'text-destructive' : 'text-secondary-foreground',\n )}\n >\n {children}\n </span>\n </div>\n {shortcut && (\n <Kbd variant=\"outline\" size=\"xs\" className=\"shrink-0\">\n {shortcut}\n </Kbd>\n )}\n </div>\n );\n\n const baseClassName = cn(\n 'group/menuitem flex items-center px-1.5 py-0 w-full text-left cursor-pointer outline-none',\n 'hover:[&>div]:bg-muted focus-visible:[&>div]:bg-muted',\n 'focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n disabled && 'pointer-events-none opacity-50',\n className,\n );\n\n if (render) {\n return render({\n className: baseClassName,\n onClick: handleClick,\n children: content,\n });\n }\n\n return (\n <button\n type=\"button\"\n role=\"menuitem\"\n data-slot=\"nav-account-menu-item\"\n className={baseClassName}\n onClick={handleClick}\n disabled={disabled}\n aria-disabled={disabled || undefined}\n >\n {content}\n </button>\n );\n}\n\n// ============================================\n// NavAccountMenuAccount\n// ============================================\n\nexport interface NavAccountMenuAccountProps {\n /** Avatar element */\n avatar?: React.ReactNode;\n /** Account display name */\n name: string;\n /** Account email */\n email: string;\n /** Whether this account is currently selected */\n selected?: boolean;\n /** Selection callback */\n onSelect?: () => void;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuAccount({\n avatar,\n name,\n email,\n selected = false,\n onSelect,\n className,\n}: NavAccountMenuAccountProps) {\n return (\n <div\n data-slot=\"nav-account-menu-account\"\n className={cn('flex items-center px-1.5 py-0 w-full', className)}\n >\n <button\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={selected}\n aria-label={`Switch to ${name} (${email})`}\n className={cn(\n 'relative flex flex-1 gap-2 items-center overflow-hidden px-2 py-1.5 rounded-md cursor-pointer outline-none',\n 'hover:bg-muted focus-visible:bg-muted',\n 'transition-colors duration-200 ease-smooth motion-reduce:transition-none',\n 'focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n 'text-left',\n selected && 'bg-surface-2',\n )}\n onClick={onSelect}\n >\n {avatar && <div className=\"shrink-0\">{avatar}</div>}\n <div className=\"flex flex-col min-w-0 flex-1\">\n <span className=\"text-sm font-semibold text-foreground truncate\">\n {name}\n </span>\n <span className=\"text-sm text-muted-foreground truncate\">\n {email}\n </span>\n </div>\n {/* Radio indicator */}\n <div\n aria-hidden=\"true\"\n className={cn(\n 'size-4 rounded-full border-2 shrink-0 flex items-center justify-center transition-colors duration-200 ease-smooth motion-reduce:transition-none',\n selected\n ? 'bg-primary border-primary'\n : 'border-border bg-transparent',\n )}\n >\n {selected && (\n <div className=\"size-1.5 rounded-full bg-primary-foreground delight-check-bounce\" />\n )}\n </div>\n </button>\n </div>\n );\n}\n\n// ============================================\n// NavAccountMenuSeparator\n// ============================================\n\nexport interface NavAccountMenuSeparatorProps {\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuSeparator({ className }: NavAccountMenuSeparatorProps) {\n return (\n <hr\n data-slot=\"nav-account-menu-separator\"\n className={cn('h-px border-0 bg-border mx-5', className)}\n />\n );\n}\n\n// ============================================\n// NavAccountMenuFooter\n// ============================================\n\nexport interface NavAccountMenuFooterProps {\n /** Footer content (typically buttons) */\n children: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuFooter({\n children,\n className,\n}: NavAccountMenuFooterProps) {\n return (\n <div\n data-slot=\"nav-account-menu-footer\"\n className={cn('flex items-center justify-center px-5 pt-4', className)}\n >\n {children}\n </div>\n );\n}\n\n// ============================================\n// NavAccountMenuThemeSwitch (uses Switch theme variant)\n// ============================================\n\nexport interface NavAccountMenuThemeSwitchProps {\n /** Switch theme variant to render */\n variant?:\n | 'theme-gradient'\n | 'theme-switch'\n | 'theme-pill'\n | 'theme-segmented';\n /** Switch size */\n size?: 'sm' | 'md' | 'lg';\n /** Controlled checked state (dark = checked) */\n checked?: boolean;\n /** Callback when checked state changes */\n onCheckedChange?: (checked: boolean) => void;\n /** Optional label displayed next to the switch (e.g. \"Light Mode\") */\n label?: string;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuThemeSwitch({\n variant = 'theme-gradient',\n size = 'sm',\n checked,\n onCheckedChange,\n label,\n className,\n}: NavAccountMenuThemeSwitchProps) {\n const switchId = React.useId();\n const labelId = label ? `${switchId}-label` : undefined;\n\n return (\n <div\n data-slot=\"nav-account-menu-theme-switch\"\n className={cn('flex items-center px-1.5 py-0', className)}\n >\n <div className=\"flex items-center gap-2.5 p-2\">\n <Switch\n id={switchId}\n variant={variant}\n size={size}\n checked={checked}\n onCheckedChange={onCheckedChange}\n aria-labelledby={labelId}\n aria-label={label ? undefined : 'Theme selector'}\n />\n {label && (\n <label\n htmlFor={switchId}\n id={labelId}\n className=\"cursor-pointer text-sm text-secondary-foreground\"\n >\n {label}\n </label>\n )}\n </div>\n </div>\n );\n}\n\n// ============================================\n// Display Names\n// ============================================\n\nNavAccountMenu.displayName = 'NavAccountMenu';\nNavAccountMenuTrigger.displayName = 'NavAccountMenuTrigger';\nNavAccountMenuContent.displayName = 'NavAccountMenuContent';\nNavAccountMenuHeader.displayName = 'NavAccountMenuHeader';\nNavAccountMenuSection.displayName = 'NavAccountMenuSection';\nNavAccountMenuItem.displayName = 'NavAccountMenuItem';\nNavAccountMenuAccount.displayName = 'NavAccountMenuAccount';\nNavAccountMenuSeparator.displayName = 'NavAccountMenuSeparator';\nNavAccountMenuFooter.displayName = 'NavAccountMenuFooter';\nNavAccountMenuThemeSwitch.displayName = 'NavAccountMenuThemeSwitch';\n\n// ============================================\n// Exports\n// ============================================\n\nexport {\n NavAccountMenu,\n NavAccountMenuAccount,\n NavAccountMenuContent,\n NavAccountMenuFooter,\n NavAccountMenuHeader,\n NavAccountMenuItem,\n NavAccountMenuSection,\n NavAccountMenuSeparator,\n NavAccountMenuThemeSwitch,\n NavAccountMenuTrigger,\n triggerVariants as navAccountMenuTriggerVariants,\n};\n"],"mappings":";;;;;;;;;;;;AA0BA,IAAM,CAAC,uBAAuB,4BAC5B,kBAA8C,gBAAgB;AAmBhE,SAAS,eAAe,EACtB,OAAO,QACP,MAAM,gBACN,cAAc,OACd,cACA,YACsB;CACtB,MAAM,CAAC,MAAM,oBAAoB,qBAAqB;EACpD,OAAO;EACP,cAAc;EACd,UAAU;CACZ,CAAC;CAED,MAAM,eAAe,QAAM,eAClB;EACL;EACA;EACA,cAAc;CAChB,IACA;EAAC;EAAM;EAAM;CAAgB,CAC/B;CAEA,OACE,oBAAC,sBAAsB,UAAvB;EAAgC,OAAO;YACrC,oBAAC,aAAD;GAAmB;GAAM,cAAc;aACrC,oBAAC,OAAD;IAAK,aAAU;IAAoB;GAAc,CAAA;EACtC,CAAA;CACiB,CAAA;AAEpC;AAMA,IAAM,kBAAkB,IACtB,mOACA;CACE,UAAU,EACR,MAAM;EACJ,MAAM;EACN,QAAQ;CACV,EACF;CACA,iBAAiB,EACf,MAAM,OACR;AACF,CACF;AAsBA,SAAS,sBAAsB,EAC7B,QACA,MACA,OACA,OACA,kBACA,UACA,UAAU,OACV,aAC6B;CAC7B,MAAM,EAAE,MAAM,SAAS,yBAAyB;CAEhD,MAAM,qBAAqB,MAAwB;EACjD,EAAE,gBAAgB;EAClB,WAAW;CACb;CAEA,MAAM,gBAAgB,SACpB,oBAAC,OAAD;EAAK,WAAU;YAAY;CAAY,CAAA,IACrC;CACJ,MAAM,iBAAiB,CAAC,UACtB,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAD;IAAM,WAAU;cACb;GACG,CAAA,GACL,SAAS,oBAAC,QAAD;IAAM,WAAU;cAAY;GAAY,CAAA,CAC/C;MACJ,SACC,oBAAC,QAAD;GAAM,WAAU;aAA0C;EAAY,CAAA,CAErE;MACH;CAEJ,IAAI,SAAS,YAAY,oBAAoB,CAAC,SAC5C,OACE,qBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,gBAAgB,EAAE,KAAK,CAAC,GAAG,iBAAiB,SAAS;YAFrE,CAIE,qBAAC,gBAAD;GACE,QACE,oBAAC,UAAD;IACE,MAAK;IACL,iBAAc;IACd,WAAU;GACX,CAAA;aANL,CASG,eACA,cACiB;MACpB,oBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;GACV,cAAW;aAEX,oBAAC,QAAD,EAAQ,WAAU,SAAU,CAAA;EACtB,CAAA,CACL;;CAIT,OACE,qBAAC,gBAAD;EACE,QACE,oBAAC,UAAD;GACE,MAAK;GACL,aAAU;GACV,iBAAc;GACd,cAAY,UAAU,GAAG,KAAK,iBAAiB,KAAA;GAC/C,WAAW,GACT,gBAAgB,EAAE,KAAK,CAAC,GACxB,iBACA,UAAU,0BAA0B,iBACpC,SACF;EACD,CAAA;YAbL,CAgBG,eACA,CAAC,WACA,qBAAA,UAAA,EAAA,UAAA,CACG,gBACD,oBAAC,cAAD,EACE,WAAW,GACT,qHACA,OAAO,eAAe,gBACxB,EACD,CAAA,CACD,EAAA,CAAA,CAEc;;AAExB;AAmBA,SAAS,sBAAsB,EAC7B,OAAO,SACP,QAAQ,OACR,aAAa,GACb,UACA,aAC6B;CAC7B,OACE,oBAAC,cAAD;EACQ;EACC;EACK;EACZ,cAAW;EACX,aAAU;EACV,WAAW,GACT,+GACA,6NACA,gDACA,8CACA,gDACA,0CACA,0CACA,0CACA,0CACA,2CACA,SACF;YAEA,oBAAC,OAAD;GAAK,WAAU;GAAqB,MAAK;GAAO,cAAW;GACxD;EACE,CAAA;CACa,CAAA;AAExB;AAeA,SAAS,qBAAqB,EAC5B,OACA,UACA,aAC4B;CAC5B,OACE,qBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,6BAA6B,SAAS;YAFtD,CAIE,oBAAC,KAAD;GAAG,WAAU;aAA2C;EAAS,CAAA,GAChE,aACE,OAAO,aAAa,WACnB,oBAAC,KAAD;GAAG,WAAU;aAAsC;EAAY,CAAA,IAE/D,oBAAC,OAAD;GAAK,WAAU;aAAQ;EAAc,CAAA,EAEtC;;AAET;AAiBA,SAAS,sBAAsB,EAC7B,OACA,WAAW,OACX,UACA,aAC6B;CAC7B,OACE,qBAAC,WAAD;EACE,aAAU;EACV,cAAY;EACZ,WAAW,GACT,8BACA,YAAY,0BACZ,SACF;YAPF,CASG,SACC,oBAAC,OAAD;GAAK,WAAU;aACb,oBAAC,QAAD;IAAM,WAAU;cACb;GACG,CAAA;EACH,CAAA,GAEN,QACM;;AAEb;AAuDA,SAAS,mBAAmB,EAC1B,MACA,UACA,cAAc,OACd,WAAW,OACX,UACA,QACA,UACA,aAC0B;CAC1B,MAAM,EAAE,iBAAiB,yBAAyB;CAElD,MAAM,oBAAoB;EACxB,IAAI,UAAU;EACd,WAAW;EACX,aAAa,KAAK;CACpB;CAEA,MAAM,UACJ,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,QACC,oBAAC,QAAD;IACE,WAAW,GACT,4IACA,cAAc,qBAAqB,iBACrC;cAEC;GACG,CAAA,GAER,oBAAC,QAAD;IACE,WAAW,GACT,2BACA,cAAc,qBAAqB,2BACrC;IAEC;GACG,CAAA,CACH;MACJ,YACC,oBAAC,KAAD;GAAK,SAAQ;GAAU,MAAK;GAAK,WAAU;aACxC;EACE,CAAA,CAEJ;;CAGP,MAAM,gBAAgB,GACpB,6FACA,yDACA,0FACA,YAAY,kCACZ,SACF;CAEA,IAAI,QACF,OAAO,OAAO;EACZ,WAAW;EACX,SAAS;EACT,UAAU;CACZ,CAAC;CAGH,OACE,oBAAC,UAAD;EACE,MAAK;EACL,MAAK;EACL,aAAU;EACV,WAAW;EACX,SAAS;EACC;EACV,iBAAe,YAAY,KAAA;YAE1B;CACK,CAAA;AAEZ;AAqBA,SAAS,sBAAsB,EAC7B,QACA,MACA,OACA,WAAW,OACX,UACA,aAC6B;CAC7B,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,wCAAwC,SAAS;YAE/D,qBAAC,UAAD;GACE,MAAK;GACL,MAAK;GACL,gBAAc;GACd,cAAY,aAAa,KAAK,IAAI,MAAM;GACxC,WAAW,GACT,8GACA,yCACA,4EACA,0FACA,aACA,YAAY,cACd;GACA,SAAS;aAbX;IAeG,UAAU,oBAAC,OAAD;KAAK,WAAU;eAAY;IAAY,CAAA;IAClD,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,QAAD;MAAM,WAAU;gBACb;KACG,CAAA,GACN,oBAAC,QAAD;MAAM,WAAU;gBACb;KACG,CAAA,CACH;;IAEL,oBAAC,OAAD;KACE,eAAY;KACZ,WAAW,GACT,mJACA,WACI,8BACA,8BACN;eAEC,YACC,oBAAC,OAAD,EAAK,WAAU,mEAAoE,CAAA;IAElF,CAAA;GACC;;CACL,CAAA;AAET;AAWA,SAAS,wBAAwB,EAAE,aAA2C;CAC5E,OACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GAAG,gCAAgC,SAAS;CACxD,CAAA;AAEL;AAaA,SAAS,qBAAqB,EAC5B,UACA,aAC4B;CAC5B,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,8CAA8C,SAAS;EAEpE;CACE,CAAA;AAET;AAyBA,SAAS,0BAA0B,EACjC,UAAU,kBACV,OAAO,MACP,SACA,iBACA,OACA,aACiC;CACjC,MAAM,WAAW,QAAM,MAAM;CAC7B,MAAM,UAAU,QAAQ,GAAG,SAAS,UAAU,KAAA;CAE9C,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;YAExD,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAD;IACE,IAAI;IACK;IACH;IACG;IACQ;IACjB,mBAAiB;IACjB,cAAY,QAAQ,KAAA,IAAY;GACjC,CAAA,GACA,SACC,oBAAC,SAAD;IACE,SAAS;IACT,IAAI;IACJ,WAAU;cAET;GACI,CAAA,CAEN;;CACF,CAAA;AAET;AAMA,eAAe,cAAc;AAC7B,sBAAsB,cAAc;AACpC,sBAAsB,cAAc;AACpC,qBAAqB,cAAc;AACnC,sBAAsB,cAAc;AACpC,mBAAmB,cAAc;AACjC,sBAAsB,cAAc;AACpC,wBAAwB,cAAc;AACtC,qBAAqB,cAAc;AACnC,0BAA0B,cAAc"}
1
+ {"version":3,"file":"index228.js","names":[],"sources":["../src/components/templates/NavAccountMenu/NavAccountMenu.tsx"],"sourcesContent":["'use client';\n\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { ChevronRight, LogOut } from 'lucide-react';\nimport * as React from 'react';\nimport { Switch } from '@/components/atoms/forms/Switch/Switch';\nimport { Kbd } from '@/components/molecules/data/Kbd';\nimport {\n PopoverRoot as PopoverBase,\n PopoverPopup as PopoverContentBase,\n PopoverTrigger as PopoverTriggerBase,\n} from '@/components/organisms/overlays/Popover';\nimport { useControllableState } from '@/hooks/use-controllable-state';\nimport { createSafeContext } from '@/lib/create-safe-context';\nimport { cn } from '@/lib/utils';\n\n// ============================================\n// Context for NavAccountMenu\n// ============================================\n\ninterface NavAccountMenuContextValue {\n type: 'card' | 'simple';\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\nconst [NavAccountMenuContext, useNavAccountMenuContext] =\n createSafeContext<NavAccountMenuContextValue>('NavAccountMenu');\n\n// ============================================\n// NavAccountMenu (Root)\n// ============================================\n\nexport interface NavAccountMenuProps {\n /** Trigger variant style */\n type?: 'card' | 'simple';\n /** Controlled open state */\n open?: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Initial open state for uncontrolled mode */\n defaultOpen?: boolean;\n /** Children (trigger + content) */\n children: React.ReactNode;\n}\n\nfunction NavAccountMenu({\n type = 'card',\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n children,\n}: NavAccountMenuProps) {\n const [open, handleOpenChange] = useControllableState({\n value: controlledOpen,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n\n const contextValue = React.useMemo(\n () => ({\n type,\n open,\n onOpenChange: handleOpenChange,\n }),\n [type, open, handleOpenChange],\n );\n\n return (\n <NavAccountMenuContext.Provider value={contextValue}>\n <PopoverBase open={open} onOpenChange={handleOpenChange}>\n <div data-slot=\"nav-account-menu\">{children}</div>\n </PopoverBase>\n </NavAccountMenuContext.Provider>\n );\n}\n\n// ============================================\n// NavAccountMenuTrigger\n// ============================================\n\nconst triggerVariants = cva(\n 'flex items-center gap-4 w-full text-left cursor-pointer transition-colors duration-200 ease-smooth outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none',\n {\n variants: {\n type: {\n card: 'bg-surface-1 border border-border rounded-xl shadow-xs p-3 hover:bg-surface-2',\n simple: 'border border-border px-2 py-3 hover:bg-surface-2 rounded-lg',\n },\n },\n defaultVariants: {\n type: 'card',\n },\n },\n);\n\nexport interface NavAccountMenuTriggerProps\n extends VariantProps<typeof triggerVariants> {\n /** Avatar element (use Avatar component) */\n avatar?: React.ReactNode;\n /** User display name */\n name: string;\n /** User email address */\n email?: string;\n /** Optional badge after name (e.g., \"External\" tag) */\n badge?: React.ReactNode;\n /** Show logout button instead of chevron (for 'simple' type) */\n showLogoutButton?: boolean;\n /** Logout callback for simple variant */\n onLogout?: () => void;\n /** Show only avatar without name/email - useful for collapsed sidebar */\n compact?: boolean;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuTrigger({\n avatar,\n name,\n email,\n badge,\n showLogoutButton,\n onLogout,\n compact = false,\n className,\n}: NavAccountMenuTriggerProps) {\n const { type, open } = useNavAccountMenuContext();\n\n const handleLogoutClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n onLogout?.();\n };\n\n const avatarContent = avatar ? (\n <div className=\"shrink-0\">{avatar}</div>\n ) : null;\n const accountDetails = !compact ? (\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"min-w-0 truncate text-sm font-semibold text-foreground\">\n {name}\n </span>\n {badge && <span className=\"shrink-0\">{badge}</span>}\n </div>\n {email && (\n <span className=\"truncate text-sm text-muted-foreground\">{email}</span>\n )}\n </div>\n ) : null;\n\n if (type === 'simple' && showLogoutButton && !compact) {\n return (\n <div\n data-slot=\"nav-account-menu-trigger\"\n className={cn(triggerVariants({ type }), 'group/trigger', className)}\n >\n <PopoverTriggerBase\n render={\n <button\n type=\"button\"\n aria-haspopup=\"true\"\n className=\"flex min-w-0 flex-1 items-center gap-4 text-left outline-none\"\n />\n }\n >\n {avatarContent}\n {accountDetails}\n </PopoverTriggerBase>\n <button\n type=\"button\"\n onClick={handleLogoutClick}\n className=\"shrink-0 rounded-md p-1.5 text-text-quaternary transition-colors duration-200 ease-smooth hover:bg-muted hover:text-secondary-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring motion-reduce:transition-none\"\n aria-label=\"Sign out\"\n >\n <LogOut className=\"size-5\" />\n </button>\n </div>\n );\n }\n\n return (\n <PopoverTriggerBase\n render={\n <button\n type=\"button\"\n data-slot=\"nav-account-menu-trigger\"\n aria-haspopup=\"true\"\n aria-label={compact ? `${name} account menu` : undefined}\n className={cn(\n triggerVariants({ type }),\n 'group/trigger',\n compact ? 'w-auto justify-center' : 'max-w-[280px]',\n className,\n )}\n />\n }\n >\n {avatarContent}\n {!compact && (\n <>\n {accountDetails}\n <ChevronRight\n className={cn(\n 'size-4 shrink-0 text-muted-foreground transition-transform duration-200 ease-smooth motion-reduce:transition-none',\n open ? 'rotate-180' : 'rtl:rotate-180',\n )}\n />\n </>\n )}\n </PopoverTriggerBase>\n );\n}\n\n// ============================================\n// NavAccountMenuContent\n// ============================================\n\nexport interface NavAccountMenuContentProps {\n /** Popover side positioning */\n side?: 'top' | 'right' | 'bottom' | 'left';\n /** Popover alignment */\n align?: 'start' | 'center' | 'end';\n /** Side offset */\n sideOffset?: number;\n /** Children */\n children: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuContent({\n side = 'right',\n align = 'end',\n sideOffset = 8,\n children,\n className,\n}: NavAccountMenuContentProps) {\n return (\n <PopoverContentBase\n side={side}\n align={align}\n sideOffset={sideOffset}\n aria-label=\"Account menu\"\n data-slot=\"nav-account-menu-content\"\n className={cn(\n 'w-[300px] max-w-[calc(100vw-2rem)] p-0 gap-0 bg-popover border border-border rounded-[14px] overflow-hidden',\n 'shadow-[0px_12px_16px_-4px_color-mix(in_srgb,var(--foreground)_8%,transparent),0px_4px_6px_-2px_color-mix(in_srgb,var(--foreground)_3%,transparent),0px_2px_2px_-1px_color-mix(in_srgb,var(--foreground)_4%,transparent)]',\n 'data-open:animate-in data-closed:animate-out',\n 'data-closed:fade-out-0 data-open:fade-in-0',\n 'data-closed:zoom-out-95 data-open:zoom-in-95',\n 'data-[side=bottom]:slide-in-from-top-2',\n 'data-[side=left]:slide-in-from-right-2',\n 'data-[side=right]:slide-in-from-left-2',\n 'data-[side=top]:slide-in-from-bottom-2',\n 'duration-200 motion-reduce:animate-none',\n className,\n )}\n >\n <div className=\"flex flex-col py-2\" role=\"menu\" aria-label=\"Account menu\">\n {children}\n </div>\n </PopoverContentBase>\n );\n}\n\n// ============================================\n// NavAccountMenuHeader\n// ============================================\n\nexport interface NavAccountMenuHeaderProps {\n /** Header title (e.g. \"Profile\") */\n title: string;\n /** Subtitle text or custom React node (e.g. \"Last login 22 May, 2026 11:30pm\") */\n subtitle?: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuHeader({\n title,\n subtitle,\n className,\n}: NavAccountMenuHeaderProps) {\n return (\n <div\n data-slot=\"nav-account-menu-header\"\n className={cn('px-6 pb-3 delight-fade-in', className)}\n >\n <p className=\"text-base font-semibold text-foreground\">{title}</p>\n {subtitle &&\n (typeof subtitle === 'string' ? (\n <p className=\"mt-2 text-sm text-muted-foreground\">{subtitle}</p>\n ) : (\n <div className=\"mt-2\">{subtitle}</div>\n ))}\n </div>\n );\n}\n\n// ============================================\n// NavAccountMenuSection\n// ============================================\n\nexport interface NavAccountMenuSectionProps {\n /** Optional section header label */\n label?: string;\n /** Show top border separator between sections */\n bordered?: boolean;\n /** Section content */\n children: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuSection({\n label,\n bordered = false,\n children,\n className,\n}: NavAccountMenuSectionProps) {\n return (\n <section\n data-slot=\"nav-account-menu-section\"\n aria-label={label}\n className={cn(\n 'flex flex-col gap-1 py-1.5',\n bordered && 'border-t border-border',\n className,\n )}\n >\n {label && (\n <div className=\"px-3 pt-1.5 pb-1\">\n <span className=\"text-xs font-semibold text-muted-foreground\">\n {label}\n </span>\n </div>\n )}\n {children}\n </section>\n );\n}\n\n// ============================================\n// NavAccountMenuItem\n// ============================================\n\n/**\n * Props passed to the render function for custom menu item rendering.\n */\nexport interface NavAccountMenuItemRenderProps {\n /** Combined className for styling */\n className: string;\n /** Click handler that closes menu and triggers onSelect */\n onClick: () => void;\n /** Menu item content (icon, label, shortcut) */\n children: React.ReactNode;\n}\n\n/**\n * Render prop type for polymorphic rendering.\n * Accepts a function that receives props and returns an element.\n */\ntype NavAccountMenuItemRenderFn = (\n props: NavAccountMenuItemRenderProps,\n) => React.ReactElement;\n\nexport interface NavAccountMenuItemProps {\n /** Left icon (20px) */\n icon?: React.ReactNode;\n /** Keyboard shortcut display */\n shortcut?: string;\n /** Destructive styling (red) */\n destructive?: boolean;\n /** Disabled state */\n disabled?: boolean;\n /** Selection callback */\n onSelect?: () => void;\n /**\n * Render prop for polymorphic rendering.\n * Use this to render the menu item as a different element (e.g., link).\n * @example\n * <NavAccountMenuItem\n * icon={<User01 />}\n * render={(props) => <a {...props} href=\"/profile\" />}\n * >\n * View profile\n * </NavAccountMenuItem>\n */\n render?: NavAccountMenuItemRenderFn;\n /** Label text */\n children: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuItem({\n icon,\n shortcut,\n destructive = false,\n disabled = false,\n onSelect,\n render,\n children,\n className,\n}: NavAccountMenuItemProps) {\n const { onOpenChange } = useNavAccountMenuContext();\n\n const handleClick = () => {\n if (disabled) return;\n onSelect?.();\n onOpenChange(false);\n };\n\n const content = (\n <div className=\"flex flex-1 gap-2 items-center overflow-hidden p-2 rounded-md transition-[background-color] duration-200 ease-smooth delight-selection-press motion-reduce:transition-none\">\n <div className=\"flex flex-1 gap-2 items-center min-w-0\">\n {icon && (\n <span\n className={cn(\n 'shrink-0 [&>svg]:size-4 transition-transform duration-200 ease-smooth group-hover/menuitem:translate-x-0.5 motion-reduce:transition-none',\n destructive ? 'text-destructive' : 'text-foreground',\n )}\n >\n {icon}\n </span>\n )}\n <span\n className={cn(\n 'flex-1 text-sm truncate',\n destructive ? 'text-destructive' : 'text-secondary-foreground',\n )}\n >\n {children}\n </span>\n </div>\n {shortcut && (\n <Kbd variant=\"outline\" size=\"xs\" className=\"shrink-0\">\n {shortcut}\n </Kbd>\n )}\n </div>\n );\n\n const baseClassName = cn(\n 'group/menuitem flex items-center px-1.5 py-0 w-full text-left cursor-pointer outline-none',\n 'hover:[&>div]:bg-muted focus-visible:[&>div]:bg-muted',\n 'focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n disabled && 'pointer-events-none opacity-50',\n className,\n );\n\n if (render) {\n return render({\n className: baseClassName,\n onClick: handleClick,\n children: content,\n });\n }\n\n return (\n <button\n type=\"button\"\n role=\"menuitem\"\n data-slot=\"nav-account-menu-item\"\n className={baseClassName}\n onClick={handleClick}\n disabled={disabled}\n aria-disabled={disabled || undefined}\n >\n {content}\n </button>\n );\n}\n\n// ============================================\n// NavAccountMenuAccount\n// ============================================\n\nexport interface NavAccountMenuAccountProps {\n /** Avatar element */\n avatar?: React.ReactNode;\n /** Account display name */\n name: string;\n /** Account email */\n email: string;\n /** Whether this account is currently selected */\n selected?: boolean;\n /** Selection callback */\n onSelect?: () => void;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuAccount({\n avatar,\n name,\n email,\n selected = false,\n onSelect,\n className,\n}: NavAccountMenuAccountProps) {\n return (\n <div\n data-slot=\"nav-account-menu-account\"\n className={cn('flex items-center px-1.5 py-0 w-full', className)}\n >\n <button\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={selected}\n aria-label={`Switch to ${name} (${email})`}\n className={cn(\n 'relative flex flex-1 gap-2 items-center overflow-hidden px-2 py-1.5 rounded-md cursor-pointer outline-none',\n 'hover:bg-muted focus-visible:bg-muted',\n 'transition-colors duration-200 ease-smooth motion-reduce:transition-none',\n 'focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n 'text-left',\n selected && 'bg-surface-2',\n )}\n onClick={onSelect}\n >\n {avatar && <div className=\"shrink-0\">{avatar}</div>}\n <div className=\"flex flex-col min-w-0 flex-1\">\n <span className=\"text-sm font-semibold text-foreground truncate\">\n {name}\n </span>\n <span className=\"text-sm text-muted-foreground truncate\">\n {email}\n </span>\n </div>\n {/* Radio indicator */}\n <div\n aria-hidden=\"true\"\n className={cn(\n 'size-4 rounded-full border-2 shrink-0 flex items-center justify-center transition-colors duration-200 ease-smooth motion-reduce:transition-none',\n selected\n ? 'bg-primary border-primary'\n : 'border-border bg-transparent',\n )}\n >\n {selected && (\n <div className=\"size-1.5 rounded-full bg-background delight-check-bounce\" />\n )}\n </div>\n </button>\n </div>\n );\n}\n\n// ============================================\n// NavAccountMenuSeparator\n// ============================================\n\nexport interface NavAccountMenuSeparatorProps {\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuSeparator({ className }: NavAccountMenuSeparatorProps) {\n return (\n <hr\n data-slot=\"nav-account-menu-separator\"\n className={cn('h-px border-0 bg-border mx-5', className)}\n />\n );\n}\n\n// ============================================\n// NavAccountMenuFooter\n// ============================================\n\nexport interface NavAccountMenuFooterProps {\n /** Footer content (typically buttons) */\n children: React.ReactNode;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuFooter({\n children,\n className,\n}: NavAccountMenuFooterProps) {\n return (\n <div\n data-slot=\"nav-account-menu-footer\"\n className={cn('flex items-center justify-center px-5 pt-4', className)}\n >\n {children}\n </div>\n );\n}\n\n// ============================================\n// NavAccountMenuThemeSwitch (uses Switch theme variant)\n// ============================================\n\nexport interface NavAccountMenuThemeSwitchProps {\n /** Switch theme variant to render */\n variant?:\n | 'theme-gradient'\n | 'theme-switch'\n | 'theme-pill'\n | 'theme-segmented';\n /** Switch size */\n size?: 'sm' | 'md' | 'lg';\n /** Controlled checked state (dark = checked) */\n checked?: boolean;\n /** Callback when checked state changes */\n onCheckedChange?: (checked: boolean) => void;\n /** Optional label displayed next to the switch (e.g. \"Light Mode\") */\n label?: string;\n /** Additional className */\n className?: string;\n}\n\nfunction NavAccountMenuThemeSwitch({\n variant = 'theme-gradient',\n size = 'sm',\n checked,\n onCheckedChange,\n label,\n className,\n}: NavAccountMenuThemeSwitchProps) {\n const switchId = React.useId();\n const labelId = label ? `${switchId}-label` : undefined;\n\n return (\n <div\n data-slot=\"nav-account-menu-theme-switch\"\n className={cn('flex items-center px-1.5 py-0', className)}\n >\n <div className=\"flex items-center gap-2.5 p-2\">\n <Switch\n id={switchId}\n variant={variant}\n size={size}\n checked={checked}\n onCheckedChange={onCheckedChange}\n aria-labelledby={labelId}\n aria-label={label ? undefined : 'Theme selector'}\n />\n {label && (\n <label\n htmlFor={switchId}\n id={labelId}\n className=\"cursor-pointer text-sm text-secondary-foreground\"\n >\n {label}\n </label>\n )}\n </div>\n </div>\n );\n}\n\n// ============================================\n// Display Names\n// ============================================\n\nNavAccountMenu.displayName = 'NavAccountMenu';\nNavAccountMenuTrigger.displayName = 'NavAccountMenuTrigger';\nNavAccountMenuContent.displayName = 'NavAccountMenuContent';\nNavAccountMenuHeader.displayName = 'NavAccountMenuHeader';\nNavAccountMenuSection.displayName = 'NavAccountMenuSection';\nNavAccountMenuItem.displayName = 'NavAccountMenuItem';\nNavAccountMenuAccount.displayName = 'NavAccountMenuAccount';\nNavAccountMenuSeparator.displayName = 'NavAccountMenuSeparator';\nNavAccountMenuFooter.displayName = 'NavAccountMenuFooter';\nNavAccountMenuThemeSwitch.displayName = 'NavAccountMenuThemeSwitch';\n\n// ============================================\n// Exports\n// ============================================\n\nexport {\n NavAccountMenu,\n NavAccountMenuAccount,\n NavAccountMenuContent,\n NavAccountMenuFooter,\n NavAccountMenuHeader,\n NavAccountMenuItem,\n NavAccountMenuSection,\n NavAccountMenuSeparator,\n NavAccountMenuThemeSwitch,\n NavAccountMenuTrigger,\n triggerVariants as navAccountMenuTriggerVariants,\n};\n"],"mappings":"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"}
package/dist/index24.js CHANGED
@@ -188,7 +188,7 @@ var SelectionIndicator = React$1.forwardRef(({ type, checked = false, disabled =
188
188
  "data-checked": checked,
189
189
  className: cn(selectionIndicatorVariants({ size }), "transition-[background-color,border-color] duration-150 motion-reduce:transition-none", checked ? cn(colors.indicatorChecked, "border-transparent") : "border-border bg-background", disabled && "opacity-50", className),
190
190
  "aria-hidden": "true",
191
- children: /* @__PURE__ */ jsx("span", { className: cn("bg-primary-foreground rounded-full transition-transform duration-150 ease-out motion-reduce:transition-none", innerDotSizes[size], checked ? "scale-100" : "scale-0") })
191
+ children: /* @__PURE__ */ jsx("span", { className: cn("bg-background rounded-full transition-transform duration-150 ease-out motion-reduce:transition-none", innerDotSizes[size], checked ? "scale-100" : "scale-0") })
192
192
  });
193
193
  });
194
194
  SelectionIndicator.displayName = "SelectionIndicator";
@@ -1 +1 @@
1
- {"version":3,"file":"index24.js","names":[],"sources":["../src/components/ui/selection-group.tsx"],"sourcesContent":["'use client';\n\nimport { cva } from 'class-variance-authority';\nimport { Minus } from 'lucide-react';\nimport * as React from 'react';\nimport { cn } from '@/lib/utils';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type SelectionColor =\n | 'primary'\n | 'secondary'\n | 'success'\n | 'warning'\n | 'info'\n | 'destructive';\n\nexport type SelectionSize = 'sm' | 'md' | 'lg';\n\nexport type SelectionVariant =\n | 'default'\n | 'card'\n | 'pill'\n | 'stacked'\n | 'icon-simple'\n | 'icon-card'\n | 'avatar'\n | 'payment'\n | 'simple'\n | 'chip';\n\nexport interface SelectionItem {\n /** Unique value for the option */\n value: string;\n /** Display label */\n label: string;\n /** Whether this option is disabled */\n disabled?: boolean;\n /** Secondary text (e.g., \"$10/month\") */\n subtext?: string;\n /** Supporting description text */\n description?: string;\n /** Leading icon component */\n icon?: React.ReactNode;\n /** Avatar element */\n avatar?: React.ReactNode;\n /** Image URL */\n image?: string;\n /** Alt text for the image (defaults to \"\" if omitted) */\n imageAlt?: string;\n /** Position of the image */\n imagePosition?: 'top' | 'left' | 'trailing';\n /** Badge element */\n badge?: React.ReactNode;\n /** Dot color for chip variant (named color or hex) */\n dotColor?: string;\n /** Price display (e.g., \"$10\") */\n price?: string;\n /** Price period (e.g., \"per month\") */\n pricePeriod?: string;\n /** Payment icon element */\n paymentIcon?: React.ReactNode;\n /** Action buttons */\n actions?: React.ReactNode;\n}\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\ninterface UseSelectionStateOptions {\n /** Controlled value */\n value?: string;\n /** Default value for uncontrolled mode */\n defaultValue?: string;\n /** Callback when value changes */\n onValueChange?: (value: string) => void;\n}\n\n/**\n * Hook for managing single selection state (radio behavior).\n * Supports both controlled and uncontrolled modes.\n */\nexport function useSelectionState(\n options: UseSelectionStateOptions = {},\n): [string | undefined, (value: string) => void] {\n const { value: controlledValue, defaultValue, onValueChange } = options;\n\n const [internalValue, setInternalValue] = React.useState<string | undefined>(\n defaultValue,\n );\n\n const isControlled = controlledValue !== undefined;\n const selectedValue = isControlled ? controlledValue : internalValue;\n\n const setValue = React.useCallback(\n (newValue: string) => {\n if (!isControlled) {\n setInternalValue(newValue);\n }\n onValueChange?.(newValue);\n },\n [isControlled, onValueChange],\n );\n\n return [selectedValue, setValue];\n}\n\ninterface UseMultiSelectionStateOptions {\n /** Controlled value */\n value?: string[];\n /** Default value for uncontrolled mode */\n defaultValue?: string[];\n /** Callback when value changes */\n onValueChange?: (value: string[]) => void;\n}\n\n/**\n * Hook for managing multi selection state (checkbox behavior).\n * Supports both controlled and uncontrolled modes.\n */\nexport function useMultiSelectionState(\n options: UseMultiSelectionStateOptions = {},\n): [string[], (value: string, checked: boolean) => void] {\n const { value: controlledValue, defaultValue = [], onValueChange } = options;\n\n const [internalValue, setInternalValue] =\n React.useState<string[]>(defaultValue);\n\n const isControlled = controlledValue !== undefined;\n const selectedValues = isControlled ? controlledValue : internalValue;\n\n const setValue = React.useCallback(\n (itemValue: string, checked: boolean) => {\n const newValue = checked\n ? [...selectedValues, itemValue]\n : selectedValues.filter((v) => v !== itemValue);\n\n if (!isControlled) {\n setInternalValue(newValue);\n }\n onValueChange?.(newValue);\n },\n [isControlled, selectedValues, onValueChange],\n );\n\n return [selectedValues, setValue];\n}\n\n// =============================================================================\n// Color Mappings\n// =============================================================================\n\nexport const selectionColorClasses: Record<\n SelectionColor,\n {\n indicator: string;\n indicatorChecked: string;\n border: string;\n bg: string;\n text: string;\n }\n> = {\n primary: {\n indicator: 'border-primary',\n indicatorChecked: 'bg-primary',\n border: 'border-primary',\n bg: 'bg-primary',\n text: 'text-primary-foreground',\n },\n secondary: {\n indicator: 'border-secondary',\n indicatorChecked: 'bg-secondary',\n border: 'border-secondary',\n bg: 'bg-secondary',\n text: 'text-secondary-foreground',\n },\n success: {\n indicator: 'border-success',\n indicatorChecked: 'bg-success',\n border: 'border-success',\n bg: 'bg-success',\n text: 'text-primary-foreground',\n },\n warning: {\n indicator: 'border-warning',\n indicatorChecked: 'bg-warning',\n border: 'border-warning',\n bg: 'bg-warning',\n text: 'text-primary-foreground',\n },\n info: {\n indicator: 'border-info',\n indicatorChecked: 'bg-info',\n border: 'border-info',\n bg: 'bg-info',\n text: 'text-primary-foreground',\n },\n destructive: {\n indicator: 'border-destructive',\n indicatorChecked: 'bg-destructive',\n border: 'border-destructive',\n bg: 'bg-destructive',\n text: 'text-primary-foreground',\n },\n};\n\n// =============================================================================\n// CVA Variants\n// =============================================================================\n\n/**\n * Selection group container variants.\n * Used for the outer wrapper of a group of selection items.\n */\nexport const selectionGroupVariants = cva('flex', {\n variants: {\n orientation: {\n horizontal: 'flex-row flex-wrap',\n vertical: 'flex-col',\n },\n gap: {\n sm: 'gap-2',\n md: 'gap-3',\n lg: 'gap-4',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n gap: 'md',\n },\n});\n\n/**\n * Selection indicator variants (for both radio and checkbox).\n * Renders as a rounded indicator with border.\n */\nexport const selectionIndicatorVariants = cva(\n 'shrink-0 rounded-full border-2 transition-all flex items-center justify-center',\n {\n variants: {\n size: {\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n },\n },\n defaultVariants: {\n size: 'sm',\n },\n },\n);\n\n/**\n * Checkbox indicator variants (square with rounded corners).\n */\nexport const checkboxIndicatorVariants = cva(\n 'shrink-0 border transition-[border-color,background-color,color,box-shadow] duration-200 ease-out motion-reduce:transition-none flex items-center justify-center',\n {\n variants: {\n size: {\n sm: 'size-4 rounded-[4px]',\n md: 'size-5 rounded-[5px]',\n lg: 'size-6 rounded-[6px]',\n },\n },\n defaultVariants: {\n size: 'sm',\n },\n },\n);\n\n/**\n * Inner dot sizes for radio indicators when checked.\n */\nexport const innerDotSizes: Record<SelectionSize, string> = {\n sm: 'size-1.5',\n md: 'size-2',\n lg: 'size-2.5',\n};\n\n/**\n * Text size classes for selection labels.\n */\nexport const textSizeClasses: Record<SelectionSize, string> = {\n sm: 'text-sm',\n md: 'text-sm',\n lg: 'text-base',\n};\n\n/**\n * Icon size classes for checkbox indicators.\n */\nexport const iconSizeClasses: Record<SelectionSize, string> = {\n sm: 'size-3',\n md: 'size-3.5',\n lg: 'size-4',\n};\n\n// =============================================================================\n// Draw-on checkmark SVG (shared delight animation)\n// =============================================================================\n\nexport const CHECKMARK_PATH = 'M4 12l5 5L20 6';\n\nexport function DrawCheck({ className }: { className?: string }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={3}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn(className, 'delight-check-draw')}\n >\n <path d={CHECKMARK_PATH} />\n </svg>\n );\n}\n\n// =============================================================================\n// SelectionIndicator Component\n// =============================================================================\n\nexport interface SelectionIndicatorProps {\n /** Type of indicator to render */\n type: 'checkbox' | 'radio';\n /** Whether the indicator is checked */\n checked?: boolean;\n /** Whether the indicator is disabled */\n disabled?: boolean;\n /** Size of the indicator */\n size?: SelectionSize;\n /** Color of the indicator */\n color?: SelectionColor;\n /** Additional CSS classes */\n className?: string;\n /** Whether the checkbox is in indeterminate state (checkbox only) */\n indeterminate?: boolean;\n}\n\n/**\n * SelectionIndicator renders either a checkbox or radio indicator based on type.\n * For checkbox: renders a check icon when checked.\n * For radio: renders an inner dot when checked.\n */\nconst SelectionIndicator = React.forwardRef<\n HTMLSpanElement,\n SelectionIndicatorProps\n>(\n (\n {\n type,\n checked = false,\n disabled = false,\n size = 'sm',\n color = 'primary',\n className,\n indeterminate = false,\n },\n ref,\n ) => {\n const colors = selectionColorClasses[color];\n\n if (type === 'checkbox') {\n return (\n <span\n ref={ref}\n data-slot=\"selection-indicator\"\n data-type=\"checkbox\"\n data-checked={checked}\n className={cn(\n checkboxIndicatorVariants({ size }),\n checked\n ? cn(\n colors.indicatorChecked,\n colors.border,\n colors.text,\n 'border',\n 'delight-check-bounce',\n )\n : 'border-border bg-background',\n disabled && 'opacity-50',\n className,\n )}\n aria-hidden=\"true\"\n >\n {checked &&\n (indeterminate ? (\n <Minus\n className={cn(\n iconSizeClasses[size],\n 'animate-[scale-in_150ms_ease-out] motion-reduce:animate-none',\n )}\n strokeWidth={3}\n />\n ) : (\n <DrawCheck className={iconSizeClasses[size]} />\n ))}\n </span>\n );\n }\n\n // Radio type\n return (\n <span\n ref={ref}\n data-slot=\"selection-indicator\"\n data-type=\"radio\"\n data-checked={checked}\n className={cn(\n selectionIndicatorVariants({ size }),\n 'transition-[background-color,border-color] duration-150 motion-reduce:transition-none',\n checked\n ? cn(colors.indicatorChecked, 'border-transparent')\n : 'border-border bg-background',\n disabled && 'opacity-50',\n className,\n )}\n aria-hidden=\"true\"\n >\n <span\n className={cn(\n 'bg-primary-foreground rounded-full transition-transform duration-150 ease-out motion-reduce:transition-none',\n innerDotSizes[size],\n checked ? 'scale-100' : 'scale-0',\n )}\n />\n </span>\n );\n },\n);\n\nSelectionIndicator.displayName = 'SelectionIndicator';\n\n// =============================================================================\n// FeaturedIcon Component\n// =============================================================================\n\nexport interface FeaturedIconProps {\n /** The icon content to display */\n children: React.ReactNode;\n /** Size of the featured icon container */\n size?: SelectionSize;\n /** Additional CSS classes */\n className?: string;\n}\n\n// Hoisted outside component to avoid recreation on every render\nconst featuredIconContainerSizes: Record<SelectionSize, string> = {\n sm: 'size-8',\n md: 'size-10',\n lg: 'size-12',\n};\n\nconst featuredIconInnerSizes: Record<SelectionSize, string> = {\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n};\n\n/**\n * FeaturedIcon is a container for icons with border and shadow.\n * Used in icon-simple and icon-card variants.\n */\nconst FeaturedIcon = React.forwardRef<HTMLDivElement, FeaturedIconProps>(\n ({ children, size = 'sm', className }, ref) => {\n const containerSize = featuredIconContainerSizes[size];\n const innerSize = featuredIconInnerSizes[size];\n\n return (\n <div\n ref={ref}\n data-slot=\"featured-icon\"\n className={cn(\n 'shrink-0 rounded-md border border-border bg-card/60',\n 'flex items-center justify-center',\n 'dark:bg-foreground/60 dark:border-border',\n containerSize,\n className,\n )}\n style={{\n boxShadow: 'var(--shadow-inset-ring)',\n }}\n >\n <div\n className={cn(\n 'text-secondary-foreground dark:text-text-quaternary',\n innerSize,\n )}\n >\n {children}\n </div>\n </div>\n );\n },\n);\n\nFeaturedIcon.displayName = 'FeaturedIcon';\n\n// =============================================================================\n// ColorDot Component\n// =============================================================================\n\nexport type ColorDotColor =\n | 'gray'\n | 'orange'\n | 'red'\n | 'darkRed'\n | 'brand'\n | 'success'\n | 'warning'\n | 'error'\n | string; // Allow custom hex colors\n\nconst dotColorMap: Record<string, string> = {\n gray: 'bg-muted',\n orange: 'bg-warning',\n red: 'bg-destructive',\n darkRed: 'bg-destructive-hover',\n brand: 'bg-primary/10',\n success: 'bg-success/10',\n warning: 'bg-warning/10',\n error: 'bg-destructive/10',\n};\n\nconst dotSizeMap: Record<SelectionSize, string> = {\n sm: 'size-[10px]',\n md: 'size-3',\n lg: 'size-3.5',\n};\n\nexport interface ColorDotProps {\n /** Color of the dot (named color or hex) */\n color?: ColorDotColor;\n /** Size of the dot */\n size?: SelectionSize;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * ColorDot renders a colored dot for chip variant.\n * Supports named colors (gray, orange, red, etc.) and hex colors.\n */\nconst ColorDot = React.forwardRef<HTMLSpanElement, ColorDotProps>(\n ({ color, size = 'sm', className }, ref) => {\n if (!color) return null;\n\n const colorClass = dotColorMap[color];\n const isHexColor = color.startsWith('#');\n\n return (\n <span\n ref={ref}\n data-slot=\"color-dot\"\n className={cn(\n 'shrink-0 rounded-full',\n dotSizeMap[size],\n !isHexColor && colorClass,\n className,\n )}\n style={isHexColor ? { backgroundColor: color } : undefined}\n aria-hidden=\"true\"\n />\n );\n },\n);\n\nColorDot.displayName = 'ColorDot';\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport { ColorDot, FeaturedIcon, SelectionIndicator };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index24.js","names":[],"sources":["../src/components/ui/selection-group.tsx"],"sourcesContent":["'use client';\n\nimport { cva } from 'class-variance-authority';\nimport { Minus } from 'lucide-react';\nimport * as React from 'react';\nimport { cn } from '@/lib/utils';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type SelectionColor =\n | 'primary'\n | 'secondary'\n | 'success'\n | 'warning'\n | 'info'\n | 'destructive';\n\nexport type SelectionSize = 'sm' | 'md' | 'lg';\n\nexport type SelectionVariant =\n | 'default'\n | 'card'\n | 'pill'\n | 'stacked'\n | 'icon-simple'\n | 'icon-card'\n | 'avatar'\n | 'payment'\n | 'simple'\n | 'chip';\n\nexport interface SelectionItem {\n /** Unique value for the option */\n value: string;\n /** Display label */\n label: string;\n /** Whether this option is disabled */\n disabled?: boolean;\n /** Secondary text (e.g., \"$10/month\") */\n subtext?: string;\n /** Supporting description text */\n description?: string;\n /** Leading icon component */\n icon?: React.ReactNode;\n /** Avatar element */\n avatar?: React.ReactNode;\n /** Image URL */\n image?: string;\n /** Alt text for the image (defaults to \"\" if omitted) */\n imageAlt?: string;\n /** Position of the image */\n imagePosition?: 'top' | 'left' | 'trailing';\n /** Badge element */\n badge?: React.ReactNode;\n /** Dot color for chip variant (named color or hex) */\n dotColor?: string;\n /** Price display (e.g., \"$10\") */\n price?: string;\n /** Price period (e.g., \"per month\") */\n pricePeriod?: string;\n /** Payment icon element */\n paymentIcon?: React.ReactNode;\n /** Action buttons */\n actions?: React.ReactNode;\n}\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\ninterface UseSelectionStateOptions {\n /** Controlled value */\n value?: string;\n /** Default value for uncontrolled mode */\n defaultValue?: string;\n /** Callback when value changes */\n onValueChange?: (value: string) => void;\n}\n\n/**\n * Hook for managing single selection state (radio behavior).\n * Supports both controlled and uncontrolled modes.\n */\nexport function useSelectionState(\n options: UseSelectionStateOptions = {},\n): [string | undefined, (value: string) => void] {\n const { value: controlledValue, defaultValue, onValueChange } = options;\n\n const [internalValue, setInternalValue] = React.useState<string | undefined>(\n defaultValue,\n );\n\n const isControlled = controlledValue !== undefined;\n const selectedValue = isControlled ? controlledValue : internalValue;\n\n const setValue = React.useCallback(\n (newValue: string) => {\n if (!isControlled) {\n setInternalValue(newValue);\n }\n onValueChange?.(newValue);\n },\n [isControlled, onValueChange],\n );\n\n return [selectedValue, setValue];\n}\n\ninterface UseMultiSelectionStateOptions {\n /** Controlled value */\n value?: string[];\n /** Default value for uncontrolled mode */\n defaultValue?: string[];\n /** Callback when value changes */\n onValueChange?: (value: string[]) => void;\n}\n\n/**\n * Hook for managing multi selection state (checkbox behavior).\n * Supports both controlled and uncontrolled modes.\n */\nexport function useMultiSelectionState(\n options: UseMultiSelectionStateOptions = {},\n): [string[], (value: string, checked: boolean) => void] {\n const { value: controlledValue, defaultValue = [], onValueChange } = options;\n\n const [internalValue, setInternalValue] =\n React.useState<string[]>(defaultValue);\n\n const isControlled = controlledValue !== undefined;\n const selectedValues = isControlled ? controlledValue : internalValue;\n\n const setValue = React.useCallback(\n (itemValue: string, checked: boolean) => {\n const newValue = checked\n ? [...selectedValues, itemValue]\n : selectedValues.filter((v) => v !== itemValue);\n\n if (!isControlled) {\n setInternalValue(newValue);\n }\n onValueChange?.(newValue);\n },\n [isControlled, selectedValues, onValueChange],\n );\n\n return [selectedValues, setValue];\n}\n\n// =============================================================================\n// Color Mappings\n// =============================================================================\n\nexport const selectionColorClasses: Record<\n SelectionColor,\n {\n indicator: string;\n indicatorChecked: string;\n border: string;\n bg: string;\n text: string;\n }\n> = {\n primary: {\n indicator: 'border-primary',\n indicatorChecked: 'bg-primary',\n border: 'border-primary',\n bg: 'bg-primary',\n text: 'text-primary-foreground',\n },\n secondary: {\n indicator: 'border-secondary',\n indicatorChecked: 'bg-secondary',\n border: 'border-secondary',\n bg: 'bg-secondary',\n text: 'text-secondary-foreground',\n },\n success: {\n indicator: 'border-success',\n indicatorChecked: 'bg-success',\n border: 'border-success',\n bg: 'bg-success',\n text: 'text-primary-foreground',\n },\n warning: {\n indicator: 'border-warning',\n indicatorChecked: 'bg-warning',\n border: 'border-warning',\n bg: 'bg-warning',\n text: 'text-primary-foreground',\n },\n info: {\n indicator: 'border-info',\n indicatorChecked: 'bg-info',\n border: 'border-info',\n bg: 'bg-info',\n text: 'text-primary-foreground',\n },\n destructive: {\n indicator: 'border-destructive',\n indicatorChecked: 'bg-destructive',\n border: 'border-destructive',\n bg: 'bg-destructive',\n text: 'text-primary-foreground',\n },\n};\n\n// =============================================================================\n// CVA Variants\n// =============================================================================\n\n/**\n * Selection group container variants.\n * Used for the outer wrapper of a group of selection items.\n */\nexport const selectionGroupVariants = cva('flex', {\n variants: {\n orientation: {\n horizontal: 'flex-row flex-wrap',\n vertical: 'flex-col',\n },\n gap: {\n sm: 'gap-2',\n md: 'gap-3',\n lg: 'gap-4',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n gap: 'md',\n },\n});\n\n/**\n * Selection indicator variants (for both radio and checkbox).\n * Renders as a rounded indicator with border.\n */\nexport const selectionIndicatorVariants = cva(\n 'shrink-0 rounded-full border-2 transition-all flex items-center justify-center',\n {\n variants: {\n size: {\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n },\n },\n defaultVariants: {\n size: 'sm',\n },\n },\n);\n\n/**\n * Checkbox indicator variants (square with rounded corners).\n */\nexport const checkboxIndicatorVariants = cva(\n 'shrink-0 border transition-[border-color,background-color,color,box-shadow] duration-200 ease-out motion-reduce:transition-none flex items-center justify-center',\n {\n variants: {\n size: {\n sm: 'size-4 rounded-[4px]',\n md: 'size-5 rounded-[5px]',\n lg: 'size-6 rounded-[6px]',\n },\n },\n defaultVariants: {\n size: 'sm',\n },\n },\n);\n\n/**\n * Inner dot sizes for radio indicators when checked.\n */\nexport const innerDotSizes: Record<SelectionSize, string> = {\n sm: 'size-1.5',\n md: 'size-2',\n lg: 'size-2.5',\n};\n\n/**\n * Text size classes for selection labels.\n */\nexport const textSizeClasses: Record<SelectionSize, string> = {\n sm: 'text-sm',\n md: 'text-sm',\n lg: 'text-base',\n};\n\n/**\n * Icon size classes for checkbox indicators.\n */\nexport const iconSizeClasses: Record<SelectionSize, string> = {\n sm: 'size-3',\n md: 'size-3.5',\n lg: 'size-4',\n};\n\n// =============================================================================\n// Draw-on checkmark SVG (shared delight animation)\n// =============================================================================\n\nexport const CHECKMARK_PATH = 'M4 12l5 5L20 6';\n\nexport function DrawCheck({ className }: { className?: string }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={3}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn(className, 'delight-check-draw')}\n >\n <path d={CHECKMARK_PATH} />\n </svg>\n );\n}\n\n// =============================================================================\n// SelectionIndicator Component\n// =============================================================================\n\nexport interface SelectionIndicatorProps {\n /** Type of indicator to render */\n type: 'checkbox' | 'radio';\n /** Whether the indicator is checked */\n checked?: boolean;\n /** Whether the indicator is disabled */\n disabled?: boolean;\n /** Size of the indicator */\n size?: SelectionSize;\n /** Color of the indicator */\n color?: SelectionColor;\n /** Additional CSS classes */\n className?: string;\n /** Whether the checkbox is in indeterminate state (checkbox only) */\n indeterminate?: boolean;\n}\n\n/**\n * SelectionIndicator renders either a checkbox or radio indicator based on type.\n * For checkbox: renders a check icon when checked.\n * For radio: renders an inner dot when checked.\n */\nconst SelectionIndicator = React.forwardRef<\n HTMLSpanElement,\n SelectionIndicatorProps\n>(\n (\n {\n type,\n checked = false,\n disabled = false,\n size = 'sm',\n color = 'primary',\n className,\n indeterminate = false,\n },\n ref,\n ) => {\n const colors = selectionColorClasses[color];\n\n if (type === 'checkbox') {\n return (\n <span\n ref={ref}\n data-slot=\"selection-indicator\"\n data-type=\"checkbox\"\n data-checked={checked}\n className={cn(\n checkboxIndicatorVariants({ size }),\n checked\n ? cn(\n colors.indicatorChecked,\n colors.border,\n colors.text,\n 'border',\n 'delight-check-bounce',\n )\n : 'border-border bg-background',\n disabled && 'opacity-50',\n className,\n )}\n aria-hidden=\"true\"\n >\n {checked &&\n (indeterminate ? (\n <Minus\n className={cn(\n iconSizeClasses[size],\n 'animate-[scale-in_150ms_ease-out] motion-reduce:animate-none',\n )}\n strokeWidth={3}\n />\n ) : (\n <DrawCheck className={iconSizeClasses[size]} />\n ))}\n </span>\n );\n }\n\n // Radio type\n return (\n <span\n ref={ref}\n data-slot=\"selection-indicator\"\n data-type=\"radio\"\n data-checked={checked}\n className={cn(\n selectionIndicatorVariants({ size }),\n 'transition-[background-color,border-color] duration-150 motion-reduce:transition-none',\n checked\n ? cn(colors.indicatorChecked, 'border-transparent')\n : 'border-border bg-background',\n disabled && 'opacity-50',\n className,\n )}\n aria-hidden=\"true\"\n >\n <span\n className={cn(\n 'bg-background rounded-full transition-transform duration-150 ease-out motion-reduce:transition-none',\n innerDotSizes[size],\n checked ? 'scale-100' : 'scale-0',\n )}\n />\n </span>\n );\n },\n);\n\nSelectionIndicator.displayName = 'SelectionIndicator';\n\n// =============================================================================\n// FeaturedIcon Component\n// =============================================================================\n\nexport interface FeaturedIconProps {\n /** The icon content to display */\n children: React.ReactNode;\n /** Size of the featured icon container */\n size?: SelectionSize;\n /** Additional CSS classes */\n className?: string;\n}\n\n// Hoisted outside component to avoid recreation on every render\nconst featuredIconContainerSizes: Record<SelectionSize, string> = {\n sm: 'size-8',\n md: 'size-10',\n lg: 'size-12',\n};\n\nconst featuredIconInnerSizes: Record<SelectionSize, string> = {\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n};\n\n/**\n * FeaturedIcon is a container for icons with border and shadow.\n * Used in icon-simple and icon-card variants.\n */\nconst FeaturedIcon = React.forwardRef<HTMLDivElement, FeaturedIconProps>(\n ({ children, size = 'sm', className }, ref) => {\n const containerSize = featuredIconContainerSizes[size];\n const innerSize = featuredIconInnerSizes[size];\n\n return (\n <div\n ref={ref}\n data-slot=\"featured-icon\"\n className={cn(\n 'shrink-0 rounded-md border border-border bg-card/60',\n 'flex items-center justify-center',\n 'dark:bg-foreground/60 dark:border-border',\n containerSize,\n className,\n )}\n style={{\n boxShadow: 'var(--shadow-inset-ring)',\n }}\n >\n <div\n className={cn(\n 'text-secondary-foreground dark:text-text-quaternary',\n innerSize,\n )}\n >\n {children}\n </div>\n </div>\n );\n },\n);\n\nFeaturedIcon.displayName = 'FeaturedIcon';\n\n// =============================================================================\n// ColorDot Component\n// =============================================================================\n\nexport type ColorDotColor =\n | 'gray'\n | 'orange'\n | 'red'\n | 'darkRed'\n | 'brand'\n | 'success'\n | 'warning'\n | 'error'\n | string; // Allow custom hex colors\n\nconst dotColorMap: Record<string, string> = {\n gray: 'bg-muted',\n orange: 'bg-warning',\n red: 'bg-destructive',\n darkRed: 'bg-destructive-hover',\n brand: 'bg-primary/10',\n success: 'bg-success/10',\n warning: 'bg-warning/10',\n error: 'bg-destructive/10',\n};\n\nconst dotSizeMap: Record<SelectionSize, string> = {\n sm: 'size-[10px]',\n md: 'size-3',\n lg: 'size-3.5',\n};\n\nexport interface ColorDotProps {\n /** Color of the dot (named color or hex) */\n color?: ColorDotColor;\n /** Size of the dot */\n size?: SelectionSize;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * ColorDot renders a colored dot for chip variant.\n * Supports named colors (gray, orange, red, etc.) and hex colors.\n */\nconst ColorDot = React.forwardRef<HTMLSpanElement, ColorDotProps>(\n ({ color, size = 'sm', className }, ref) => {\n if (!color) return null;\n\n const colorClass = dotColorMap[color];\n const isHexColor = color.startsWith('#');\n\n return (\n <span\n ref={ref}\n data-slot=\"color-dot\"\n className={cn(\n 'shrink-0 rounded-full',\n dotSizeMap[size],\n !isHexColor && colorClass,\n className,\n )}\n style={isHexColor ? { backgroundColor: color } : undefined}\n aria-hidden=\"true\"\n />\n );\n },\n);\n\nColorDot.displayName = 'ColorDot';\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport { ColorDot, FeaturedIcon, SelectionIndicator };\n"],"mappings":"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"}
package/dist/index25.js CHANGED
@@ -48,22 +48,22 @@ var checkboxVariants = cva("relative flex shrink-0 items-center justify-center b
48
48
  {
49
49
  variant: "info",
50
50
  checked: true,
51
- className: "border-info bg-info text-primary-foreground hover:bg-info/90"
51
+ className: "border-info bg-info text-info-foreground hover:bg-info/90"
52
52
  },
53
53
  {
54
54
  variant: "success",
55
55
  checked: true,
56
- className: "border-success bg-success text-primary-foreground hover:bg-success/90"
56
+ className: "border-success bg-success text-success-foreground hover:bg-success/90"
57
57
  },
58
58
  {
59
59
  variant: "warning",
60
60
  checked: true,
61
- className: "border-warning bg-warning text-primary-foreground hover:bg-warning/90"
61
+ className: "border-warning bg-warning text-warning-foreground hover:bg-warning/90"
62
62
  },
63
63
  {
64
64
  variant: "destructive",
65
65
  checked: true,
66
- className: "border-destructive bg-destructive text-primary-foreground hover:bg-destructive/90"
66
+ className: "border-destructive bg-destructive text-destructive-foreground hover:bg-destructive/90"
67
67
  }
68
68
  ],
69
69
  defaultVariants: {
@@ -1 +1 @@
1
- {"version":3,"file":"index25.js","names":[],"sources":["../src/components/atoms/forms/Checkbox/Checkbox.tsx"],"sourcesContent":["'use client';\n\nimport { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox';\nimport { cva } from 'class-variance-authority';\nimport { Minus } from 'lucide-react';\nimport * as React from 'react';\nimport {\n type FieldProps,\n FieldTooltip,\n hasFieldProps,\n splitFieldProps,\n} from '@/components/atoms/forms/_shared';\nimport {\n Field,\n FieldBottom,\n FieldDescription,\n FieldError,\n FieldLabel,\n useFieldContextOptional,\n} from '@/components/ui/form-field';\nimport { DrawCheck } from '@/components/ui/selection-group';\nimport { cn } from '@/lib/utils';\n\n// ============================================\n// CVA Variants\n// ============================================\n\nconst checkboxVariants = cva(\n // Base styles\n 'relative flex shrink-0 items-center justify-center border shadow-xs transition-[border-color,background-color,color,box-shadow] duration-200 ease-out motion-reduce:transition-none focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',\n {\n variants: {\n variant: {\n primary: '',\n secondary: '',\n info: '',\n success: '',\n warning: '',\n destructive: '',\n },\n size: {\n sm: 'size-4 rounded-[4px]',\n md: 'size-5 rounded-[5px]',\n lg: 'size-6 rounded-[6px]',\n },\n checked: {\n true: '',\n false: '',\n },\n },\n compoundVariants: [\n // ===== UNCHECKED (all variants) =====\n {\n checked: false,\n className:\n 'bg-background border-border hover:border-primary hover:bg-primary/10',\n },\n // ===== PRIMARY CHECKED =====\n {\n variant: 'primary',\n checked: true,\n className:\n 'border-primary bg-primary text-primary-foreground hover:bg-primary/90',\n },\n // ===== SECONDARY CHECKED (outline style) =====\n {\n variant: 'secondary',\n checked: true,\n className: 'border-border bg-background text-foreground hover:bg-muted',\n },\n // ===== INFO CHECKED =====\n {\n variant: 'info',\n checked: true,\n className:\n 'border-info bg-info text-primary-foreground hover:bg-info/90',\n },\n // ===== SUCCESS CHECKED =====\n {\n variant: 'success',\n checked: true,\n className:\n 'border-success bg-success text-primary-foreground hover:bg-success/90',\n },\n // ===== WARNING CHECKED =====\n {\n variant: 'warning',\n checked: true,\n className:\n 'border-warning bg-warning text-primary-foreground hover:bg-warning/90',\n },\n // ===== DESTRUCTIVE CHECKED =====\n {\n variant: 'destructive',\n checked: true,\n className:\n 'border-destructive bg-destructive text-primary-foreground hover:bg-destructive/90',\n },\n ],\n defaultVariants: {\n variant: 'primary',\n size: 'sm',\n checked: false,\n },\n },\n);\n\n// ============================================\n// Types\n// ============================================\n\ntype CheckboxVariant =\n | 'primary'\n | 'secondary'\n | 'info'\n | 'success'\n | 'warning'\n | 'destructive';\ntype CheckboxSize = 'sm' | 'md' | 'lg';\n\n/**\n * Checkbox is a pure atom component that renders ONLY the checkbox element.\n * It does NOT render label, description, or error.\n *\n * Supports 6 color variants and 3 sizes matching Figma design.\n * When used inside a Field with an error, automatically uses destructive variant.\n *\n * @example\n * ```tsx\n * // Simple checkbox\n * <Checkbox />\n *\n * // With variant and size\n * <Checkbox variant=\"success\" size=\"lg\" />\n *\n * // Auto-destructive when Field has error\n * <Field error=\"Required\">\n * <Checkbox /> // automatically uses destructive variant\n * </Field>\n * ```\n */\ntype CheckboxControlProps = Omit<\n CheckboxPrimitive.Root.Props,\n 'onCheckedChange'\n> & {\n /** Color variant for the checkbox */\n variant?: CheckboxVariant;\n /** Size variant for the checkbox */\n size?: CheckboxSize;\n /** Controlled checked state. When provided, component is controlled. */\n checked?: boolean;\n /** Initial checked state for uncontrolled mode */\n defaultChecked?: boolean;\n /** Callback when checked state changes */\n onCheckedChange?: (checked: boolean) => void;\n};\n\nexport interface CheckboxProps\n extends CheckboxControlProps,\n Omit<FieldProps, 'topRight' | 'bottomRight'> {}\n\nexport type { CheckboxSize, CheckboxVariant };\n\n// ============================================\n// Size configuration\n// ============================================\n\nconst iconClasses = {\n sm: 'size-3',\n md: 'size-3.5',\n lg: 'size-4',\n};\n\n// ============================================\n// Checkbox (pure atom)\n// ============================================\n\nconst CheckboxControl = React.forwardRef<\n HTMLButtonElement,\n CheckboxControlProps\n>(\n (\n {\n className,\n variant: variantProp,\n size = 'sm',\n checked: controlledChecked,\n defaultChecked = false,\n onCheckedChange,\n ...props\n },\n ref,\n ) => {\n // Internal state for uncontrolled mode\n const [internalChecked, setInternalChecked] =\n React.useState(defaultChecked);\n\n // Use controlled value if provided, otherwise use internal state\n const isControlled = controlledChecked !== undefined;\n const checked = isControlled ? controlledChecked : internalChecked;\n\n // Field context integration - auto-destructive on error\n const fieldContext = useFieldContextOptional();\n const hasFieldError = fieldContext?.state === 'destructive';\n const variant = variantProp ?? (hasFieldError ? 'destructive' : 'primary');\n\n const handleCheckedChange = React.useCallback(\n (newChecked: boolean) => {\n if (!isControlled) {\n setInternalChecked(newChecked);\n }\n onCheckedChange?.(newChecked);\n },\n [isControlled, onCheckedChange],\n );\n\n return (\n <CheckboxPrimitive.Root\n ref={ref}\n data-slot=\"checkbox\"\n data-size={size}\n data-variant={variant}\n checked={checked}\n onCheckedChange={handleCheckedChange}\n className={cn(\n checkboxVariants({ variant, size, checked }),\n // Touch target expansion (ensures ≥24px hit area)\n 'before:content-[\"\"] before:absolute before:-inset-[4px] before:rounded-[inherit]',\n // Focus state - double ring effect\n 'focus-visible:ring-4 focus-visible:ring-bg-secondary focus-visible:shadow-[0_0_0_2px_var(--card),0_0_0_4px_var(--border)]',\n 'dark:focus-visible:ring-bg-tertiary dark:focus-visible:shadow-[0_0_0_2px_var(--card),0_0_0_4px_var(--border)]',\n checked && 'delight-check-bounce',\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"flex items-center justify-center text-current\"\n >\n {props.indeterminate ? (\n <Minus\n className={cn(\n iconClasses[size],\n 'animate-[scale-in_150ms_ease-out] motion-reduce:animate-none',\n )}\n strokeWidth={3}\n />\n ) : (\n <DrawCheck className={iconClasses[size]} />\n )}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n },\n);\n\nCheckboxControl.displayName = 'CheckboxControl';\n\n// =============================================================================\n// Checkbox — public API with smart field mode (inline layout)\n// =============================================================================\n\nconst Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(\n (props, forwardedRef) => {\n const [fieldPropsObj, controlProps] = splitFieldProps(props);\n const { ref: _ref, ...cp } = controlProps as CheckboxControlProps & {\n ref?: unknown;\n };\n\n if (!hasFieldProps(fieldPropsObj)) {\n return (\n <CheckboxControl\n ref={forwardedRef}\n {...cp}\n required={fieldPropsObj.required}\n />\n );\n }\n\n const {\n label,\n required,\n labelIcon,\n tooltip,\n description,\n error,\n fieldClassName,\n } = fieldPropsObj;\n\n return (\n <Field\n layout=\"inline\"\n state={error ? 'destructive' : undefined}\n disabled={cp.disabled}\n className={fieldClassName}\n >\n <CheckboxControl ref={forwardedRef} {...cp} required={required} />\n {label && (\n <FieldLabel required={required} labelIcon={labelIcon}>\n {label}\n {tooltip && <FieldTooltip content={tooltip} />}\n </FieldLabel>\n )}\n {(error || description) && (\n <FieldBottom>\n {error && <FieldError>{error}</FieldError>}\n {!error && description && (\n <FieldDescription>{description}</FieldDescription>\n )}\n </FieldBottom>\n )}\n </Field>\n );\n },\n);\nCheckbox.displayName = 'Checkbox';\n\nexport { Checkbox };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index25.js","names":[],"sources":["../src/components/atoms/forms/Checkbox/Checkbox.tsx"],"sourcesContent":["'use client';\n\nimport { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox';\nimport { cva } from 'class-variance-authority';\nimport { Minus } from 'lucide-react';\nimport * as React from 'react';\nimport {\n type FieldProps,\n FieldTooltip,\n hasFieldProps,\n splitFieldProps,\n} from '@/components/atoms/forms/_shared';\nimport {\n Field,\n FieldBottom,\n FieldDescription,\n FieldError,\n FieldLabel,\n useFieldContextOptional,\n} from '@/components/ui/form-field';\nimport { DrawCheck } from '@/components/ui/selection-group';\nimport { cn } from '@/lib/utils';\n\n// ============================================\n// CVA Variants\n// ============================================\n\nconst checkboxVariants = cva(\n // Base styles\n 'relative flex shrink-0 items-center justify-center border shadow-xs transition-[border-color,background-color,color,box-shadow] duration-200 ease-out motion-reduce:transition-none focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',\n {\n variants: {\n variant: {\n primary: '',\n secondary: '',\n info: '',\n success: '',\n warning: '',\n destructive: '',\n },\n size: {\n sm: 'size-4 rounded-[4px]',\n md: 'size-5 rounded-[5px]',\n lg: 'size-6 rounded-[6px]',\n },\n checked: {\n true: '',\n false: '',\n },\n },\n compoundVariants: [\n // ===== UNCHECKED (all variants) =====\n {\n checked: false,\n className:\n 'bg-background border-border hover:border-primary hover:bg-primary/10',\n },\n // ===== PRIMARY CHECKED =====\n {\n variant: 'primary',\n checked: true,\n className:\n 'border-primary bg-primary text-primary-foreground hover:bg-primary/90',\n },\n // ===== SECONDARY CHECKED (outline style) =====\n {\n variant: 'secondary',\n checked: true,\n className: 'border-border bg-background text-foreground hover:bg-muted',\n },\n // ===== INFO CHECKED =====\n {\n variant: 'info',\n checked: true,\n className: 'border-info bg-info text-info-foreground hover:bg-info/90',\n },\n // ===== SUCCESS CHECKED =====\n {\n variant: 'success',\n checked: true,\n className:\n 'border-success bg-success text-success-foreground hover:bg-success/90',\n },\n // ===== WARNING CHECKED =====\n {\n variant: 'warning',\n checked: true,\n className:\n 'border-warning bg-warning text-warning-foreground hover:bg-warning/90',\n },\n // ===== DESTRUCTIVE CHECKED =====\n {\n variant: 'destructive',\n checked: true,\n className:\n 'border-destructive bg-destructive text-destructive-foreground hover:bg-destructive/90',\n },\n ],\n defaultVariants: {\n variant: 'primary',\n size: 'sm',\n checked: false,\n },\n },\n);\n\n// ============================================\n// Types\n// ============================================\n\ntype CheckboxVariant =\n | 'primary'\n | 'secondary'\n | 'info'\n | 'success'\n | 'warning'\n | 'destructive';\ntype CheckboxSize = 'sm' | 'md' | 'lg';\n\n/**\n * Checkbox is a pure atom component that renders ONLY the checkbox element.\n * It does NOT render label, description, or error.\n *\n * Supports 6 color variants and 3 sizes matching Figma design.\n * When used inside a Field with an error, automatically uses destructive variant.\n *\n * @example\n * ```tsx\n * // Simple checkbox\n * <Checkbox />\n *\n * // With variant and size\n * <Checkbox variant=\"success\" size=\"lg\" />\n *\n * // Auto-destructive when Field has error\n * <Field error=\"Required\">\n * <Checkbox /> // automatically uses destructive variant\n * </Field>\n * ```\n */\ntype CheckboxControlProps = Omit<\n CheckboxPrimitive.Root.Props,\n 'onCheckedChange'\n> & {\n /** Color variant for the checkbox */\n variant?: CheckboxVariant;\n /** Size variant for the checkbox */\n size?: CheckboxSize;\n /** Controlled checked state. When provided, component is controlled. */\n checked?: boolean;\n /** Initial checked state for uncontrolled mode */\n defaultChecked?: boolean;\n /** Callback when checked state changes */\n onCheckedChange?: (checked: boolean) => void;\n};\n\nexport interface CheckboxProps\n extends CheckboxControlProps,\n Omit<FieldProps, 'topRight' | 'bottomRight'> {}\n\nexport type { CheckboxSize, CheckboxVariant };\n\n// ============================================\n// Size configuration\n// ============================================\n\nconst iconClasses = {\n sm: 'size-3',\n md: 'size-3.5',\n lg: 'size-4',\n};\n\n// ============================================\n// Checkbox (pure atom)\n// ============================================\n\nconst CheckboxControl = React.forwardRef<\n HTMLButtonElement,\n CheckboxControlProps\n>(\n (\n {\n className,\n variant: variantProp,\n size = 'sm',\n checked: controlledChecked,\n defaultChecked = false,\n onCheckedChange,\n ...props\n },\n ref,\n ) => {\n // Internal state for uncontrolled mode\n const [internalChecked, setInternalChecked] =\n React.useState(defaultChecked);\n\n // Use controlled value if provided, otherwise use internal state\n const isControlled = controlledChecked !== undefined;\n const checked = isControlled ? controlledChecked : internalChecked;\n\n // Field context integration - auto-destructive on error\n const fieldContext = useFieldContextOptional();\n const hasFieldError = fieldContext?.state === 'destructive';\n const variant = variantProp ?? (hasFieldError ? 'destructive' : 'primary');\n\n const handleCheckedChange = React.useCallback(\n (newChecked: boolean) => {\n if (!isControlled) {\n setInternalChecked(newChecked);\n }\n onCheckedChange?.(newChecked);\n },\n [isControlled, onCheckedChange],\n );\n\n return (\n <CheckboxPrimitive.Root\n ref={ref}\n data-slot=\"checkbox\"\n data-size={size}\n data-variant={variant}\n checked={checked}\n onCheckedChange={handleCheckedChange}\n className={cn(\n checkboxVariants({ variant, size, checked }),\n // Touch target expansion (ensures ≥24px hit area)\n 'before:content-[\"\"] before:absolute before:-inset-[4px] before:rounded-[inherit]',\n // Focus state - double ring effect\n 'focus-visible:ring-4 focus-visible:ring-bg-secondary focus-visible:shadow-[0_0_0_2px_var(--card),0_0_0_4px_var(--border)]',\n 'dark:focus-visible:ring-bg-tertiary dark:focus-visible:shadow-[0_0_0_2px_var(--card),0_0_0_4px_var(--border)]',\n checked && 'delight-check-bounce',\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"flex items-center justify-center text-current\"\n >\n {props.indeterminate ? (\n <Minus\n className={cn(\n iconClasses[size],\n 'animate-[scale-in_150ms_ease-out] motion-reduce:animate-none',\n )}\n strokeWidth={3}\n />\n ) : (\n <DrawCheck className={iconClasses[size]} />\n )}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n },\n);\n\nCheckboxControl.displayName = 'CheckboxControl';\n\n// =============================================================================\n// Checkbox — public API with smart field mode (inline layout)\n// =============================================================================\n\nconst Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(\n (props, forwardedRef) => {\n const [fieldPropsObj, controlProps] = splitFieldProps(props);\n const { ref: _ref, ...cp } = controlProps as CheckboxControlProps & {\n ref?: unknown;\n };\n\n if (!hasFieldProps(fieldPropsObj)) {\n return (\n <CheckboxControl\n ref={forwardedRef}\n {...cp}\n required={fieldPropsObj.required}\n />\n );\n }\n\n const {\n label,\n required,\n labelIcon,\n tooltip,\n description,\n error,\n fieldClassName,\n } = fieldPropsObj;\n\n return (\n <Field\n layout=\"inline\"\n state={error ? 'destructive' : undefined}\n disabled={cp.disabled}\n className={fieldClassName}\n >\n <CheckboxControl ref={forwardedRef} {...cp} required={required} />\n {label && (\n <FieldLabel required={required} labelIcon={labelIcon}>\n {label}\n {tooltip && <FieldTooltip content={tooltip} />}\n </FieldLabel>\n )}\n {(error || description) && (\n <FieldBottom>\n {error && <FieldError>{error}</FieldError>}\n {!error && description && (\n <FieldDescription>{description}</FieldDescription>\n )}\n </FieldBottom>\n )}\n </Field>\n );\n },\n);\nCheckbox.displayName = 'Checkbox';\n\nexport { Checkbox };\n"],"mappings":"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"}
package/dist/index39.js CHANGED
@@ -244,7 +244,7 @@ function ThemeSwitchToggle({ isLight, onThemeChange, sizes }) {
244
244
  "aria-label": `Switch to ${isLight ? "dark" : "light"} theme`,
245
245
  onClick: () => onThemeChange(isLight ? "dark" : "light"),
246
246
  className: cn("relative rounded-full transition-[colors,box-shadow] hover:shadow-[0_0_0_3px_var(--ring)/0.1] active:scale-[0.97]", sizes.track, themeTransitionClasses, themeFocusRingClasses, isLight ? "bg-input" : "bg-primary"),
247
- children: /* @__PURE__ */ jsx("div", { className: cn("absolute top-1/2 -translate-y-1/2 rounded-full shadow-md group-active:shadow-sm transition-all", sizes.indicator, themeTransitionClasses, isLight ? "bg-card" : "bg-primary-foreground", isLight ? "left-0.5" : "right-0.5") })
247
+ children: /* @__PURE__ */ jsx("div", { className: cn("absolute top-1/2 -translate-y-1/2 rounded-full shadow-md group-active:shadow-sm transition-all", sizes.indicator, themeTransitionClasses, isLight ? "bg-card" : "bg-background", isLight ? "left-0.5" : "right-0.5") })
248
248
  }), /* @__PURE__ */ jsx("div", {
249
249
  className: cn(sizes.icon, "text-[var(--muted-foreground)]"),
250
250
  children: isLight ? /* @__PURE__ */ jsx(Moon, { className: "size-full" }) : /* @__PURE__ */ jsx(Sun, { className: "size-full" })
@@ -428,7 +428,7 @@ var SwitchControl = React$1.forwardRef(({ label, description, labelPosition = "r
428
428
  const activeSizeClasses = hasLabels ? sizeWithLabelClasses : sizeClasses;
429
429
  const activeThumbTranslate = hasLabels ? thumbTranslateWithLabelClasses : thumbTranslateClasses;
430
430
  const trackClasses = switchStyle === "solid" ? cn("data-unchecked:bg-input dark:data-unchecked:bg-input/80 border-transparent", solidVariantClasses[colorVariant]) : cn("data-unchecked:bg-transparent data-checked:bg-transparent border-input", outlineVariantClasses[colorVariant]);
431
- const thumbClasses = switchStyle === "solid" ? "bg-background dark:data-unchecked:bg-foreground dark:data-checked:bg-primary-foreground" : cn("bg-input dark:bg-foreground", outlineThumbVariantClasses[colorVariant]);
431
+ const thumbClasses = switchStyle === "solid" ? "bg-background dark:data-unchecked:bg-foreground dark:data-checked:bg-background" : cn("bg-input dark:bg-foreground", outlineThumbVariantClasses[colorVariant]);
432
432
  const showThumbIcon = withIcon || thumbIconOn || thumbIconOff;
433
433
  const checkedIcon = showThumbIcon ? thumbIconOn || /* @__PURE__ */ jsx(Check, { className: iconSizeClasses[size] }) : null;
434
434
  const uncheckedIcon = showThumbIcon ? thumbIconOff || /* @__PURE__ */ jsx(X, { className: iconSizeClasses[size] }) : null;
@@ -1 +1 @@
1
- {"version":3,"file":"index39.js","names":[],"sources":["../src/components/atoms/forms/Switch/Switch.tsx"],"sourcesContent":["/** biome-ignore-all lint/a11y/useSemanticElements: Theme toggles use button-based radio controls for animated segmented UI while preserving radiogroup semantics. */\n'use client';\n\nimport { Switch as SwitchPrimitive } from '@base-ui/react/switch';\nimport { Check, Moon, Sun, X } from 'lucide-react';\nimport * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport {\n type FieldProps,\n FieldTooltip,\n} from '@/components/atoms/forms/_shared';\nimport { useThemeOptional } from '@/components/ThemeProvider';\nimport {\n Field,\n FieldBottom,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from '@/components/ui/form-field';\nimport { cn } from '@/lib/utils';\n\n// ============================================\n// Types\n// ============================================\n\n/** Color variants for the standard switch */\ntype ColorVariant =\n | 'primary'\n | 'secondary'\n | 'warning'\n | 'info'\n | 'success'\n | 'destructive';\n\n/** Theme variants that render theme toggle UIs */\ntype ThemeVariant =\n | 'theme-gradient'\n | 'theme-switch'\n | 'theme-pill'\n | 'theme-segmented';\n\n/** All switch variants */\nexport type SwitchVariant = ColorVariant | ThemeVariant;\n\n// ============================================\n// Switch (with controlled/uncontrolled support)\n// ============================================\n\ninterface SwitchControlProps\n extends Omit<SwitchPrimitive.Root.Props, 'onCheckedChange'> {\n // Variants\n size?: 'sm' | 'md' | 'lg';\n variant?: SwitchVariant;\n /** Visual style of the switch (solid = filled track, outline = bordered track). Only applies to color variants. */\n switchStyle?: 'solid' | 'outline';\n\n // Thumb icons (color variants only)\n withIcon?: boolean;\n thumbIconOn?: React.ReactNode;\n thumbIconOff?: React.ReactNode;\n\n // Track labels (ON/OFF inside track, color variants only)\n showOnOff?: boolean;\n onLabel?: string;\n offLabel?: string;\n\n // External label\n label?: React.ReactNode;\n description?: string;\n labelPosition?: 'left' | 'right';\n\n /** Layout orientation for label (theme variants only) */\n orientation?: 'horizontal' | 'vertical';\n\n // Core (keep existing)\n checked?: boolean;\n defaultChecked?: boolean;\n onCheckedChange?: (checked: boolean) => void;\n}\n\nexport interface SwitchProps\n extends Omit<SwitchControlProps, 'label' | 'description'>,\n Omit<FieldProps, 'label' | 'description' | 'topRight' | 'bottomRight'> {\n /** Label text or ReactNode (inline with switch, or in FieldLabel when field props are set) */\n label?: React.ReactNode;\n /** Description text (inline below label, or in FieldDescription when field props are set) */\n description?: React.ReactNode;\n}\n\n// ============================================\n// Color Switch Constants\n// ============================================\n\n// Icon size classes for thumb icons\nconst iconSizeClasses = {\n sm: 'size-2', // 8px\n md: 'size-2.5', // 10px\n lg: 'size-3', // 12px\n};\n\n// Size classes for the track\nconst sizeClasses = {\n sm: 'h-[18px] w-[32px]',\n md: 'h-[22px] w-[40px]',\n lg: 'h-[26px] w-[48px]',\n};\n\n// Size classes for the thumb\nconst thumbSizeClasses = {\n sm: 'size-[14px]',\n md: 'size-[18px]',\n lg: 'size-[22px]',\n};\n\n// Thumb translation when checked (track width - thumb size - 2px padding on each side)\nconst thumbTranslateClasses = {\n sm: 'data-checked:ltr:translate-x-[14px] data-checked:rtl:-translate-x-[14px]',\n md: 'data-checked:ltr:translate-x-[18px] data-checked:rtl:-translate-x-[18px]',\n lg: 'data-checked:ltr:translate-x-[22px] data-checked:rtl:-translate-x-[22px]',\n};\n\n// Wider track sizes when ON/OFF labels are shown (extra space for text)\nconst sizeWithLabelClasses = {\n sm: 'h-[18px] w-[40px]',\n md: 'h-[22px] w-[48px]',\n lg: 'h-[26px] w-[56px]',\n};\n\n// Thumb translation for wider tracks with labels\nconst thumbTranslateWithLabelClasses = {\n sm: 'data-checked:ltr:translate-x-[22px] data-checked:rtl:-translate-x-[22px]',\n md: 'data-checked:ltr:translate-x-[26px] data-checked:rtl:-translate-x-[26px]',\n lg: 'data-checked:ltr:translate-x-[30px] data-checked:rtl:-translate-x-[30px]',\n};\n\n// Font sizes for track labels (ON/OFF)\nconst labelFontSizes = {\n sm: 'text-[8px]',\n md: 'text-[10px]',\n lg: 'text-xs', // 12px\n};\n\n// Solid style: colored track when checked\nconst solidVariantClasses: Record<ColorVariant, string> = {\n primary: 'data-checked:bg-primary',\n secondary: 'data-checked:bg-secondary',\n warning: 'data-checked:bg-warning',\n info: 'data-checked:bg-info',\n success: 'data-checked:bg-success',\n destructive: 'data-checked:bg-destructive',\n};\n\n// Outline style: colored border when checked\nconst outlineVariantClasses: Record<ColorVariant, string> = {\n primary: 'data-checked:border-primary',\n secondary: 'data-checked:border-secondary',\n warning: 'data-checked:border-warning',\n info: 'data-checked:border-info',\n success: 'data-checked:border-success',\n destructive: 'data-checked:border-destructive',\n};\n\n// Outline style: colored thumb when checked\nconst outlineThumbVariantClasses: Record<ColorVariant, string> = {\n primary: 'group-data-checked/switch:bg-primary',\n secondary: 'group-data-checked/switch:bg-secondary',\n warning: 'group-data-checked/switch:bg-warning',\n info: 'group-data-checked/switch:bg-info',\n success: 'group-data-checked/switch:bg-success',\n destructive: 'group-data-checked/switch:bg-destructive',\n};\n\n// ============================================\n// Theme Variant Constants & Config\n// ============================================\n\nconst themeSizeConfig = {\n sm: {\n gradient: {\n width: 'w-[64px]',\n height: 'h-6',\n indicator: 'size-5',\n icon: 'size-3',\n text: 'text-xs',\n padding: 'p-0.5',\n skeletonWidth: 'w-[64px]',\n skeletonHeight: 'h-6',\n },\n switch: {\n track: 'w-[30px] h-4',\n indicator: 'size-3.5',\n icon: 'size-3.5',\n gap: 'gap-1.5',\n skeletonWidth: 'w-[50px]',\n skeletonHeight: 'h-4',\n },\n pill: {\n width: 'w-[42px]',\n height: 'h-6',\n indicator: 'size-5',\n icon: 'size-3',\n padding: 'p-0.5',\n skeletonWidth: 'w-[42px]',\n skeletonHeight: 'h-6',\n },\n segmented: {\n height: 'h-6',\n segmentHeight: 'h-4',\n icon: 'size-3',\n text: 'text-xs',\n padding: 'p-1',\n gap: 'gap-0.5',\n skeletonWidth: 'w-[100px]',\n skeletonHeight: 'h-6',\n },\n },\n md: {\n gradient: {\n width: 'w-[76px]',\n height: 'h-7',\n indicator: 'size-6',\n icon: 'size-3.5',\n text: 'text-sm',\n padding: 'p-0.5',\n skeletonWidth: 'w-[76px]',\n skeletonHeight: 'h-7',\n },\n switch: {\n track: 'w-9 h-[18px]',\n indicator: 'size-4',\n icon: 'size-4',\n gap: 'gap-2',\n skeletonWidth: 'w-[58px]',\n skeletonHeight: 'h-[18px]',\n },\n pill: {\n width: 'w-[50px]',\n height: 'h-7',\n indicator: 'size-6',\n icon: 'size-3.5',\n padding: 'p-0.5',\n skeletonWidth: 'w-[50px]',\n skeletonHeight: 'h-7',\n },\n segmented: {\n height: 'h-7',\n segmentHeight: 'h-5',\n icon: 'size-3.5',\n text: 'text-sm',\n padding: 'p-1',\n gap: 'gap-1',\n skeletonWidth: 'w-[120px]',\n skeletonHeight: 'h-7',\n },\n },\n lg: {\n gradient: {\n width: 'w-[88px]',\n height: 'h-8',\n indicator: 'size-7',\n icon: 'size-4',\n text: 'text-sm',\n padding: 'p-0.5',\n skeletonWidth: 'w-[88px]',\n skeletonHeight: 'h-8',\n },\n switch: {\n track: 'w-[42px] h-5',\n indicator: 'size-[18px]',\n icon: 'size-[18px]',\n gap: 'gap-2.5',\n skeletonWidth: 'w-[66px]',\n skeletonHeight: 'h-5',\n },\n pill: {\n width: 'w-[58px]',\n height: 'h-8',\n indicator: 'size-7',\n icon: 'size-4',\n padding: 'p-0.5',\n skeletonWidth: 'w-[58px]',\n skeletonHeight: 'h-8',\n },\n segmented: {\n height: 'h-8',\n segmentHeight: 'h-6',\n icon: 'size-4',\n text: 'text-sm',\n padding: 'p-1',\n gap: 'gap-1',\n skeletonWidth: 'w-[140px]',\n skeletonHeight: 'h-8',\n },\n },\n} as const;\n\n// Gradient directions matching Figma design\nconst GRADIENT_LIGHT =\n 'linear-gradient(247deg, var(--destructive) 10%, var(--orange-500) 50%, var(--warning) 90%)';\nconst GRADIENT_DARK =\n 'linear-gradient(264deg, var(--blue-200) 3%, var(--indigo-400) 60%, var(--blue-800) 98%)';\n\n// Shared theme style constants\nconst themeFocusRingClasses =\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2';\nconst themeTransitionClasses =\n 'duration-300 ease-smooth motion-reduce:duration-0';\n\n// ============================================\n// Theme Variant Renderers\n// ============================================\n\nfunction GradientToggle({\n isLight,\n onThemeChange,\n sizes,\n}: {\n isLight: boolean;\n onThemeChange: (theme: 'light' | 'dark') => void;\n sizes: (typeof themeSizeConfig)['md']['gradient'];\n}) {\n return (\n <div\n data-slot=\"switch\"\n className={cn(\n 'relative flex items-center rounded-full transition-all overflow-hidden',\n sizes.width,\n sizes.height,\n sizes.padding,\n themeTransitionClasses,\n )}\n style={{\n background: isLight ? GRADIENT_LIGHT : GRADIENT_DARK,\n }}\n role=\"radiogroup\"\n aria-label=\"Theme selector\"\n >\n {/* Sliding indicator */}\n <div\n className={cn(\n 'absolute flex items-center justify-center rounded-full bg-card shadow-md hover:shadow-lg active:scale-95 transition-all z-10',\n sizes.indicator,\n themeTransitionClasses,\n isLight ? 'left-0.5' : 'right-0.5',\n )}\n >\n {isLight ? (\n <Sun className={cn(sizes.icon, 'text-warning')} />\n ) : (\n <Moon className={cn(sizes.icon, 'text-info')} />\n )}\n </div>\n\n {/* Clickable areas */}\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={isLight}\n aria-label=\"Light theme\"\n onClick={() => onThemeChange('light')}\n className={cn('flex-1 h-full rounded-full z-20', themeFocusRingClasses)}\n />\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={!isLight}\n aria-label=\"Dark theme\"\n onClick={() => onThemeChange('dark')}\n className={cn('flex-1 h-full rounded-full z-20', themeFocusRingClasses)}\n />\n\n {/* Label text */}\n <span\n className={cn(\n 'absolute pointer-events-none font-medium text-white transition-opacity',\n sizes.text,\n themeTransitionClasses,\n isLight ? 'right-2 opacity-100' : 'right-2 opacity-0',\n )}\n >\n Light\n </span>\n <span\n className={cn(\n 'absolute pointer-events-none font-medium text-white transition-opacity',\n sizes.text,\n themeTransitionClasses,\n !isLight ? 'left-2 opacity-100' : 'left-2 opacity-0',\n )}\n >\n Dark\n </span>\n </div>\n );\n}\n\nfunction ThemeSwitchToggle({\n isLight,\n onThemeChange,\n sizes,\n}: {\n isLight: boolean;\n onThemeChange: (theme: 'light' | 'dark') => void;\n sizes: (typeof themeSizeConfig)['md']['switch'];\n}) {\n return (\n <div\n className={cn('flex items-center', sizes.gap)}\n role=\"radiogroup\"\n aria-label=\"Theme selector\"\n data-slot=\"switch\"\n >\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={isLight}\n aria-label={`Switch to ${isLight ? 'dark' : 'light'} theme`}\n onClick={() => onThemeChange(isLight ? 'dark' : 'light')}\n className={cn(\n 'relative rounded-full transition-[colors,box-shadow] hover:shadow-[0_0_0_3px_var(--ring)/0.1] active:scale-[0.97]',\n sizes.track,\n themeTransitionClasses,\n themeFocusRingClasses,\n isLight ? 'bg-input' : 'bg-primary',\n )}\n >\n <div\n className={cn(\n 'absolute top-1/2 -translate-y-1/2 rounded-full shadow-md group-active:shadow-sm transition-all',\n sizes.indicator,\n themeTransitionClasses,\n isLight ? 'bg-card' : 'bg-primary-foreground',\n isLight ? 'left-0.5' : 'right-0.5',\n )}\n />\n </button>\n <div className={cn(sizes.icon, 'text-[var(--muted-foreground)]')}>\n {isLight ? (\n <Moon className=\"size-full\" />\n ) : (\n <Sun className=\"size-full\" />\n )}\n </div>\n </div>\n );\n}\n\nfunction PillToggle({\n isLight,\n onThemeChange,\n sizes,\n}: {\n isLight: boolean;\n onThemeChange: (theme: 'light' | 'dark') => void;\n sizes: (typeof themeSizeConfig)['md']['pill'];\n}) {\n return (\n <div\n className={cn(\n 'relative flex items-center justify-between rounded-full bg-input',\n sizes.width,\n sizes.height,\n sizes.padding,\n )}\n role=\"radiogroup\"\n aria-label=\"Theme selector\"\n data-slot=\"switch\"\n >\n {/* Moon icon (left) */}\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={!isLight}\n aria-label=\"Dark theme\"\n tabIndex={!isLight ? -1 : 0}\n aria-hidden={!isLight ? true : undefined}\n onClick={() => onThemeChange('dark')}\n className={cn(\n 'z-10 flex items-center justify-center rounded-full transition-opacity',\n sizes.indicator,\n themeTransitionClasses,\n themeFocusRingClasses,\n isLight ? 'opacity-50' : 'opacity-0',\n )}\n >\n <Moon className={cn(sizes.icon, 'text-[var(--foreground)]')} />\n </button>\n\n {/* Sun icon (right) */}\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={isLight}\n aria-label=\"Light theme\"\n tabIndex={isLight ? -1 : 0}\n aria-hidden={isLight ? true : undefined}\n onClick={() => onThemeChange('light')}\n className={cn(\n 'z-10 flex items-center justify-center rounded-full transition-opacity',\n sizes.indicator,\n themeTransitionClasses,\n themeFocusRingClasses,\n !isLight ? 'opacity-50' : 'opacity-0',\n )}\n >\n <Sun className={cn(sizes.icon, 'text-[var(--foreground)]')} />\n </button>\n\n {/* Sliding indicator with icon */}\n <div\n className={cn(\n 'absolute top-1/2 -translate-y-1/2 flex items-center justify-center rounded-full bg-card shadow-md hover:shadow-lg active:scale-95 transition-all',\n sizes.indicator,\n themeTransitionClasses,\n isLight ? 'right-0.5' : 'left-0.5',\n )}\n >\n {isLight ? (\n <Sun className={cn(sizes.icon, 'text-warning')} />\n ) : (\n <Moon className={cn(sizes.icon, 'text-info')} />\n )}\n </div>\n </div>\n );\n}\n\nfunction SegmentedToggle({\n isLight,\n onThemeChange,\n sizes,\n}: {\n isLight: boolean;\n onThemeChange: (theme: 'light' | 'dark') => void;\n sizes: (typeof themeSizeConfig)['md']['segmented'];\n}) {\n return (\n <div\n className={cn(\n 'inline-flex items-center rounded bg-input',\n sizes.height,\n sizes.padding,\n sizes.gap,\n )}\n role=\"radiogroup\"\n aria-label=\"Theme selector\"\n data-slot=\"switch\"\n >\n {/* Light segment */}\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={isLight}\n aria-label=\"Light theme\"\n onClick={() => onThemeChange('light')}\n className={cn(\n 'flex items-center justify-center gap-1 rounded px-2 whitespace-nowrap transition-all',\n sizes.segmentHeight,\n sizes.text,\n themeTransitionClasses,\n themeFocusRingClasses,\n isLight\n ? 'bg-card text-foreground shadow-sm'\n : 'text-[var(--foreground)] opacity-50 hover:opacity-70 active:scale-[0.97]',\n )}\n >\n <Sun className={sizes.icon} />\n <span>Light</span>\n </button>\n\n {/* Dark segment */}\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={!isLight}\n aria-label=\"Dark theme\"\n onClick={() => onThemeChange('dark')}\n className={cn(\n 'flex items-center justify-center gap-1 rounded px-2 whitespace-nowrap transition-all',\n sizes.segmentHeight,\n sizes.text,\n themeTransitionClasses,\n themeFocusRingClasses,\n !isLight\n ? 'bg-[var(--primary)] text-[var(--primary-foreground)] shadow-sm'\n : 'text-[var(--foreground)] opacity-50 hover:opacity-70 active:scale-[0.97]',\n )}\n >\n <Moon className={sizes.icon} />\n <span>Dark</span>\n </button>\n </div>\n );\n}\n\n// ============================================\n// Theme Variant Renderer (auto-connects to ThemeProvider)\n// ============================================\n\nconst themeVariantKeyMap: Record<\n ThemeVariant,\n 'gradient' | 'switch' | 'pill' | 'segmented'\n> = {\n 'theme-gradient': 'gradient',\n 'theme-switch': 'switch',\n 'theme-pill': 'pill',\n 'theme-segmented': 'segmented',\n};\n\nconst ThemeVariantRenderer = React.forwardRef<\n HTMLDivElement,\n SwitchControlProps\n>(\n (\n {\n variant,\n size = 'md',\n label,\n orientation = 'vertical',\n checked: controlledChecked,\n onCheckedChange,\n className: classNameProp,\n description: _description,\n labelPosition: _labelPosition,\n switchStyle: _switchStyle,\n withIcon: _withIcon,\n thumbIconOn: _thumbIconOn,\n thumbIconOff: _thumbIconOff,\n showOnOff: _showOnOff,\n onLabel: _onLabel,\n offLabel: _offLabel,\n defaultChecked: _defaultChecked,\n ...restProps\n },\n ref,\n ) => {\n // className from base-ui can be string | function; theme variants only use string\n const className =\n typeof classNameProp === 'function' ? undefined : classNameProp;\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n const themeContext = useThemeOptional();\n const contextTheme = themeContext?.theme;\n const contextSetTheme = themeContext?.setTheme;\n\n // Auto-connect to ThemeProvider when no explicit checked/onCheckedChange\n const isControlled = controlledChecked !== undefined;\n const activeTheme: 'light' | 'dark' = isControlled\n ? controlledChecked\n ? 'dark'\n : 'light'\n : ((contextTheme ?? 'light') as 'light' | 'dark');\n\n const handleThemeChange = React.useCallback(\n (newTheme: 'light' | 'dark') => {\n const newChecked = newTheme === 'dark';\n if (isControlled) {\n onCheckedChange?.(newChecked);\n } else if (contextSetTheme) {\n contextSetTheme(newTheme);\n }\n },\n [isControlled, onCheckedChange, contextSetTheme],\n );\n\n const key = themeVariantKeyMap[variant as ThemeVariant];\n\n const sizes = themeSizeConfig[size][key];\n\n // SSR placeholder - show skeleton during hydration\n if (!mounted && !isControlled) {\n return (\n <div className={cn('inline-flex', className)}>\n <div\n className={cn(\n 'rounded-full bg-[var(--surface-1)] animate-pulse',\n sizes.skeletonWidth,\n sizes.skeletonHeight,\n )}\n role=\"radiogroup\"\n aria-label={typeof label === 'string' ? label : 'Theme selector'}\n />\n </div>\n );\n }\n\n const isLight = activeTheme === 'light';\n\n let toggle: React.ReactNode;\n switch (key) {\n case 'gradient':\n toggle = (\n <GradientToggle\n isLight={isLight}\n onThemeChange={handleThemeChange}\n sizes={sizes as (typeof themeSizeConfig)['md']['gradient']}\n />\n );\n break;\n case 'switch':\n toggle = (\n <ThemeSwitchToggle\n isLight={isLight}\n onThemeChange={handleThemeChange}\n sizes={sizes as (typeof themeSizeConfig)['md']['switch']}\n />\n );\n break;\n case 'pill':\n toggle = (\n <PillToggle\n isLight={isLight}\n onThemeChange={handleThemeChange}\n sizes={sizes as (typeof themeSizeConfig)['md']['pill']}\n />\n );\n break;\n case 'segmented':\n toggle = (\n <SegmentedToggle\n isLight={isLight}\n onThemeChange={handleThemeChange}\n sizes={sizes as (typeof themeSizeConfig)['md']['segmented']}\n />\n );\n break;\n }\n\n // Pick standard HTML attributes safe for div (aria-*, data-*, id)\n const divProps = restProps as React.HTMLAttributes<HTMLDivElement>;\n\n if (!label) {\n return (\n <div ref={ref} className={className} {...divProps}>\n {toggle}\n </div>\n );\n }\n\n return (\n <div\n ref={ref}\n className={cn(\n 'inline-flex',\n orientation === 'vertical'\n ? 'flex-col items-start gap-2'\n : 'flex-row items-center gap-3',\n className,\n )}\n {...divProps}\n >\n <span className=\"text-xs font-semibold text-[var(--muted-foreground)]\">\n {label}\n </span>\n {toggle}\n </div>\n );\n },\n);\n\nThemeVariantRenderer.displayName = 'ThemeVariantRenderer';\n\n// ============================================\n// Main Switch Component\n// ============================================\n\n/** Type guard: is the variant a color variant? */\nfunction isColorVariant(v: SwitchVariant): v is ColorVariant {\n return !v.startsWith('theme-');\n}\n\nconst SwitchControl = React.forwardRef<HTMLButtonElement, SwitchControlProps>(\n (\n {\n label,\n description,\n labelPosition = 'right',\n orientation,\n className,\n id,\n size = 'md',\n variant = 'primary',\n switchStyle = 'solid',\n withIcon,\n thumbIconOn,\n thumbIconOff,\n showOnOff,\n onLabel,\n offLabel,\n checked: controlledChecked,\n defaultChecked = false,\n onCheckedChange,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const switchId = id || generatedId;\n\n const [internalChecked, setInternalChecked] =\n React.useState(defaultChecked);\n const isControlled = controlledChecked !== undefined;\n const checked = isControlled ? controlledChecked : internalChecked;\n\n const handleCheckedChange = React.useCallback(\n (newChecked: boolean) => {\n if (!isControlled) {\n setInternalChecked(newChecked);\n }\n onCheckedChange?.(newChecked);\n },\n [isControlled, onCheckedChange],\n );\n\n // Dispatch to theme variant renderer if applicable\n if (!isColorVariant(variant)) {\n return (\n <ThemeVariantRenderer\n ref={ref as React.Ref<HTMLDivElement>}\n variant={variant}\n size={size}\n label={label}\n orientation={orientation}\n checked={controlledChecked}\n onCheckedChange={onCheckedChange}\n className={className}\n {...props}\n />\n );\n }\n\n // --- Color variant rendering (unchanged) ---\n const colorVariant: ColorVariant = variant;\n\n // Use wider track when labels are present to prevent text cutoff\n const hasLabels = showOnOff || !!onLabel || !!offLabel;\n const activeSizeClasses = hasLabels ? sizeWithLabelClasses : sizeClasses;\n const activeThumbTranslate = hasLabels\n ? thumbTranslateWithLabelClasses\n : thumbTranslateClasses;\n\n // Build track classes based on style\n const trackClasses =\n switchStyle === 'solid'\n ? cn(\n 'data-unchecked:bg-input dark:data-unchecked:bg-input/80 border-transparent',\n solidVariantClasses[colorVariant],\n )\n : cn(\n 'data-unchecked:bg-transparent data-checked:bg-transparent border-input',\n outlineVariantClasses[colorVariant],\n );\n\n // Build thumb classes based on style\n const thumbClasses =\n switchStyle === 'solid'\n ? 'bg-background dark:data-unchecked:bg-foreground dark:data-checked:bg-primary-foreground'\n : cn(\n 'bg-input dark:bg-foreground',\n outlineThumbVariantClasses[colorVariant],\n );\n\n // Thumb icon logic\n const showThumbIcon = withIcon || thumbIconOn || thumbIconOff;\n const checkedIcon = showThumbIcon\n ? thumbIconOn || <Check className={iconSizeClasses[size]} />\n : null;\n const uncheckedIcon = showThumbIcon\n ? thumbIconOff || <X className={iconSizeClasses[size]} />\n : null;\n\n const switchElement = (\n <SwitchPrimitive.Root\n ref={ref}\n id={switchId}\n data-slot=\"switch\"\n data-size={size}\n data-variant={colorVariant}\n data-style={switchStyle}\n className={cn(\n // Base styles\n 'peer group/switch relative inline-flex items-center shrink-0 rounded-full border shadow-xs',\n 'transition-[colors,box-shadow,border-color] duration-200 ease-smooth motion-reduce:transition-none outline-none',\n 'after:absolute after:-inset-x-3 after:-inset-y-[13px]',\n 'data-disabled:cursor-not-allowed data-disabled:opacity-50',\n // Hover: subtle track glow\n 'not-data-disabled:hover:shadow-[0_0_0_3px_var(--ring)/0.08] not-data-disabled:active:scale-[0.97]',\n 'motion-reduce:active:scale-100',\n // Focus styles\n 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n // Invalid styles\n 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 aria-invalid:ring-[3px]',\n // Size classes (wider when labels are shown)\n activeSizeClasses[size],\n // Variant/style classes\n trackClasses,\n className,\n )}\n checked={checked}\n onCheckedChange={handleCheckedChange}\n {...props}\n >\n {hasLabels && (\n <>\n <span\n aria-hidden=\"true\"\n className={cn(\n 'absolute font-semibold transition-opacity duration-200 ease-smooth motion-reduce:transition-none',\n labelFontSizes[size],\n 'start-1.5',\n checked ? 'opacity-100' : 'opacity-0',\n switchStyle === 'solid'\n ? 'text-primary-foreground'\n : 'text-current',\n )}\n >\n {onLabel || 'ON'}\n </span>\n <span\n aria-hidden=\"true\"\n className={cn(\n 'absolute font-semibold transition-opacity duration-200 ease-smooth motion-reduce:transition-none',\n labelFontSizes[size],\n 'end-1.5',\n checked ? 'opacity-0' : 'opacity-100',\n switchStyle === 'solid'\n ? 'text-muted-foreground'\n : 'text-current',\n )}\n >\n {offLabel || 'OFF'}\n </span>\n </>\n )}\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className={cn(\n 'pointer-events-none block rounded-full ring-0',\n 'shadow-sm group-hover/switch:shadow-md group-active/switch:shadow-sm',\n 'transition-[transform,box-shadow,background-color] duration-200 motion-reduce:transition-none',\n 'ease-smooth',\n 'group-active/switch:scale-95',\n 'data-unchecked:translate-x-0',\n // Size classes\n thumbSizeClasses[size],\n // Translation classes (wider when labels are shown)\n activeThumbTranslate[size],\n // Color classes based on style\n thumbClasses,\n // Flex for centering icons\n showThumbIcon && 'flex items-center justify-center',\n )}\n >\n {showThumbIcon && (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'flex items-center justify-center transition-colors duration-200 ease-smooth motion-reduce:transition-none',\n // Icon colors based on style\n switchStyle === 'solid'\n ? 'text-muted-foreground group-data-checked/switch:text-primary'\n : 'text-background group-data-checked/switch:text-primary-foreground',\n )}\n >\n {checked ? checkedIcon : uncheckedIcon}\n </span>\n )}\n </SwitchPrimitive.Thumb>\n </SwitchPrimitive.Root>\n );\n\n if (!label) {\n return switchElement;\n }\n\n return (\n <div\n className={cn(\n 'flex items-center gap-3',\n labelPosition === 'left' && 'flex-row-reverse justify-end',\n )}\n >\n {switchElement}\n <div className=\"flex flex-col gap-0.5\">\n <label\n htmlFor={switchId}\n className=\"text-sm font-medium leading-none cursor-pointer peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n >\n {label}\n </label>\n {description && (\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n )}\n </div>\n </div>\n );\n },\n);\n\nSwitchControl.displayName = 'SwitchControl';\n\n// =============================================================================\n// Switch — public API with smart field mode (inline layout)\n// =============================================================================\n\nconst Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(\n (props, ref) => {\n // Extract field-specific props (error, tooltip, required, labelIcon, fieldClassName)\n // label and description are shared — we handle them manually\n const {\n label,\n description,\n error,\n tooltip,\n required,\n labelIcon,\n fieldClassName,\n ...controlProps\n } = props;\n\n const isFieldMode = !!(error || tooltip || required || labelIcon);\n\n if (!isFieldMode) {\n // Bare mode: pass label/description to control for inline rendering\n return (\n <SwitchControl\n ref={ref}\n label={label}\n description={description as string}\n {...controlProps}\n />\n );\n }\n\n // Field mode: label goes to FieldLabel, switch renders bare\n return (\n <Field\n layout=\"inline\"\n state={error ? 'destructive' : undefined}\n disabled={controlProps.disabled}\n className={fieldClassName}\n >\n <SwitchControl ref={ref} {...controlProps} />\n {label && (\n <FieldLabel required={required} labelIcon={labelIcon}>\n {label}\n {tooltip && <FieldTooltip content={tooltip} />}\n </FieldLabel>\n )}\n {(error || description) && (\n <FieldBottom>\n {error && <FieldError>{error}</FieldError>}\n {!error && description && (\n <FieldDescription>{description}</FieldDescription>\n )}\n </FieldBottom>\n )}\n </Field>\n );\n },\n);\nSwitch.displayName = 'Switch';\n\nexport { Switch };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index39.js","names":[],"sources":["../src/components/atoms/forms/Switch/Switch.tsx"],"sourcesContent":["/** biome-ignore-all lint/a11y/useSemanticElements: Theme toggles use button-based radio controls for animated segmented UI while preserving radiogroup semantics. */\n'use client';\n\nimport { Switch as SwitchPrimitive } from '@base-ui/react/switch';\nimport { Check, Moon, Sun, X } from 'lucide-react';\nimport * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport {\n type FieldProps,\n FieldTooltip,\n} from '@/components/atoms/forms/_shared';\nimport { useThemeOptional } from '@/components/ThemeProvider';\nimport {\n Field,\n FieldBottom,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from '@/components/ui/form-field';\nimport { cn } from '@/lib/utils';\n\n// ============================================\n// Types\n// ============================================\n\n/** Color variants for the standard switch */\ntype ColorVariant =\n | 'primary'\n | 'secondary'\n | 'warning'\n | 'info'\n | 'success'\n | 'destructive';\n\n/** Theme variants that render theme toggle UIs */\ntype ThemeVariant =\n | 'theme-gradient'\n | 'theme-switch'\n | 'theme-pill'\n | 'theme-segmented';\n\n/** All switch variants */\nexport type SwitchVariant = ColorVariant | ThemeVariant;\n\n// ============================================\n// Switch (with controlled/uncontrolled support)\n// ============================================\n\ninterface SwitchControlProps\n extends Omit<SwitchPrimitive.Root.Props, 'onCheckedChange'> {\n // Variants\n size?: 'sm' | 'md' | 'lg';\n variant?: SwitchVariant;\n /** Visual style of the switch (solid = filled track, outline = bordered track). Only applies to color variants. */\n switchStyle?: 'solid' | 'outline';\n\n // Thumb icons (color variants only)\n withIcon?: boolean;\n thumbIconOn?: React.ReactNode;\n thumbIconOff?: React.ReactNode;\n\n // Track labels (ON/OFF inside track, color variants only)\n showOnOff?: boolean;\n onLabel?: string;\n offLabel?: string;\n\n // External label\n label?: React.ReactNode;\n description?: string;\n labelPosition?: 'left' | 'right';\n\n /** Layout orientation for label (theme variants only) */\n orientation?: 'horizontal' | 'vertical';\n\n // Core (keep existing)\n checked?: boolean;\n defaultChecked?: boolean;\n onCheckedChange?: (checked: boolean) => void;\n}\n\nexport interface SwitchProps\n extends Omit<SwitchControlProps, 'label' | 'description'>,\n Omit<FieldProps, 'label' | 'description' | 'topRight' | 'bottomRight'> {\n /** Label text or ReactNode (inline with switch, or in FieldLabel when field props are set) */\n label?: React.ReactNode;\n /** Description text (inline below label, or in FieldDescription when field props are set) */\n description?: React.ReactNode;\n}\n\n// ============================================\n// Color Switch Constants\n// ============================================\n\n// Icon size classes for thumb icons\nconst iconSizeClasses = {\n sm: 'size-2', // 8px\n md: 'size-2.5', // 10px\n lg: 'size-3', // 12px\n};\n\n// Size classes for the track\nconst sizeClasses = {\n sm: 'h-[18px] w-[32px]',\n md: 'h-[22px] w-[40px]',\n lg: 'h-[26px] w-[48px]',\n};\n\n// Size classes for the thumb\nconst thumbSizeClasses = {\n sm: 'size-[14px]',\n md: 'size-[18px]',\n lg: 'size-[22px]',\n};\n\n// Thumb translation when checked (track width - thumb size - 2px padding on each side)\nconst thumbTranslateClasses = {\n sm: 'data-checked:ltr:translate-x-[14px] data-checked:rtl:-translate-x-[14px]',\n md: 'data-checked:ltr:translate-x-[18px] data-checked:rtl:-translate-x-[18px]',\n lg: 'data-checked:ltr:translate-x-[22px] data-checked:rtl:-translate-x-[22px]',\n};\n\n// Wider track sizes when ON/OFF labels are shown (extra space for text)\nconst sizeWithLabelClasses = {\n sm: 'h-[18px] w-[40px]',\n md: 'h-[22px] w-[48px]',\n lg: 'h-[26px] w-[56px]',\n};\n\n// Thumb translation for wider tracks with labels\nconst thumbTranslateWithLabelClasses = {\n sm: 'data-checked:ltr:translate-x-[22px] data-checked:rtl:-translate-x-[22px]',\n md: 'data-checked:ltr:translate-x-[26px] data-checked:rtl:-translate-x-[26px]',\n lg: 'data-checked:ltr:translate-x-[30px] data-checked:rtl:-translate-x-[30px]',\n};\n\n// Font sizes for track labels (ON/OFF)\nconst labelFontSizes = {\n sm: 'text-[8px]',\n md: 'text-[10px]',\n lg: 'text-xs', // 12px\n};\n\n// Solid style: colored track when checked\nconst solidVariantClasses: Record<ColorVariant, string> = {\n primary: 'data-checked:bg-primary',\n secondary: 'data-checked:bg-secondary',\n warning: 'data-checked:bg-warning',\n info: 'data-checked:bg-info',\n success: 'data-checked:bg-success',\n destructive: 'data-checked:bg-destructive',\n};\n\n// Outline style: colored border when checked\nconst outlineVariantClasses: Record<ColorVariant, string> = {\n primary: 'data-checked:border-primary',\n secondary: 'data-checked:border-secondary',\n warning: 'data-checked:border-warning',\n info: 'data-checked:border-info',\n success: 'data-checked:border-success',\n destructive: 'data-checked:border-destructive',\n};\n\n// Outline style: colored thumb when checked\nconst outlineThumbVariantClasses: Record<ColorVariant, string> = {\n primary: 'group-data-checked/switch:bg-primary',\n secondary: 'group-data-checked/switch:bg-secondary',\n warning: 'group-data-checked/switch:bg-warning',\n info: 'group-data-checked/switch:bg-info',\n success: 'group-data-checked/switch:bg-success',\n destructive: 'group-data-checked/switch:bg-destructive',\n};\n\n// ============================================\n// Theme Variant Constants & Config\n// ============================================\n\nconst themeSizeConfig = {\n sm: {\n gradient: {\n width: 'w-[64px]',\n height: 'h-6',\n indicator: 'size-5',\n icon: 'size-3',\n text: 'text-xs',\n padding: 'p-0.5',\n skeletonWidth: 'w-[64px]',\n skeletonHeight: 'h-6',\n },\n switch: {\n track: 'w-[30px] h-4',\n indicator: 'size-3.5',\n icon: 'size-3.5',\n gap: 'gap-1.5',\n skeletonWidth: 'w-[50px]',\n skeletonHeight: 'h-4',\n },\n pill: {\n width: 'w-[42px]',\n height: 'h-6',\n indicator: 'size-5',\n icon: 'size-3',\n padding: 'p-0.5',\n skeletonWidth: 'w-[42px]',\n skeletonHeight: 'h-6',\n },\n segmented: {\n height: 'h-6',\n segmentHeight: 'h-4',\n icon: 'size-3',\n text: 'text-xs',\n padding: 'p-1',\n gap: 'gap-0.5',\n skeletonWidth: 'w-[100px]',\n skeletonHeight: 'h-6',\n },\n },\n md: {\n gradient: {\n width: 'w-[76px]',\n height: 'h-7',\n indicator: 'size-6',\n icon: 'size-3.5',\n text: 'text-sm',\n padding: 'p-0.5',\n skeletonWidth: 'w-[76px]',\n skeletonHeight: 'h-7',\n },\n switch: {\n track: 'w-9 h-[18px]',\n indicator: 'size-4',\n icon: 'size-4',\n gap: 'gap-2',\n skeletonWidth: 'w-[58px]',\n skeletonHeight: 'h-[18px]',\n },\n pill: {\n width: 'w-[50px]',\n height: 'h-7',\n indicator: 'size-6',\n icon: 'size-3.5',\n padding: 'p-0.5',\n skeletonWidth: 'w-[50px]',\n skeletonHeight: 'h-7',\n },\n segmented: {\n height: 'h-7',\n segmentHeight: 'h-5',\n icon: 'size-3.5',\n text: 'text-sm',\n padding: 'p-1',\n gap: 'gap-1',\n skeletonWidth: 'w-[120px]',\n skeletonHeight: 'h-7',\n },\n },\n lg: {\n gradient: {\n width: 'w-[88px]',\n height: 'h-8',\n indicator: 'size-7',\n icon: 'size-4',\n text: 'text-sm',\n padding: 'p-0.5',\n skeletonWidth: 'w-[88px]',\n skeletonHeight: 'h-8',\n },\n switch: {\n track: 'w-[42px] h-5',\n indicator: 'size-[18px]',\n icon: 'size-[18px]',\n gap: 'gap-2.5',\n skeletonWidth: 'w-[66px]',\n skeletonHeight: 'h-5',\n },\n pill: {\n width: 'w-[58px]',\n height: 'h-8',\n indicator: 'size-7',\n icon: 'size-4',\n padding: 'p-0.5',\n skeletonWidth: 'w-[58px]',\n skeletonHeight: 'h-8',\n },\n segmented: {\n height: 'h-8',\n segmentHeight: 'h-6',\n icon: 'size-4',\n text: 'text-sm',\n padding: 'p-1',\n gap: 'gap-1',\n skeletonWidth: 'w-[140px]',\n skeletonHeight: 'h-8',\n },\n },\n} as const;\n\n// Gradient directions matching Figma design\nconst GRADIENT_LIGHT =\n 'linear-gradient(247deg, var(--destructive) 10%, var(--orange-500) 50%, var(--warning) 90%)';\nconst GRADIENT_DARK =\n 'linear-gradient(264deg, var(--blue-200) 3%, var(--indigo-400) 60%, var(--blue-800) 98%)';\n\n// Shared theme style constants\nconst themeFocusRingClasses =\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2';\nconst themeTransitionClasses =\n 'duration-300 ease-smooth motion-reduce:duration-0';\n\n// ============================================\n// Theme Variant Renderers\n// ============================================\n\nfunction GradientToggle({\n isLight,\n onThemeChange,\n sizes,\n}: {\n isLight: boolean;\n onThemeChange: (theme: 'light' | 'dark') => void;\n sizes: (typeof themeSizeConfig)['md']['gradient'];\n}) {\n return (\n <div\n data-slot=\"switch\"\n className={cn(\n 'relative flex items-center rounded-full transition-all overflow-hidden',\n sizes.width,\n sizes.height,\n sizes.padding,\n themeTransitionClasses,\n )}\n style={{\n background: isLight ? GRADIENT_LIGHT : GRADIENT_DARK,\n }}\n role=\"radiogroup\"\n aria-label=\"Theme selector\"\n >\n {/* Sliding indicator */}\n <div\n className={cn(\n 'absolute flex items-center justify-center rounded-full bg-card shadow-md hover:shadow-lg active:scale-95 transition-all z-10',\n sizes.indicator,\n themeTransitionClasses,\n isLight ? 'left-0.5' : 'right-0.5',\n )}\n >\n {isLight ? (\n <Sun className={cn(sizes.icon, 'text-warning')} />\n ) : (\n <Moon className={cn(sizes.icon, 'text-info')} />\n )}\n </div>\n\n {/* Clickable areas */}\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={isLight}\n aria-label=\"Light theme\"\n onClick={() => onThemeChange('light')}\n className={cn('flex-1 h-full rounded-full z-20', themeFocusRingClasses)}\n />\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={!isLight}\n aria-label=\"Dark theme\"\n onClick={() => onThemeChange('dark')}\n className={cn('flex-1 h-full rounded-full z-20', themeFocusRingClasses)}\n />\n\n {/* Label text */}\n <span\n className={cn(\n 'absolute pointer-events-none font-medium text-white transition-opacity',\n sizes.text,\n themeTransitionClasses,\n isLight ? 'right-2 opacity-100' : 'right-2 opacity-0',\n )}\n >\n Light\n </span>\n <span\n className={cn(\n 'absolute pointer-events-none font-medium text-white transition-opacity',\n sizes.text,\n themeTransitionClasses,\n !isLight ? 'left-2 opacity-100' : 'left-2 opacity-0',\n )}\n >\n Dark\n </span>\n </div>\n );\n}\n\nfunction ThemeSwitchToggle({\n isLight,\n onThemeChange,\n sizes,\n}: {\n isLight: boolean;\n onThemeChange: (theme: 'light' | 'dark') => void;\n sizes: (typeof themeSizeConfig)['md']['switch'];\n}) {\n return (\n <div\n className={cn('flex items-center', sizes.gap)}\n role=\"radiogroup\"\n aria-label=\"Theme selector\"\n data-slot=\"switch\"\n >\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={isLight}\n aria-label={`Switch to ${isLight ? 'dark' : 'light'} theme`}\n onClick={() => onThemeChange(isLight ? 'dark' : 'light')}\n className={cn(\n 'relative rounded-full transition-[colors,box-shadow] hover:shadow-[0_0_0_3px_var(--ring)/0.1] active:scale-[0.97]',\n sizes.track,\n themeTransitionClasses,\n themeFocusRingClasses,\n isLight ? 'bg-input' : 'bg-primary',\n )}\n >\n <div\n className={cn(\n 'absolute top-1/2 -translate-y-1/2 rounded-full shadow-md group-active:shadow-sm transition-all',\n sizes.indicator,\n themeTransitionClasses,\n isLight ? 'bg-card' : 'bg-background',\n isLight ? 'left-0.5' : 'right-0.5',\n )}\n />\n </button>\n <div className={cn(sizes.icon, 'text-[var(--muted-foreground)]')}>\n {isLight ? (\n <Moon className=\"size-full\" />\n ) : (\n <Sun className=\"size-full\" />\n )}\n </div>\n </div>\n );\n}\n\nfunction PillToggle({\n isLight,\n onThemeChange,\n sizes,\n}: {\n isLight: boolean;\n onThemeChange: (theme: 'light' | 'dark') => void;\n sizes: (typeof themeSizeConfig)['md']['pill'];\n}) {\n return (\n <div\n className={cn(\n 'relative flex items-center justify-between rounded-full bg-input',\n sizes.width,\n sizes.height,\n sizes.padding,\n )}\n role=\"radiogroup\"\n aria-label=\"Theme selector\"\n data-slot=\"switch\"\n >\n {/* Moon icon (left) */}\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={!isLight}\n aria-label=\"Dark theme\"\n tabIndex={!isLight ? -1 : 0}\n aria-hidden={!isLight ? true : undefined}\n onClick={() => onThemeChange('dark')}\n className={cn(\n 'z-10 flex items-center justify-center rounded-full transition-opacity',\n sizes.indicator,\n themeTransitionClasses,\n themeFocusRingClasses,\n isLight ? 'opacity-50' : 'opacity-0',\n )}\n >\n <Moon className={cn(sizes.icon, 'text-[var(--foreground)]')} />\n </button>\n\n {/* Sun icon (right) */}\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={isLight}\n aria-label=\"Light theme\"\n tabIndex={isLight ? -1 : 0}\n aria-hidden={isLight ? true : undefined}\n onClick={() => onThemeChange('light')}\n className={cn(\n 'z-10 flex items-center justify-center rounded-full transition-opacity',\n sizes.indicator,\n themeTransitionClasses,\n themeFocusRingClasses,\n !isLight ? 'opacity-50' : 'opacity-0',\n )}\n >\n <Sun className={cn(sizes.icon, 'text-[var(--foreground)]')} />\n </button>\n\n {/* Sliding indicator with icon */}\n <div\n className={cn(\n 'absolute top-1/2 -translate-y-1/2 flex items-center justify-center rounded-full bg-card shadow-md hover:shadow-lg active:scale-95 transition-all',\n sizes.indicator,\n themeTransitionClasses,\n isLight ? 'right-0.5' : 'left-0.5',\n )}\n >\n {isLight ? (\n <Sun className={cn(sizes.icon, 'text-warning')} />\n ) : (\n <Moon className={cn(sizes.icon, 'text-info')} />\n )}\n </div>\n </div>\n );\n}\n\nfunction SegmentedToggle({\n isLight,\n onThemeChange,\n sizes,\n}: {\n isLight: boolean;\n onThemeChange: (theme: 'light' | 'dark') => void;\n sizes: (typeof themeSizeConfig)['md']['segmented'];\n}) {\n return (\n <div\n className={cn(\n 'inline-flex items-center rounded bg-input',\n sizes.height,\n sizes.padding,\n sizes.gap,\n )}\n role=\"radiogroup\"\n aria-label=\"Theme selector\"\n data-slot=\"switch\"\n >\n {/* Light segment */}\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={isLight}\n aria-label=\"Light theme\"\n onClick={() => onThemeChange('light')}\n className={cn(\n 'flex items-center justify-center gap-1 rounded px-2 whitespace-nowrap transition-all',\n sizes.segmentHeight,\n sizes.text,\n themeTransitionClasses,\n themeFocusRingClasses,\n isLight\n ? 'bg-card text-foreground shadow-sm'\n : 'text-[var(--foreground)] opacity-50 hover:opacity-70 active:scale-[0.97]',\n )}\n >\n <Sun className={sizes.icon} />\n <span>Light</span>\n </button>\n\n {/* Dark segment */}\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={!isLight}\n aria-label=\"Dark theme\"\n onClick={() => onThemeChange('dark')}\n className={cn(\n 'flex items-center justify-center gap-1 rounded px-2 whitespace-nowrap transition-all',\n sizes.segmentHeight,\n sizes.text,\n themeTransitionClasses,\n themeFocusRingClasses,\n !isLight\n ? 'bg-[var(--primary)] text-[var(--primary-foreground)] shadow-sm'\n : 'text-[var(--foreground)] opacity-50 hover:opacity-70 active:scale-[0.97]',\n )}\n >\n <Moon className={sizes.icon} />\n <span>Dark</span>\n </button>\n </div>\n );\n}\n\n// ============================================\n// Theme Variant Renderer (auto-connects to ThemeProvider)\n// ============================================\n\nconst themeVariantKeyMap: Record<\n ThemeVariant,\n 'gradient' | 'switch' | 'pill' | 'segmented'\n> = {\n 'theme-gradient': 'gradient',\n 'theme-switch': 'switch',\n 'theme-pill': 'pill',\n 'theme-segmented': 'segmented',\n};\n\nconst ThemeVariantRenderer = React.forwardRef<\n HTMLDivElement,\n SwitchControlProps\n>(\n (\n {\n variant,\n size = 'md',\n label,\n orientation = 'vertical',\n checked: controlledChecked,\n onCheckedChange,\n className: classNameProp,\n description: _description,\n labelPosition: _labelPosition,\n switchStyle: _switchStyle,\n withIcon: _withIcon,\n thumbIconOn: _thumbIconOn,\n thumbIconOff: _thumbIconOff,\n showOnOff: _showOnOff,\n onLabel: _onLabel,\n offLabel: _offLabel,\n defaultChecked: _defaultChecked,\n ...restProps\n },\n ref,\n ) => {\n // className from base-ui can be string | function; theme variants only use string\n const className =\n typeof classNameProp === 'function' ? undefined : classNameProp;\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n const themeContext = useThemeOptional();\n const contextTheme = themeContext?.theme;\n const contextSetTheme = themeContext?.setTheme;\n\n // Auto-connect to ThemeProvider when no explicit checked/onCheckedChange\n const isControlled = controlledChecked !== undefined;\n const activeTheme: 'light' | 'dark' = isControlled\n ? controlledChecked\n ? 'dark'\n : 'light'\n : ((contextTheme ?? 'light') as 'light' | 'dark');\n\n const handleThemeChange = React.useCallback(\n (newTheme: 'light' | 'dark') => {\n const newChecked = newTheme === 'dark';\n if (isControlled) {\n onCheckedChange?.(newChecked);\n } else if (contextSetTheme) {\n contextSetTheme(newTheme);\n }\n },\n [isControlled, onCheckedChange, contextSetTheme],\n );\n\n const key = themeVariantKeyMap[variant as ThemeVariant];\n\n const sizes = themeSizeConfig[size][key];\n\n // SSR placeholder - show skeleton during hydration\n if (!mounted && !isControlled) {\n return (\n <div className={cn('inline-flex', className)}>\n <div\n className={cn(\n 'rounded-full bg-[var(--surface-1)] animate-pulse',\n sizes.skeletonWidth,\n sizes.skeletonHeight,\n )}\n role=\"radiogroup\"\n aria-label={typeof label === 'string' ? label : 'Theme selector'}\n />\n </div>\n );\n }\n\n const isLight = activeTheme === 'light';\n\n let toggle: React.ReactNode;\n switch (key) {\n case 'gradient':\n toggle = (\n <GradientToggle\n isLight={isLight}\n onThemeChange={handleThemeChange}\n sizes={sizes as (typeof themeSizeConfig)['md']['gradient']}\n />\n );\n break;\n case 'switch':\n toggle = (\n <ThemeSwitchToggle\n isLight={isLight}\n onThemeChange={handleThemeChange}\n sizes={sizes as (typeof themeSizeConfig)['md']['switch']}\n />\n );\n break;\n case 'pill':\n toggle = (\n <PillToggle\n isLight={isLight}\n onThemeChange={handleThemeChange}\n sizes={sizes as (typeof themeSizeConfig)['md']['pill']}\n />\n );\n break;\n case 'segmented':\n toggle = (\n <SegmentedToggle\n isLight={isLight}\n onThemeChange={handleThemeChange}\n sizes={sizes as (typeof themeSizeConfig)['md']['segmented']}\n />\n );\n break;\n }\n\n // Pick standard HTML attributes safe for div (aria-*, data-*, id)\n const divProps = restProps as React.HTMLAttributes<HTMLDivElement>;\n\n if (!label) {\n return (\n <div ref={ref} className={className} {...divProps}>\n {toggle}\n </div>\n );\n }\n\n return (\n <div\n ref={ref}\n className={cn(\n 'inline-flex',\n orientation === 'vertical'\n ? 'flex-col items-start gap-2'\n : 'flex-row items-center gap-3',\n className,\n )}\n {...divProps}\n >\n <span className=\"text-xs font-semibold text-[var(--muted-foreground)]\">\n {label}\n </span>\n {toggle}\n </div>\n );\n },\n);\n\nThemeVariantRenderer.displayName = 'ThemeVariantRenderer';\n\n// ============================================\n// Main Switch Component\n// ============================================\n\n/** Type guard: is the variant a color variant? */\nfunction isColorVariant(v: SwitchVariant): v is ColorVariant {\n return !v.startsWith('theme-');\n}\n\nconst SwitchControl = React.forwardRef<HTMLButtonElement, SwitchControlProps>(\n (\n {\n label,\n description,\n labelPosition = 'right',\n orientation,\n className,\n id,\n size = 'md',\n variant = 'primary',\n switchStyle = 'solid',\n withIcon,\n thumbIconOn,\n thumbIconOff,\n showOnOff,\n onLabel,\n offLabel,\n checked: controlledChecked,\n defaultChecked = false,\n onCheckedChange,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const switchId = id || generatedId;\n\n const [internalChecked, setInternalChecked] =\n React.useState(defaultChecked);\n const isControlled = controlledChecked !== undefined;\n const checked = isControlled ? controlledChecked : internalChecked;\n\n const handleCheckedChange = React.useCallback(\n (newChecked: boolean) => {\n if (!isControlled) {\n setInternalChecked(newChecked);\n }\n onCheckedChange?.(newChecked);\n },\n [isControlled, onCheckedChange],\n );\n\n // Dispatch to theme variant renderer if applicable\n if (!isColorVariant(variant)) {\n return (\n <ThemeVariantRenderer\n ref={ref as React.Ref<HTMLDivElement>}\n variant={variant}\n size={size}\n label={label}\n orientation={orientation}\n checked={controlledChecked}\n onCheckedChange={onCheckedChange}\n className={className}\n {...props}\n />\n );\n }\n\n // --- Color variant rendering (unchanged) ---\n const colorVariant: ColorVariant = variant;\n\n // Use wider track when labels are present to prevent text cutoff\n const hasLabels = showOnOff || !!onLabel || !!offLabel;\n const activeSizeClasses = hasLabels ? sizeWithLabelClasses : sizeClasses;\n const activeThumbTranslate = hasLabels\n ? thumbTranslateWithLabelClasses\n : thumbTranslateClasses;\n\n // Build track classes based on style\n const trackClasses =\n switchStyle === 'solid'\n ? cn(\n 'data-unchecked:bg-input dark:data-unchecked:bg-input/80 border-transparent',\n solidVariantClasses[colorVariant],\n )\n : cn(\n 'data-unchecked:bg-transparent data-checked:bg-transparent border-input',\n outlineVariantClasses[colorVariant],\n );\n\n // Build thumb classes based on style\n const thumbClasses =\n switchStyle === 'solid'\n ? 'bg-background dark:data-unchecked:bg-foreground dark:data-checked:bg-background'\n : cn(\n 'bg-input dark:bg-foreground',\n outlineThumbVariantClasses[colorVariant],\n );\n\n // Thumb icon logic\n const showThumbIcon = withIcon || thumbIconOn || thumbIconOff;\n const checkedIcon = showThumbIcon\n ? thumbIconOn || <Check className={iconSizeClasses[size]} />\n : null;\n const uncheckedIcon = showThumbIcon\n ? thumbIconOff || <X className={iconSizeClasses[size]} />\n : null;\n\n const switchElement = (\n <SwitchPrimitive.Root\n ref={ref}\n id={switchId}\n data-slot=\"switch\"\n data-size={size}\n data-variant={colorVariant}\n data-style={switchStyle}\n className={cn(\n // Base styles\n 'peer group/switch relative inline-flex items-center shrink-0 rounded-full border shadow-xs',\n 'transition-[colors,box-shadow,border-color] duration-200 ease-smooth motion-reduce:transition-none outline-none',\n 'after:absolute after:-inset-x-3 after:-inset-y-[13px]',\n 'data-disabled:cursor-not-allowed data-disabled:opacity-50',\n // Hover: subtle track glow\n 'not-data-disabled:hover:shadow-[0_0_0_3px_var(--ring)/0.08] not-data-disabled:active:scale-[0.97]',\n 'motion-reduce:active:scale-100',\n // Focus styles\n 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n // Invalid styles\n 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 aria-invalid:ring-[3px]',\n // Size classes (wider when labels are shown)\n activeSizeClasses[size],\n // Variant/style classes\n trackClasses,\n className,\n )}\n checked={checked}\n onCheckedChange={handleCheckedChange}\n {...props}\n >\n {hasLabels && (\n <>\n <span\n aria-hidden=\"true\"\n className={cn(\n 'absolute font-semibold transition-opacity duration-200 ease-smooth motion-reduce:transition-none',\n labelFontSizes[size],\n 'start-1.5',\n checked ? 'opacity-100' : 'opacity-0',\n switchStyle === 'solid'\n ? 'text-primary-foreground'\n : 'text-current',\n )}\n >\n {onLabel || 'ON'}\n </span>\n <span\n aria-hidden=\"true\"\n className={cn(\n 'absolute font-semibold transition-opacity duration-200 ease-smooth motion-reduce:transition-none',\n labelFontSizes[size],\n 'end-1.5',\n checked ? 'opacity-0' : 'opacity-100',\n switchStyle === 'solid'\n ? 'text-muted-foreground'\n : 'text-current',\n )}\n >\n {offLabel || 'OFF'}\n </span>\n </>\n )}\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className={cn(\n 'pointer-events-none block rounded-full ring-0',\n 'shadow-sm group-hover/switch:shadow-md group-active/switch:shadow-sm',\n 'transition-[transform,box-shadow,background-color] duration-200 motion-reduce:transition-none',\n 'ease-smooth',\n 'group-active/switch:scale-95',\n 'data-unchecked:translate-x-0',\n // Size classes\n thumbSizeClasses[size],\n // Translation classes (wider when labels are shown)\n activeThumbTranslate[size],\n // Color classes based on style\n thumbClasses,\n // Flex for centering icons\n showThumbIcon && 'flex items-center justify-center',\n )}\n >\n {showThumbIcon && (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'flex items-center justify-center transition-colors duration-200 ease-smooth motion-reduce:transition-none',\n // Icon colors based on style\n switchStyle === 'solid'\n ? 'text-muted-foreground group-data-checked/switch:text-primary'\n : 'text-background group-data-checked/switch:text-primary-foreground',\n )}\n >\n {checked ? checkedIcon : uncheckedIcon}\n </span>\n )}\n </SwitchPrimitive.Thumb>\n </SwitchPrimitive.Root>\n );\n\n if (!label) {\n return switchElement;\n }\n\n return (\n <div\n className={cn(\n 'flex items-center gap-3',\n labelPosition === 'left' && 'flex-row-reverse justify-end',\n )}\n >\n {switchElement}\n <div className=\"flex flex-col gap-0.5\">\n <label\n htmlFor={switchId}\n className=\"text-sm font-medium leading-none cursor-pointer peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n >\n {label}\n </label>\n {description && (\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n )}\n </div>\n </div>\n );\n },\n);\n\nSwitchControl.displayName = 'SwitchControl';\n\n// =============================================================================\n// Switch — public API with smart field mode (inline layout)\n// =============================================================================\n\nconst Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(\n (props, ref) => {\n // Extract field-specific props (error, tooltip, required, labelIcon, fieldClassName)\n // label and description are shared — we handle them manually\n const {\n label,\n description,\n error,\n tooltip,\n required,\n labelIcon,\n fieldClassName,\n ...controlProps\n } = props;\n\n const isFieldMode = !!(error || tooltip || required || labelIcon);\n\n if (!isFieldMode) {\n // Bare mode: pass label/description to control for inline rendering\n return (\n <SwitchControl\n ref={ref}\n label={label}\n description={description as string}\n {...controlProps}\n />\n );\n }\n\n // Field mode: label goes to FieldLabel, switch renders bare\n return (\n <Field\n layout=\"inline\"\n state={error ? 'destructive' : undefined}\n disabled={controlProps.disabled}\n className={fieldClassName}\n >\n <SwitchControl ref={ref} {...controlProps} />\n {label && (\n <FieldLabel required={required} labelIcon={labelIcon}>\n {label}\n {tooltip && <FieldTooltip content={tooltip} />}\n </FieldLabel>\n )}\n {(error || description) && (\n <FieldBottom>\n {error && <FieldError>{error}</FieldError>}\n {!error && description && (\n <FieldDescription>{description}</FieldDescription>\n )}\n </FieldBottom>\n )}\n </Field>\n );\n },\n);\nSwitch.displayName = 'Switch';\n\nexport { Switch };\n"],"mappings":"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"}