{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "dynamic-toolbar", "title": "Dynamic Toolbar", "description": "A toolbar that adapts its size and layout based on content.", "dependencies": [ "motion", "@hugeicons/core-free-icons", "@hugeicons/react", "clsx", "tailwind-merge" ], "files": [ { "path": "registry/default/example/dynamic-toolbar.tsx", "content": "\"use client\";\r\nimport { delay, motion } from \"motion/react\";\r\nimport React, { useEffect, useRef, useState } from \"react\";\r\nimport {\r\n InboxIcon,\r\n Message01Icon,\r\n PaintBoardIcon,\r\n Tag01Icon,\r\n Image01Icon,\r\n Archive02Icon,\r\n ArrowReloadHorizontalIcon,\r\n Delete02Icon,\r\n ArrowRight01Icon,\r\n ArrowLeft01Icon,\r\n} from \"@hugeicons/core-free-icons\";\r\nimport { HugeiconsIcon } from \"@hugeicons/react\";\r\nimport useMeasure from \"@/hooks/use-measure\";\r\n\r\nconst ICON_SIZE = 24;\r\n\r\n// Change Here\r\nconst primaryTools = [\r\n { icon: InboxIcon, label: \"Inbox\" },\r\n { icon: Message01Icon, label: \"Messages\" },\r\n { icon: PaintBoardIcon, label: \"Paint\" },\r\n { icon: Tag01Icon, label: \"Tags\", blur: true },\r\n];\r\n\r\nconst secondaryTools = [\r\n { icon: Image01Icon, label: \"Image\", blur: true },\r\n { icon: Archive02Icon, label: \"Archive\" },\r\n {\r\n icon: ArrowReloadHorizontalIcon,\r\n label: \"Reload\",\r\n className: \"-scale-x-100\",\r\n },\r\n {\r\n icon: ArrowReloadHorizontalIcon,\r\n label: \"Reload\",\r\n className: \"-scale-x-100\",\r\n },\r\n { icon: Delete02Icon, label: \"Delete\", className: \"text-red-500\" },\r\n];\r\n\r\nfunction ToolbarButton({\r\n icon,\r\n size = ICON_SIZE,\r\n blur = false,\r\n isBlurred = false,\r\n className = \"\",\r\n}: {\r\n icon: any;\r\n size?: number;\r\n blur?: boolean;\r\n isBlurred?: boolean;\r\n className?: string;\r\n}) {\r\n const iconElement = (\r\n \r\n );\r\n\r\n if (blur) {\r\n return (\r\n \r\n );\r\n }\r\n\r\n return (\r\n \r\n );\r\n}\r\n\r\nfunction ExtendedToolbar() {\r\n const [isExpanded, setIsExpanded] = useState(false);\r\n const [isMounted, setIsMounted] = useState(false);\r\n const [primaryRef, primaryBounds] = useMeasure();\r\n const [secondaryRef, secondaryBounds] = useMeasure();\r\n\r\n useEffect(() => {\r\n setIsMounted(true);\r\n }, []);\r\n\r\n const currentWidth = isExpanded ? secondaryBounds.width : primaryBounds.width;\r\n const hasMeasurements = primaryBounds.width > 0;\r\n\r\n const initialWidth = hasMeasurements ? primaryBounds.width : \"auto\";\r\n\r\n const springTransition = {\r\n type: \"spring\" as const,\r\n stiffness: 200,\r\n damping: 20,\r\n mass: 0.8,\r\n bounce: 0.9,\r\n duration: isExpanded ? 0.4 : 1.2,\r\n delay: isExpanded ? 0 : 0.015,\r\n };\r\n\r\n return (\r\n \r\n \r\n {/* Primary Tools Panel */}\r\n }\r\n className=\"flex items-center gap-1 p-1.5 pl-3 pr-2 flex-shrink-0\"\r\n >\r\n {primaryTools.map((item, index) => (\r\n \r\n ))}\r\n setIsExpanded(true)}\r\n className=\"h-full aspect-square flex justify-center items-center bg-background rounded-full\"\r\n >\r\n \r\n \r\n \r\n\r\n {/* Secondary Tools Panel */}\r\n }\r\n className=\"flex items-center gap-1 p-1.5 pl-2 pr-3 flex-shrink-0\"\r\n style={{\r\n position: isExpanded ? \"relative\" : \"absolute\",\r\n opacity: isExpanded ? 1 : 0,\r\n pointerEvents: isExpanded ? \"auto\" : \"none\",\r\n }}\r\n >\r\n setIsExpanded(false)}\r\n className=\"h-full aspect-square flex justify-center items-center bg-background rounded-full\"\r\n >\r\n \r\n \r\n {secondaryTools.map((item, index) => (\r\n \r\n ))}\r\n \r\n \r\n \r\n );\r\n}\r\n\r\nexport default ExtendedToolbar;\r\n", "type": "registry:component" }, { "path": "hooks/use-measure.tsx", "content": "import { useEffect, useRef, useState } from \"react\";\n\nconst useMeasure = () => {\n const ref = useRef(null);\n const [bounds, setBounds] = useState({ height: 0, width: 0 });\n\n useEffect(() => {\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const rect = entry.target.getBoundingClientRect();\n\n setBounds({\n height: rect.height,\n width: rect.width,\n });\n }\n });\n\n if (ref.current) {\n observer.observe(ref.current);\n }\n\n return () => observer.disconnect();\n }, []);\n\n return [ref, bounds] as const;\n};\n\nexport default useMeasure;\n", "type": "registry:hook" } ], "type": "registry:component" }