Fortura/apps/www/public/r/base-toggle-group.json
2025-08-20 04:12:49 -06:00

19 lines
6.2 KiB
JSON

{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "base-toggle-group",
"type": "registry:ui",
"title": "Base Toggle Group",
"description": "Provides a shared state to a series of toggle buttons.",
"dependencies": [
"motion",
"@base-ui-components/react"
],
"files": [
{
"path": "registry/base/toggle-group/index.tsx",
"content": "'use client';\n\nimport * as React from 'react';\nimport { ToggleGroup as ToggleGroupPrimitive } from '@base-ui-components/react/toggle-group';\nimport { Toggle as TogglePrimitive } from '@base-ui-components/react/toggle';\nimport {\n type HTMLMotionProps,\n type Transition,\n motion,\n AnimatePresence,\n} from 'motion/react';\nimport { cva, type VariantProps } from 'class-variance-authority';\n\nimport { cn } from '@/lib/utils';\n\nconst toggleVariants = cva(\n \"cursor-pointer inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:text-muted-foreground text-accent-foreground transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 data-[pressed]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none focus:outline-none aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap\",\n {\n variants: {\n type: {\n single: '',\n multiple: 'data-[pressed]:bg-accent',\n },\n variant: {\n default: 'bg-transparent',\n outline: 'border border-input bg-transparent shadow-xs',\n },\n size: {\n default: 'h-9 px-2 min-w-9',\n sm: 'h-8 px-1.5 min-w-8',\n lg: 'h-10 px-2.5 min-w-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\ntype ToggleGroupContextProps = VariantProps<typeof toggleVariants> & {\n type?: 'single' | 'multiple';\n transition?: Transition;\n activeClassName?: string;\n globalId: string;\n};\n\nconst ToggleGroupContext = React.createContext<\n ToggleGroupContextProps | undefined\n>(undefined);\n\nconst useToggleGroup = (): ToggleGroupContextProps => {\n const context = React.useContext(ToggleGroupContext);\n if (!context) {\n throw new Error('useToggleGroup must be used within a ToggleGroup');\n }\n return context;\n};\n\ntype ToggleGroupProps = React.ComponentProps<typeof ToggleGroupPrimitive> &\n Omit<VariantProps<typeof toggleVariants>, 'type'> & {\n transition?: Transition;\n activeClassName?: string;\n };\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n children,\n transition = { type: 'spring', bounce: 0, stiffness: 200, damping: 25 },\n activeClassName,\n ...props\n}: ToggleGroupProps) {\n const globalId = React.useId();\n\n return (\n <ToggleGroupContext.Provider\n value={{\n variant,\n size,\n type: props.toggleMultiple ? 'multiple' : 'single',\n transition,\n activeClassName,\n globalId,\n }}\n >\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n className={cn(\n 'flex items-center justify-center gap-1 relative',\n className,\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive>\n </ToggleGroupContext.Provider>\n );\n}\n\ntype ToggleGroupItemProps = Omit<\n React.ComponentProps<typeof TogglePrimitive>,\n 'render'\n> &\n Omit<VariantProps<typeof toggleVariants>, 'type'> & {\n children?: React.ReactNode;\n buttonProps?: HTMLMotionProps<'button'>;\n spanProps?: React.ComponentProps<'span'>;\n };\n\nfunction ToggleGroupItem({\n ref,\n className,\n children,\n variant,\n size,\n buttonProps,\n spanProps,\n ...props\n}: ToggleGroupItemProps) {\n const {\n activeClassName,\n transition,\n type,\n variant: contextVariant,\n size: contextSize,\n globalId,\n } = useToggleGroup();\n const itemRef = React.useRef<HTMLButtonElement | null>(null);\n React.useImperativeHandle(ref, () => itemRef.current as HTMLButtonElement);\n const [isActive, setIsActive] = React.useState(false);\n\n React.useEffect(() => {\n const node = itemRef.current;\n if (!node) return;\n const observer = new MutationObserver(() => {\n setIsActive(node.getAttribute('data-pressed') === '');\n });\n observer.observe(node, {\n attributes: true,\n attributeFilter: ['data-pressed'],\n });\n setIsActive(node.getAttribute('data-pressed') === '');\n return () => observer.disconnect();\n }, [setIsActive]);\n\n return (\n <TogglePrimitive\n ref={itemRef}\n {...props}\n render={\n <motion.button\n data-slot=\"toggle-group-item\"\n initial={{ scale: 1 }}\n whileTap={{ scale: 0.9 }}\n {...buttonProps}\n className={cn('relative', buttonProps?.className)}\n >\n <span\n {...spanProps}\n {...(isActive ? { 'data-pressed': '' } : {})}\n className={cn(\n 'relative z-[1]',\n toggleVariants({\n variant: variant || contextVariant,\n size: size || contextSize,\n type,\n }),\n className,\n spanProps?.className,\n )}\n >\n {children}\n </span>\n\n <AnimatePresence initial={false}>\n {isActive && type === 'single' && (\n <motion.span\n layoutId={`active-toggle-group-item-${globalId}`}\n data-slot=\"active-toggle-group-item\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={transition}\n className={cn(\n 'absolute inset-0 z-0 rounded-md bg-muted',\n activeClassName,\n )}\n />\n )}\n </AnimatePresence>\n </motion.button>\n }\n />\n );\n}\n\nexport {\n ToggleGroup,\n ToggleGroupItem,\n type ToggleGroupProps,\n type ToggleGroupItemProps,\n};\n",
"type": "registry:ui",
"target": "components/animate-ui/base/toggle-group.tsx"
}
]
}