{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "fab-grid",
  "type": "registry:block",
  "title": "Fab grid",
  "description": "Fab grid",
  "files": [
    {
      "path": "components/usages/fabgridusage.tsx",
      "content": "import FabGrid from \"@/registry/open-source/fab-grid\";\nimport { HomeIcon } from \"lucide-react\";\n\nexport default function Usage() {\n    return (\n        <div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\n            <FabGrid actions={[\n                {\n                    link: \"https://www.google.com\",\n                    icon: <HomeIcon className=\"w-4 h-4\" />,\n                    name: \"Home\",\n                },\n            ]} />\n        </div>\n    );\n}",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/fabgridusage.tsx",
      "content": "import FabGrid from \"@/registry/open-source/fab-grid\";\nimport { HomeIcon } from \"lucide-react\";\n\nexport default function Usage() {\n    return (\n        <div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\n            <FabGrid actions={[\n                {\n                    link: \"https://www.google.com\",\n                    icon: <HomeIcon className=\"w-4 h-4\" />,\n                    name: \"Home\",\n                },\n            ]} />\n        </div>\n    );\n}",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/fab-grid.tsx",
      "content": "\"use client\"\n\nimport React from \"react\"\nimport {\n    AnimatePresence,\n    MotionConfig,\n    motion,\n    type Transition,\n} from \"motion/react\"\nimport { Plus, X } from \"lucide-react\"\n\n// Credit:\n// https://www.easyui.pro/component\n\nconst transition: Transition = { type: \"spring\", bounce: 0, duration: 0.4 }\n\nconst Context = React.createContext<{\n    status: string\n    setStatus: React.Dispatch<React.SetStateAction<string>>\n}>({ status: \"\", setStatus: () => null })\n\ninterface ActionItem {\n    link: string\n    icon: React.ReactNode\n    name: string\n}\n\ninterface InnerContentProps {\n    actions: ActionItem[]\n}\n\nfunction InnerContent({ actions }: InnerContentProps) {\n    const ctx = React.useContext(Context)\n    const isOpen = ctx.status === \"open\"\n    const isHovered = ctx.status === \"hovered\"\n\n    return (\n        <AnimatePresence>\n            {isOpen || isHovered ? (\n                <motion.div\n                    layoutId=\"container\"\n                    style={{ borderRadius: 22 }}\n                    className=\"dark:bg-gray-900 bg-[#f7f6ef] tracking-tight text-[#6b6967] shadow-mixed ring-2 ring-black/[8%] dark:bg-black dark:text-white dark:border dark:border-gray-800\"\n                >\n                    <div className=\"flex w-full items-center justify-between py-2.5 pl-5 pr-2.5\">\n                        <motion.span layoutId=\"label\" className=\"relative\">\n                            Create New\n                        </motion.span>\n                        <div className=\"relative\">\n                            <AnimatePresence>\n                                {isHovered && (\n                                    <motion.p\n                                        initial={{ opacity: 0, x: 10 }}\n                                        animate={{ opacity: 1, x: 0 }}\n                                        exit={{ opacity: 0, x: 10 }}\n                                        className=\"absolute -left-11 top-0.5 text-sm text-[#6b6967]/70\"\n                                    >\n                                        Close\n                                    </motion.p>\n                                )}\n                            </AnimatePresence>\n                            <motion.button\n                                layout\n                                onClick={() => ctx.setStatus(\"idle\")}\n                                initial={{ opacity: 0, x: -20, y: 10 }}\n                                animate={{ opacity: 1, x: 0, y: 0 }}\n                                exit={{ opacity: 0, x: -20, y: 10 }}\n                                transition={{ ...transition, delay: 0.15 }}\n                                whileTap={{\n                                    scale: 0.9,\n                                    transition: { ...transition, duration: 0.2 },\n                                }}\n                                onHoverStart={() => ctx.setStatus(\"hovered\")}\n                                onHoverEnd={() => ctx.setStatus(\"open\")}\n                                className=\"size-6 flex items-center justify-center rounded-full bg-[#b8b6af] dark:bg-transparent\"\n                            >\n                                <X\n                                    strokeWidth={4}\n                                    className=\"size-4 text-tight text-[#fafafa]\"\n                                />\n                            </motion.button>\n                        </div>\n                    </div>\n                    <motion.div\n                        initial={{ opacity: 0 }}\n                        animate={\n                            isHovered\n                                ? { opacity: 1, scaleX: 0.95, scaleY: 0.92 }\n                                : { opacity: 1 }\n                        }\n                        exit={{ opacity: 0 }}\n                        className=\"flex flex-col gap-2.5 rounded-[22px] bg-[#fafafa] p-2.5 shadow-[0_-3px_3px_-1.5px_rgba(0,0,0,0.08)] ring-1 ring-black/[8%] dark:bg-black dark:text-white dark:border dark:border-gray-800\"\n                    >\n                        <div className=\"grid grid-cols-3 gap-2.5 text-[#b8b6af]\">\n                            {actions.map((item, index) => (\n                                <button\n                                    key={index}\n                                    onClick={() => {\n                                        window.open(item.link, \"_blank\")\n                                        ctx.setStatus(\"idle\")\n                                    }}\n                                    className=\"size-24 grid cursor-pointer place-items-center rounded-2xl bg-[#fefefe] transition duration-500 ease-in-out hover:bg-[#f6f4f0] dark:hover:bg-gray-700 hover:duration-200 active:scale-90 dark:bg-black dark:shadow-xl dark:text-white dark:border dark:border-gray-900\"\n                                >\n                                    <div className=\"flex flex-col items-center gap-2\">\n                                        {item.icon}\n                                        <p className=\"text-[#6b6967]\">{item.name}</p>\n                                    </div>\n                                </button>\n                            ))}\n                        </div>\n                    </motion.div>\n                </motion.div>\n            ) : (\n                <motion.button\n                    layoutId=\"container\"\n                    onClick={() => ctx.setStatus(\"open\")}\n                    whileTap={{ scale: 0.95 }}\n                    style={{ borderRadius: 22 }}\n                    className=\"flex items-center gap-1.5 bg-[#fafafa] px-5 py-2.5 tracking-tight text-[#202020] shadow-mixed ring-1 ring-black/[8%] transition-[box-shadow,background-color] hover:bg-[#e0deda] active:shadow-none dark:bg-black dark:text-white dark:border dark:border-gray-700 dark:hover:bg-gray-800\"\n                >\n                    <Plus strokeWidth={1} />\n                    Create New\n                </motion.button>\n            )}\n        </AnimatePresence>\n    )\n}\n\ninterface CreateNewComponentProps {\n    actions: ActionItem[]\n}\n\nexport default function FabGrid({\n    actions,\n}: CreateNewComponentProps) {\n    const [status, setStatus] = React.useState(\"idle\")\n\n    React.useEffect(() => {\n        function handleEscape(e: KeyboardEvent) {\n            if (e.key === \"Escape\") {\n                setStatus(\"idle\")\n            }\n        }\n        window.addEventListener(\"keydown\", handleEscape)\n        return () => window.removeEventListener(\"keydown\", handleEscape)\n    }, [setStatus])\n\n    return (\n        <Context.Provider value={{ status, setStatus }}>\n            <MotionConfig transition={transition}>\n                <main className=\"relative flex h-auto items-center justify-center bg-[#fdfdfc] font-medium dark:bg-black\">\n                    <InnerContent actions={actions} />\n                </main>\n            </MotionConfig>\n        </Context.Provider>\n    )\n}",
      "type": "registry:ui"
    }
  ]
}