22 lines
6.0 KiB
JSON
22 lines
6.0 KiB
JSON
{
|
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
"name": "radix-tabs",
|
|
"type": "registry:ui",
|
|
"title": "Radix Tabs",
|
|
"description": "A set of layered sections of content—known as tab panels—that are displayed one at a time.",
|
|
"dependencies": [
|
|
"motion",
|
|
"radix-ui"
|
|
],
|
|
"registryDependencies": [
|
|
"https://animate-ui.com/r/motion-highlight"
|
|
],
|
|
"files": [
|
|
{
|
|
"path": "registry/radix/tabs/index.tsx",
|
|
"content": "'use client';\n\nimport * as React from 'react';\nimport { Tabs as TabsPrimitive } from 'radix-ui';\nimport { type HTMLMotionProps, type Transition, motion } from 'motion/react';\n\nimport { cn } from '@/lib/utils';\nimport {\n MotionHighlight,\n MotionHighlightItem,\n} from '@/components/animate-ui/effects/motion-highlight';\n\ntype TabsProps = React.ComponentProps<typeof TabsPrimitive.Root>;\n\nfunction Tabs({ className, ...props }: TabsProps) {\n return (\n <TabsPrimitive.Root\n data-slot=\"tabs\"\n className={cn('flex flex-col gap-2', className)}\n {...props}\n />\n );\n}\n\ntype TabsListProps = React.ComponentProps<typeof TabsPrimitive.List> & {\n activeClassName?: string;\n transition?: Transition;\n};\n\nfunction TabsList({\n ref,\n children,\n className,\n activeClassName,\n transition = {\n type: 'spring',\n stiffness: 200,\n damping: 25,\n },\n ...props\n}: TabsListProps) {\n const localRef = React.useRef<HTMLDivElement | null>(null);\n React.useImperativeHandle(ref, () => localRef.current as HTMLDivElement);\n\n const [activeValue, setActiveValue] = React.useState<string | undefined>(\n undefined,\n );\n\n const getActiveValue = React.useCallback(() => {\n if (!localRef.current) return;\n const activeTab = localRef.current.querySelector<HTMLElement>(\n '[data-state=\"active\"]',\n );\n if (!activeTab) return;\n setActiveValue(activeTab.getAttribute('data-value') ?? undefined);\n }, []);\n\n React.useEffect(() => {\n getActiveValue();\n\n const observer = new MutationObserver(getActiveValue);\n\n if (localRef.current) {\n observer.observe(localRef.current, {\n attributes: true,\n childList: true,\n subtree: true,\n });\n }\n\n return () => {\n observer.disconnect();\n };\n }, [getActiveValue]);\n\n return (\n <MotionHighlight\n controlledItems\n className={cn('rounded-sm bg-background shadow-sm', activeClassName)}\n value={activeValue}\n transition={transition}\n >\n <TabsPrimitive.List\n ref={localRef}\n data-slot=\"tabs-list\"\n className={cn(\n 'bg-muted text-muted-foreground inline-flex h-10 w-fit items-center justify-center rounded-lg p-[4px]',\n className,\n )}\n {...props}\n >\n {children}\n </TabsPrimitive.List>\n </MotionHighlight>\n );\n}\n\ntype TabsTriggerProps = React.ComponentProps<typeof TabsPrimitive.Trigger>;\n\nfunction TabsTrigger({ className, value, ...props }: TabsTriggerProps) {\n return (\n <MotionHighlightItem value={value} className=\"size-full\">\n <TabsPrimitive.Trigger\n data-slot=\"tabs-trigger\"\n className={cn(\n 'inline-flex cursor-pointer items-center size-full justify-center whitespace-nowrap rounded-sm px-2 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-foreground z-[1]',\n className,\n )}\n value={value}\n {...props}\n />\n </MotionHighlightItem>\n );\n}\n\ntype TabsContentProps = React.ComponentProps<typeof TabsPrimitive.Content> &\n HTMLMotionProps<'div'> & {\n transition?: Transition;\n };\n\nfunction TabsContent({\n className,\n children,\n transition = {\n duration: 0.5,\n ease: 'easeInOut',\n },\n ...props\n}: TabsContentProps) {\n return (\n <TabsPrimitive.Content asChild {...props}>\n <motion.div\n data-slot=\"tabs-content\"\n className={cn('flex-1 outline-none', className)}\n layout\n initial={{ opacity: 0, y: -10, filter: 'blur(4px)' }}\n animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}\n exit={{ opacity: 0, y: 10, filter: 'blur(4px)' }}\n transition={transition}\n {...props}\n >\n {children}\n </motion.div>\n </TabsPrimitive.Content>\n );\n}\n\ntype TabsContentsProps = HTMLMotionProps<'div'> & {\n children: React.ReactNode;\n className?: string;\n transition?: Transition;\n};\n\nfunction TabsContents({\n children,\n className,\n transition = { type: 'spring', stiffness: 200, damping: 25 },\n ...props\n}: TabsContentsProps) {\n const containerRef = React.useRef<HTMLDivElement | null>(null);\n\n const [height, setHeight] = React.useState(0);\n\n React.useEffect(() => {\n if (!containerRef.current) return;\n\n const resizeObserver = new ResizeObserver((entries) => {\n const newHeight = entries?.[0]?.contentRect.height;\n if (!newHeight) return;\n requestAnimationFrame(() => {\n setHeight(newHeight);\n });\n });\n\n resizeObserver.observe(containerRef.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [children]);\n\n React.useLayoutEffect(() => {\n if (containerRef.current) {\n const initialHeight = containerRef.current.getBoundingClientRect().height;\n setHeight(initialHeight);\n }\n }, [children]);\n\n return (\n <motion.div\n data-slot=\"tabs-contents\"\n layout\n animate={{ height: height }}\n transition={transition}\n className={className}\n {...props}\n >\n <div ref={containerRef}>{children}</div>\n </motion.div>\n );\n}\n\nexport {\n Tabs,\n TabsList,\n TabsTrigger,\n TabsContent,\n TabsContents,\n type TabsProps,\n type TabsListProps,\n type TabsTriggerProps,\n type TabsContentProps,\n type TabsContentsProps,\n};\n",
|
|
"type": "registry:ui",
|
|
"target": "components/animate-ui/radix/tabs.tsx"
|
|
}
|
|
]
|
|
} |