diff --git a/docs/public/registry/frameworks/tailwindcss/sidebar.json b/docs/public/registry/frameworks/tailwindcss/sidebar.json new file mode 100644 index 0000000..0ca2ef7 --- /dev/null +++ b/docs/public/registry/frameworks/tailwindcss/sidebar.json @@ -0,0 +1,13 @@ +{ + "name": "sidebar", + "dependencies": [ + "@kobalte/core" + ], + "files": [ + { + "name": "sidebar.tsx", + "content": "import { Button } from \"@repo/tailwindcss/ui/button\";\nimport { Separator } from \"@repo/tailwindcss/ui/separator\";\nimport { Sheet, SheetContent } from \"@repo/tailwindcss/ui/sheet\";\nimport { TextField } from \"@repo/tailwindcss/ui/textfield\";\n\nimport { cn } from \"@/libs/cn\";\nimport { Skeleton } from \"@repo/tailwindcss/ui/skeleton\";\nimport {\n\tTooltip,\n\tTooltipContent,\n\tTooltipTrigger,\n} from \"@repo/tailwindcss/ui/tooltip\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport {\n\ttype Accessor,\n\ttype ComponentProps,\n\ttype JSX,\n\tShow,\n\tcreateContext,\n\tcreateMemo,\n\tcreateSignal,\n\tmergeProps,\n\tonCleanup,\n\tonMount,\n\tsplitProps,\n\tuseContext,\n} from \"solid-js\";\n\nconst SIDEBAR_COOKIE_NAME = \"sidebar:state\";\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;\nconst SIDEBAR_WIDTH = \"16rem\";\nconst SIDEBAR_WIDTH_MOBILE = \"18rem\";\nconst SIDEBAR_WIDTH_ICON = \"3rem\";\nconst SIDEBAR_KEYBOARD_SHORTCUT = \"b\";\nconst MOBILE_BREAKPOINT = 768;\n\ntype SidebarContext = {\n\tstate: \"expanded\" | \"collapsed\";\n\topen: boolean;\n\tsetOpen: (open: boolean) => void;\n\topenMobile: boolean;\n\tsetOpenMobile: (open: boolean) => void;\n\tisMobile: boolean;\n\ttoggleSidebar: () => void;\n};\n\nconst SidebarContext = createContext | null>(null);\n\nfunction useSidebar() {\n\tconst context = useContext(SidebarContext);\n\n\tif (!context) {\n\t\tthrow new Error(\"useSidebar must be used within a SidebarProvider\");\n\t}\n\n\treturn context;\n}\n\nexport function useIsMobile() {\n\tconst [isMobile, setIsMobile] = createSignal(false);\n\n\tonMount(() => {\n\t\tconst mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n\t\tconst onChange = () => {\n\t\t\tsetIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n\t\t};\n\n\t\tmql.addEventListener(\"change\", onChange);\n\t\tsetIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n\n\t\tonCleanup(() => mql.removeEventListener(\"change\", onChange));\n\t});\n\n\treturn isMobile;\n}\n\ntype SidebarProviderProps = {\n\tdefaultOpen?: boolean;\n\topen?: boolean;\n\tonOpenChange?: (open: boolean) => void;\n\tchildren: JSX.Element;\n} & ComponentProps<\"div\">;\n\nconst SidebarProvider = (props: SidebarProviderProps) => {\n\tconst isMobile = useIsMobile();\n\tconst [openMobile, setOpenMobile] = createSignal(false);\n\n\tconst [local, rest] = splitProps(props, [\n\t\t\"open\",\n\t\t\"class\",\n\t\t\"style\",\n\t\t\"defaultOpen\",\n\t\t\"onOpenChange\",\n\t\t\"children\",\n\t]);\n\n\t// This is the internal state of the sidebar.\n\t// We use local.open and local.onOpenChange for control from outside the component.\n\tconst [_open, _setOpen] = createSignal(local.defaultOpen ?? true);\n\tconst open = () => local.open ?? _open();\n\n\tconst setOpen = (value: boolean | ((value: boolean) => boolean)) => {\n\t\tconst openState = typeof value === \"function\" ? value(open()) : open();\n\t\t_setOpen(openState);\n\t\tlocal.onOpenChange?.(open());\n\n\t\tdocument.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;\n\t};\n\n\tconst toggleSidebar = () => {\n\t\tisMobile() ? setOpenMobile(!openMobile()) : setOpen(!open());\n\t};\n\n\t// Adds a keyboard shortcut to toggle the sidebar\n\tonMount(() => {\n\t\tconst handleKeyDown = (event: KeyboardEvent) => {\n\t\t\tif (\n\t\t\t\tevent.key === SIDEBAR_KEYBOARD_SHORTCUT &&\n\t\t\t\t(event.metaKey || event.ctrlKey)\n\t\t\t) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\ttoggleSidebar();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"keydown\", handleKeyDown);\n\t\tonCleanup(() => window.removeEventListener(\"keydown\", handleKeyDown));\n\t});\n\n\t// We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n\t// This makes it easier to style the sidebar with Tailwind classes.\n\tconst state = open() ? \"expanded\" : \"collapsed\";\n\n\tconst contextValue = createMemo(() => ({\n\t\tstate,\n\t\topen: open(),\n\t\tsetOpen,\n\t\tisMobile: isMobile(),\n\t\topenMobile: openMobile(),\n\t\tsetOpenMobile,\n\t\ttoggleSidebar,\n\t}));\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t{local.children}\n\t\t\t\n\t\t\n\t);\n};\n\ntype SidebarProps = {\n\tside?: \"left\" | \"right\";\n\tvariant?: \"sidebar\" | \"floating\" | \"inset\";\n\tcollapsible?: \"offcanvas\" | \"icon\" | \"none\";\n\tchildren: JSX.Element;\n} & ComponentProps<\"div\">;\n\nconst Sidebar = (props: SidebarProps) => {\n\tconst mergedProps = mergeProps(\n\t\t{\n\t\t\tside: \"left\" as const,\n\t\t\tvariant: \"sidebar\" as const,\n\t\t\tcollapsible: \"offcanvas\" as const,\n\t\t},\n\t\tprops,\n\t);\n\n\tconst [local, rest] = splitProps<\n\t\tSidebarProps,\n\t\t[[\"class\", \"children\", \"side\", \"variant\", \"collapsible\"]]\n\t>(mergedProps, [\"class\", \"children\", \"side\", \"variant\", \"collapsible\"]);\n\n\tconst context = useSidebar();\n\n\tif (local.collapsible === \"none\") {\n\t\treturn (\n\t\t\t\n\t\t\t\t{local.children}\n\t\t\t\n\t\t);\n\t}\n\n\tif (context().isMobile) {\n\t\treturn (\n\t\t\t\n\t\t\t\tbutton]:hidden\"\n\t\t\t\t\tstyle={\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"--sidebar-width\": SIDEBAR_WIDTH_MOBILE,\n\t\t\t\t\t\t} as JSX.CSSProperties\n\t\t\t\t\t}\n\t\t\t\t\tside={local.side}\n\t\t\t\t>\n\t\t\t\t\t
{local.children}
\n\t\t\t\t\n\t\t\t\n\t\t);\n\t}\n\n\treturn (\n\t\t