@dogsbay/ui 0.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/package.json +38 -0
  2. package/src/accordion/Accordion.astro +38 -0
  3. package/src/accordion/AccordionContent.astro +25 -0
  4. package/src/accordion/AccordionItem.astro +26 -0
  5. package/src/accordion/AccordionTrigger.astro +38 -0
  6. package/src/accordion/accordion.ts +189 -0
  7. package/src/accordion/index.ts +4 -0
  8. package/src/alert/Alert.astro +69 -0
  9. package/src/alert/AlertDescription.astro +17 -0
  10. package/src/alert/AlertTitle.astro +21 -0
  11. package/src/alert/CollapsibleAlert.astro +103 -0
  12. package/src/alert/icon.ts +32 -0
  13. package/src/alert/index.ts +4 -0
  14. package/src/alert/variants.ts +29 -0
  15. package/src/alert-dialog/AlertDialog.astro +16 -0
  16. package/src/alert-dialog/AlertDialogAction.astro +23 -0
  17. package/src/alert-dialog/AlertDialogCancel.astro +23 -0
  18. package/src/alert-dialog/AlertDialogContent.astro +37 -0
  19. package/src/alert-dialog/AlertDialogDescription.astro +18 -0
  20. package/src/alert-dialog/AlertDialogFooter.astro +18 -0
  21. package/src/alert-dialog/AlertDialogHeader.astro +18 -0
  22. package/src/alert-dialog/AlertDialogTitle.astro +18 -0
  23. package/src/alert-dialog/AlertDialogTrigger.astro +24 -0
  24. package/src/alert-dialog/alert-dialog.ts +154 -0
  25. package/src/alert-dialog/index.ts +9 -0
  26. package/src/api-doc/ApiDoc.astro +52 -0
  27. package/src/api-doc/index.ts +1 -0
  28. package/src/api-layout/ApiCodePanel.astro +21 -0
  29. package/src/api-layout/ApiLayout.astro +29 -0
  30. package/src/api-layout/ExampleBlock.astro +63 -0
  31. package/src/api-params/ApiParams.astro +96 -0
  32. package/src/api-params/index.ts +1 -0
  33. package/src/api-source/ApiSource.astro +20 -0
  34. package/src/api-source/api-source.css +32 -0
  35. package/src/api-source/index.ts +1 -0
  36. package/src/api-symbol/ApiSymbol.astro +48 -0
  37. package/src/api-symbol/api-symbol.css +89 -0
  38. package/src/api-symbol/index.ts +1 -0
  39. package/src/api-url-bar/ApiUrlBar.astro +63 -0
  40. package/src/avatar/Avatar.astro +57 -0
  41. package/src/avatar/index.ts +1 -0
  42. package/src/badge/Badge.astro +37 -0
  43. package/src/badge/index.ts +1 -0
  44. package/src/breadcrumb/Breadcrumb.astro +32 -0
  45. package/src/breadcrumb/BreadcrumbEllipsis.astro +32 -0
  46. package/src/breadcrumb/BreadcrumbItem.astro +41 -0
  47. package/src/breadcrumb/BreadcrumbSeparator.astro +33 -0
  48. package/src/breadcrumb/index.ts +4 -0
  49. package/src/button/Button.astro +70 -0
  50. package/src/button/index.ts +1 -0
  51. package/src/card/Card.astro +17 -0
  52. package/src/card/CardContent.astro +17 -0
  53. package/src/card/CardDescription.astro +17 -0
  54. package/src/card/CardFooter.astro +17 -0
  55. package/src/card/CardHeader.astro +17 -0
  56. package/src/card/CardTitle.astro +17 -0
  57. package/src/card/index.ts +6 -0
  58. package/src/checkbox/Checkbox.astro +70 -0
  59. package/src/checkbox/index.ts +1 -0
  60. package/src/checkbox-group/CheckboxGroup.astro +38 -0
  61. package/src/checkbox-group/checkbox-group.ts +53 -0
  62. package/src/checkbox-group/index.ts +1 -0
  63. package/src/code-block/CodeBlock.astro +160 -0
  64. package/src/code-rich/CodeRich.astro +267 -0
  65. package/src/code-rich/code-rich.css +68 -0
  66. package/src/code-rich/index.ts +1 -0
  67. package/src/code-samples/CodeSamples.astro +59 -0
  68. package/src/collapsible/Collapsible.astro +35 -0
  69. package/src/collapsible/CollapsibleContent.astro +24 -0
  70. package/src/collapsible/CollapsibleTrigger.astro +23 -0
  71. package/src/collapsible/collapsible.ts +42 -0
  72. package/src/collapsible/index.ts +3 -0
  73. package/src/combobox/Combobox.astro +53 -0
  74. package/src/combobox/ComboboxEmpty.astro +18 -0
  75. package/src/combobox/ComboboxItem.astro +28 -0
  76. package/src/combobox/combobox.ts +179 -0
  77. package/src/combobox/index.ts +3 -0
  78. package/src/command/Command.astro +22 -0
  79. package/src/command/CommandEmpty.astro +18 -0
  80. package/src/command/CommandGroup.astro +24 -0
  81. package/src/command/CommandInput.astro +49 -0
  82. package/src/command/CommandItem.astro +26 -0
  83. package/src/command/CommandList.astro +19 -0
  84. package/src/command/CommandSeparator.astro +16 -0
  85. package/src/command/CommandShortcut.astro +18 -0
  86. package/src/command/command.ts +212 -0
  87. package/src/command/index.ts +8 -0
  88. package/src/content-renderer/ContentRenderer.astro +410 -0
  89. package/src/content-renderer/InlineRenderer.astro +149 -0
  90. package/src/context-menu/ContextMenu.astro +22 -0
  91. package/src/context-menu/ContextMenuContent.astro +24 -0
  92. package/src/context-menu/ContextMenuItem.astro +49 -0
  93. package/src/context-menu/ContextMenuSeparator.astro +14 -0
  94. package/src/context-menu/ContextMenuShortcut.astro +14 -0
  95. package/src/context-menu/ContextMenuTrigger.astro +22 -0
  96. package/src/context-menu/context-menu.ts +238 -0
  97. package/src/context-menu/index.ts +6 -0
  98. package/src/copy-button/CopyButton.astro +88 -0
  99. package/src/copy-button/index.ts +1 -0
  100. package/src/diagram/Diagram.astro +15 -0
  101. package/src/diagram/index.ts +1 -0
  102. package/src/dialog/Dialog.astro +22 -0
  103. package/src/dialog/DialogClose.astro +32 -0
  104. package/src/dialog/DialogContent.astro +37 -0
  105. package/src/dialog/DialogDescription.astro +22 -0
  106. package/src/dialog/DialogFooter.astro +18 -0
  107. package/src/dialog/DialogHeader.astro +18 -0
  108. package/src/dialog/DialogTitle.astro +18 -0
  109. package/src/dialog/DialogTrigger.astro +33 -0
  110. package/src/dialog/dialog.ts +291 -0
  111. package/src/dialog/index.ts +8 -0
  112. package/src/docs-footer/DocsFooter.astro +50 -0
  113. package/src/docs-header/DocsHeader.astro +32 -0
  114. package/src/docs-layout/DocsLayout.astro +187 -0
  115. package/src/docs-layout/index.ts +1 -0
  116. package/src/drawer/Drawer.astro +12 -0
  117. package/src/drawer/DrawerClose.astro +12 -0
  118. package/src/drawer/DrawerContent.astro +46 -0
  119. package/src/drawer/DrawerDescription.astro +10 -0
  120. package/src/drawer/DrawerFooter.astro +10 -0
  121. package/src/drawer/DrawerHandle.astro +16 -0
  122. package/src/drawer/DrawerHeader.astro +10 -0
  123. package/src/drawer/DrawerTitle.astro +10 -0
  124. package/src/drawer/DrawerTrigger.astro +25 -0
  125. package/src/drawer/drawer.ts +262 -0
  126. package/src/drawer/index.ts +9 -0
  127. package/src/dropdown-menu/DropdownMenu.astro +22 -0
  128. package/src/dropdown-menu/DropdownMenuContent.astro +33 -0
  129. package/src/dropdown-menu/DropdownMenuGroup.astro +17 -0
  130. package/src/dropdown-menu/DropdownMenuItem.astro +48 -0
  131. package/src/dropdown-menu/DropdownMenuLabel.astro +28 -0
  132. package/src/dropdown-menu/DropdownMenuSeparator.astro +21 -0
  133. package/src/dropdown-menu/DropdownMenuShortcut.astro +18 -0
  134. package/src/dropdown-menu/DropdownMenuTrigger.astro +34 -0
  135. package/src/dropdown-menu/dropdown-menu.ts +348 -0
  136. package/src/dropdown-menu/index.ts +8 -0
  137. package/src/endpoint-card/EndpointCard.astro +60 -0
  138. package/src/endpoint-card/EndpointCardSection.astro +22 -0
  139. package/src/endpoint-card/EndpointSection.astro +35 -0
  140. package/src/field/Field.astro +31 -0
  141. package/src/field/FieldDescription.astro +22 -0
  142. package/src/field/FieldLabel.astro +24 -0
  143. package/src/field/field.ts +61 -0
  144. package/src/field/index.ts +3 -0
  145. package/src/fieldset/Fieldset.astro +24 -0
  146. package/src/fieldset/FieldsetLegend.astro +18 -0
  147. package/src/fieldset/index.ts +2 -0
  148. package/src/footnote/Footnote.astro +37 -0
  149. package/src/footnote/footnote.ts +79 -0
  150. package/src/footnote/index.ts +1 -0
  151. package/src/form/Form.astro +27 -0
  152. package/src/form/form.ts +160 -0
  153. package/src/form/index.ts +1 -0
  154. package/src/grid/Grid.astro +117 -0
  155. package/src/grid/GridItem.astro +95 -0
  156. package/src/grid/index.ts +2 -0
  157. package/src/image/Image.astro +115 -0
  158. package/src/image/index.ts +1 -0
  159. package/src/input/Input.astro +34 -0
  160. package/src/input/index.ts +1 -0
  161. package/src/kbd/Kbd.astro +17 -0
  162. package/src/kbd/index.ts +1 -0
  163. package/src/label/Label.astro +17 -0
  164. package/src/label/index.ts +1 -0
  165. package/src/link-card/LinkCard.astro +47 -0
  166. package/src/link-card/index.ts +1 -0
  167. package/src/map-view/MapView.astro +88 -0
  168. package/src/map-view/index.ts +1 -0
  169. package/src/map-view/map-view.ts +194 -0
  170. package/src/markdown-example/MarkdownExample.astro +138 -0
  171. package/src/markdown-example/index.ts +1 -0
  172. package/src/math/MathKatex.astro +35 -0
  173. package/src/math/index.ts +1 -0
  174. package/src/menubar/Menubar.astro +25 -0
  175. package/src/menubar/MenubarContent.astro +24 -0
  176. package/src/menubar/MenubarItem.astro +26 -0
  177. package/src/menubar/MenubarMenu.astro +12 -0
  178. package/src/menubar/MenubarSeparator.astro +11 -0
  179. package/src/menubar/MenubarShortcut.astro +11 -0
  180. package/src/menubar/MenubarTrigger.astro +27 -0
  181. package/src/menubar/index.ts +7 -0
  182. package/src/menubar/menubar.ts +258 -0
  183. package/src/meter/Meter.astro +75 -0
  184. package/src/meter/index.ts +1 -0
  185. package/src/method-badge/MethodBadge.astro +32 -0
  186. package/src/navigation-menu/NavigationMenu.astro +24 -0
  187. package/src/navigation-menu/NavigationMenuContent.astro +24 -0
  188. package/src/navigation-menu/NavigationMenuItem.astro +22 -0
  189. package/src/navigation-menu/NavigationMenuLink.astro +26 -0
  190. package/src/navigation-menu/NavigationMenuList.astro +22 -0
  191. package/src/navigation-menu/NavigationMenuTrigger.astro +40 -0
  192. package/src/navigation-menu/index.ts +6 -0
  193. package/src/navigation-menu/navigation-menu.ts +192 -0
  194. package/src/number-field/NumberField.astro +84 -0
  195. package/src/number-field/index.ts +1 -0
  196. package/src/number-field/number-field.ts +68 -0
  197. package/src/pagination/Pagination.astro +59 -0
  198. package/src/pagination/index.ts +1 -0
  199. package/src/parameter-table/ParameterTable.astro +75 -0
  200. package/src/popover/Popover.astro +21 -0
  201. package/src/popover/PopoverContent.astro +31 -0
  202. package/src/popover/PopoverTrigger.astro +31 -0
  203. package/src/popover/index.ts +3 -0
  204. package/src/popover/popover.ts +144 -0
  205. package/src/preview-card/PreviewCard.astro +24 -0
  206. package/src/preview-card/PreviewCardContent.astro +24 -0
  207. package/src/preview-card/PreviewCardTrigger.astro +23 -0
  208. package/src/preview-card/index.ts +3 -0
  209. package/src/preview-card/preview-card.ts +91 -0
  210. package/src/progress/Progress.astro +61 -0
  211. package/src/progress/index.ts +1 -0
  212. package/src/property-item/PropertyItem.astro +183 -0
  213. package/src/property-list/PropertyList.astro +68 -0
  214. package/src/radio-group/RadioGroup.astro +41 -0
  215. package/src/radio-group/RadioGroupItem.astro +36 -0
  216. package/src/radio-group/index.ts +2 -0
  217. package/src/radio-group/radio-group.ts +126 -0
  218. package/src/response-tabs/ResponseTabs.astro +60 -0
  219. package/src/schema-viewer/SchemaViewer.astro +42 -0
  220. package/src/scroll-area/ScrollArea.astro +44 -0
  221. package/src/scroll-area/index.ts +1 -0
  222. package/src/scroll-area/scroll-area.ts +176 -0
  223. package/src/select/Select.astro +34 -0
  224. package/src/select/SelectContent.astro +24 -0
  225. package/src/select/SelectGroup.astro +23 -0
  226. package/src/select/SelectItem.astro +28 -0
  227. package/src/select/SelectLabel.astro +18 -0
  228. package/src/select/SelectSeparator.astro +22 -0
  229. package/src/select/SelectTrigger.astro +28 -0
  230. package/src/select/SelectValue.astro +18 -0
  231. package/src/select/index.ts +8 -0
  232. package/src/select/select.ts +381 -0
  233. package/src/separator/Separator.astro +35 -0
  234. package/src/separator/index.ts +1 -0
  235. package/src/sheet/Sheet.astro +12 -0
  236. package/src/sheet/SheetClose.astro +35 -0
  237. package/src/sheet/SheetContent.astro +52 -0
  238. package/src/sheet/SheetDescription.astro +18 -0
  239. package/src/sheet/SheetFooter.astro +18 -0
  240. package/src/sheet/SheetHeader.astro +18 -0
  241. package/src/sheet/SheetTitle.astro +18 -0
  242. package/src/sheet/SheetTrigger.astro +32 -0
  243. package/src/sheet/index.ts +8 -0
  244. package/src/sheet/sheet.ts +173 -0
  245. package/src/sidebar/Sidebar.astro +113 -0
  246. package/src/sidebar/SidebarContent.astro +18 -0
  247. package/src/sidebar/SidebarFooter.astro +18 -0
  248. package/src/sidebar/SidebarGroup.astro +18 -0
  249. package/src/sidebar/SidebarGroupContent.astro +18 -0
  250. package/src/sidebar/SidebarGroupLabel.astro +18 -0
  251. package/src/sidebar/SidebarHeader.astro +18 -0
  252. package/src/sidebar/SidebarInset.astro +18 -0
  253. package/src/sidebar/SidebarMenu.astro +18 -0
  254. package/src/sidebar/SidebarMenuBadge.astro +18 -0
  255. package/src/sidebar/SidebarMenuButton.astro +71 -0
  256. package/src/sidebar/SidebarMenuItem.astro +18 -0
  257. package/src/sidebar/SidebarMenuSub.astro +18 -0
  258. package/src/sidebar/SidebarMenuSubButton.astro +45 -0
  259. package/src/sidebar/SidebarMenuSubItem.astro +12 -0
  260. package/src/sidebar/SidebarNavTree.astro +106 -0
  261. package/src/sidebar/SidebarProvider.astro +23 -0
  262. package/src/sidebar/SidebarRail.astro +25 -0
  263. package/src/sidebar/SidebarSeparator.astro +21 -0
  264. package/src/sidebar/SidebarTrigger.astro +40 -0
  265. package/src/sidebar/index.ts +19 -0
  266. package/src/sidebar/sidebar.ts +265 -0
  267. package/src/skeleton/Skeleton.astro +16 -0
  268. package/src/skeleton/index.ts +1 -0
  269. package/src/skip-link/SkipLink.astro +24 -0
  270. package/src/slider/Slider.astro +61 -0
  271. package/src/slider/index.ts +1 -0
  272. package/src/slider/slider.ts +111 -0
  273. package/src/spinner/Spinner.astro +54 -0
  274. package/src/spinner/index.ts +1 -0
  275. package/src/steps/Step.astro +16 -0
  276. package/src/steps/Steps.astro +122 -0
  277. package/src/steps/SubSteps.astro +73 -0
  278. package/src/switch/Switch.astro +52 -0
  279. package/src/switch/index.ts +1 -0
  280. package/src/switch/switch.ts +109 -0
  281. package/src/table/Table.astro +24 -0
  282. package/src/table/TableBody.astro +18 -0
  283. package/src/table/TableCaption.astro +18 -0
  284. package/src/table/TableCell.astro +18 -0
  285. package/src/table/TableFooter.astro +18 -0
  286. package/src/table/TableHead.astro +18 -0
  287. package/src/table/TableHeader.astro +18 -0
  288. package/src/table/TableRow.astro +18 -0
  289. package/src/table/index.ts +8 -0
  290. package/src/tabs/Tabs.astro +37 -0
  291. package/src/tabs/TabsContent.astro +32 -0
  292. package/src/tabs/TabsList.astro +23 -0
  293. package/src/tabs/TabsTrigger.astro +29 -0
  294. package/src/tabs/index.ts +4 -0
  295. package/src/tabs/tabs.ts +220 -0
  296. package/src/textarea/Textarea.astro +25 -0
  297. package/src/textarea/index.ts +1 -0
  298. package/src/theme-toggle/ThemeToggle.astro +67 -0
  299. package/src/theme-toggle/index.ts +1 -0
  300. package/src/toast/Toaster.astro +39 -0
  301. package/src/toast/index.ts +3 -0
  302. package/src/toast/toast.ts +104 -0
  303. package/src/toc/TableOfContents.astro +225 -0
  304. package/src/toggle/Toggle.astro +56 -0
  305. package/src/toggle/index.ts +1 -0
  306. package/src/toggle/toggle.ts +38 -0
  307. package/src/toggle-group/ToggleGroup.astro +39 -0
  308. package/src/toggle-group/ToggleGroupItem.astro +56 -0
  309. package/src/toggle-group/index.ts +2 -0
  310. package/src/toggle-group/toggle-group.ts +121 -0
  311. package/src/toolbar/Toolbar.astro +26 -0
  312. package/src/toolbar/ToolbarButton.astro +29 -0
  313. package/src/toolbar/ToolbarSeparator.astro +14 -0
  314. package/src/toolbar/index.ts +3 -0
  315. package/src/toolbar/toolbar.ts +65 -0
  316. package/src/tooltip/Tooltip.astro +21 -0
  317. package/src/tooltip/TooltipContent.astro +25 -0
  318. package/src/tooltip/TooltipTrigger.astro +31 -0
  319. package/src/tooltip/index.ts +3 -0
  320. package/src/tooltip/tooltip.ts +156 -0
  321. package/src/youtube/YouTube.astro +119 -0
  322. package/src/youtube/index.ts +1 -0
@@ -0,0 +1,187 @@
1
+ ---
2
+ /**
3
+ * DocsLayout — canonical documentation layout with sidebar navigation.
4
+ *
5
+ * Handles all the boilerplate that every docs site needs:
6
+ * - ClientRouter for view transitions (sidebar state preserved)
7
+ * - Dark mode with localStorage persistence
8
+ * - SidebarProvider + Sidebar + SidebarInset wiring
9
+ * - Sticky header with SidebarTrigger + ThemeToggle
10
+ * - Active nav highlighting + ancestor expansion
11
+ * - Dark mode preservation across view transitions
12
+ *
13
+ * Install: npx dogsbay add docs-layout
14
+ *
15
+ * Prerequisites:
16
+ * - Run `npx dogsbay init` first (creates theme.css + global.css)
17
+ * - Import global.css in your pages or a wrapper layout:
18
+ * `import "@/styles/global.css";`
19
+ * - DocsLayout does NOT import your styles — that's your app's responsibility,
20
+ * same as shadcn's globals.css pattern.
21
+ *
22
+ * Usage:
23
+ * ---
24
+ * import "@/styles/global.css";
25
+ * import DocsLayout from "@ui/docs-layout/DocsLayout.astro";
26
+ * const nav = [{ label: "Guide", items: [{ label: "Quick Start", href: "/quick-start" }] }];
27
+ * ---
28
+ * <DocsLayout title="Quick Start" siteTitle="My Docs" nav={nav}>
29
+ * <p>Page content here</p>
30
+ * </DocsLayout>
31
+ */
32
+ import { ClientRouter } from "astro:transitions";
33
+ import SidebarProvider from "../sidebar/SidebarProvider.astro";
34
+ import Sidebar from "../sidebar/Sidebar.astro";
35
+ import SidebarHeader from "../sidebar/SidebarHeader.astro";
36
+ import SidebarContent from "../sidebar/SidebarContent.astro";
37
+ import SidebarTrigger from "../sidebar/SidebarTrigger.astro";
38
+ import SidebarInset from "../sidebar/SidebarInset.astro";
39
+ import SidebarRail from "../sidebar/SidebarRail.astro";
40
+ import SidebarGroup from "../sidebar/SidebarGroup.astro";
41
+ import SidebarGroupLabel from "../sidebar/SidebarGroupLabel.astro";
42
+ import SidebarGroupContent from "../sidebar/SidebarGroupContent.astro";
43
+ import SidebarMenu from "../sidebar/SidebarMenu.astro";
44
+ import SidebarMenuItem from "../sidebar/SidebarMenuItem.astro";
45
+ import SidebarMenuButton from "../sidebar/SidebarMenuButton.astro";
46
+ import SidebarSeparator from "../sidebar/SidebarSeparator.astro";
47
+ import SidebarNavTree from "../sidebar/SidebarNavTree.astro";
48
+ import Separator from "../separator/Separator.astro";
49
+ import ThemeToggle from "../theme-toggle/ThemeToggle.astro";
50
+
51
+ interface NavItem {
52
+ label: string;
53
+ href?: string;
54
+ icon?: string;
55
+ children?: NavItem[];
56
+ }
57
+
58
+ interface NavSection {
59
+ label: string;
60
+ items: NavItem[];
61
+ }
62
+
63
+ interface Props {
64
+ /** Page title — shown in header and <title> tag */
65
+ title: string;
66
+ /** Site title — shown in sidebar header */
67
+ siteTitle?: string;
68
+ /** Meta description */
69
+ description?: string;
70
+ /** Navigation sections for the sidebar */
71
+ nav?: NavSection[];
72
+ /** Sidebar collapsible mode */
73
+ collapsible?: "offcanvas" | "icon" | "none";
74
+ /** Max width for content area */
75
+ maxWidth?: string;
76
+ /** Language attribute */
77
+ lang?: string;
78
+ }
79
+
80
+ const {
81
+ title,
82
+ siteTitle = "Docs",
83
+ description = "",
84
+ nav = [],
85
+ collapsible = "icon",
86
+ maxWidth = "max-w-3xl",
87
+ lang = "en",
88
+ } = Astro.props;
89
+
90
+ const currentPath = Astro.url.pathname.replace(/\/$/, "") || "/";
91
+ ---
92
+
93
+ <!doctype html>
94
+ <html lang={lang}>
95
+ <head>
96
+ <meta charset="UTF-8" />
97
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
98
+ {description && <meta name="description" content={description} />}
99
+ <title>{title} — {siteTitle}</title>
100
+ <ClientRouter />
101
+ <script is:inline>
102
+ if (localStorage.getItem("theme") === "dark") {
103
+ document.documentElement.classList.add("dark");
104
+ }
105
+ </script>
106
+ <slot name="head" />
107
+ </head>
108
+ <body class="min-h-screen bg-background text-foreground antialiased">
109
+ <SidebarProvider>
110
+ <Sidebar collapsible={collapsible}>
111
+ <SidebarHeader>
112
+ <SidebarMenu>
113
+ <SidebarMenuItem>
114
+ <SidebarMenuButton href="/" size="lg">
115
+ <span class="font-semibold">{siteTitle}</span>
116
+ </SidebarMenuButton>
117
+ </SidebarMenuItem>
118
+ </SidebarMenu>
119
+ </SidebarHeader>
120
+ <SidebarSeparator />
121
+ <SidebarContent>
122
+ {nav.map((section) => (
123
+ <SidebarGroup>
124
+ <SidebarGroupLabel>{section.label}</SidebarGroupLabel>
125
+ <SidebarGroupContent>
126
+ <SidebarNavTree items={section.items} currentPath={currentPath} />
127
+ </SidebarGroupContent>
128
+ </SidebarGroup>
129
+ ))}
130
+ </SidebarContent>
131
+ <SidebarRail />
132
+ </Sidebar>
133
+ <SidebarInset>
134
+ <header class="sticky top-0 z-40 flex items-center gap-3 border-b border-border bg-background/95 px-4 py-3 backdrop-blur-sm">
135
+ <SidebarTrigger />
136
+ <Separator orientation="vertical" class="h-5" />
137
+ <span class="text-sm font-medium" data-page-title>{title}</span>
138
+ <div class="ml-auto">
139
+ <slot name="header-actions">
140
+ <ThemeToggle />
141
+ </slot>
142
+ </div>
143
+ </header>
144
+ <main class="flex-1 px-6 py-8" transition:animate="fade">
145
+ <div class:list={["mx-auto", maxWidth]}>
146
+ <slot />
147
+ </div>
148
+ </main>
149
+ <slot name="footer" />
150
+ </SidebarInset>
151
+ </SidebarProvider>
152
+
153
+ <script>
154
+ import "../sidebar/sidebar.ts";
155
+
156
+ function updateActiveNav() {
157
+ const path = window.location.pathname.replace(/\/$/, "") || "/";
158
+ document.querySelectorAll("[data-nav-href]").forEach((el) => {
159
+ const href = el.getAttribute("data-nav-href")?.replace(/\/$/, "") || "";
160
+ if (href === path) {
161
+ el.setAttribute("data-active", "true");
162
+ let parent = el.parentElement;
163
+ while (parent) {
164
+ if (parent.tagName === "DETAILS") {
165
+ parent.setAttribute("open", "");
166
+ }
167
+ parent = parent.parentElement;
168
+ }
169
+ } else {
170
+ el.removeAttribute("data-active");
171
+ }
172
+ });
173
+ }
174
+
175
+ updateActiveNav();
176
+
177
+ document.addEventListener("astro:before-swap", (e: any) => {
178
+ const dark = document.documentElement.classList.contains("dark");
179
+ e.newDocument.documentElement.classList.toggle("dark", dark);
180
+ });
181
+
182
+ document.addEventListener("astro:after-swap", () => {
183
+ updateActiveNav();
184
+ });
185
+ </script>
186
+ </body>
187
+ </html>
@@ -0,0 +1 @@
1
+ export { default as DocsLayout } from "./DocsLayout.astro";
@@ -0,0 +1,12 @@
1
+ ---
2
+ interface Props {
3
+ class?: string;
4
+ [key: string]: unknown;
5
+ }
6
+
7
+ const { class: className, ...rest } = Astro.props;
8
+ ---
9
+
10
+ <div class={className} data-component="drawer" {...rest}>
11
+ <slot />
12
+ </div>
@@ -0,0 +1,12 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const drawerClose = tv({
5
+ base: "inline-flex items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
6
+ });
7
+
8
+ interface Props { class?: string; [key: string]: unknown; }
9
+ const { class: className, ...rest } = Astro.props;
10
+ ---
11
+
12
+ <button class={drawerClose({ class: className })} data-part="close" type="button" {...rest}><slot /></button>
@@ -0,0 +1,46 @@
1
+ ---
2
+ import { tv, type VariantProps } from "tailwind-variants";
3
+
4
+ const drawerBackdrop = tv({
5
+ base: "fixed inset-0 z-50 bg-black/80 transition-opacity",
6
+ });
7
+
8
+ const drawerContent = tv({
9
+ base: "fixed z-[51] flex flex-col bg-background transition-transform",
10
+ variants: {
11
+ direction: {
12
+ down: "inset-x-0 bottom-0 mt-24 rounded-t-[10px] border-t",
13
+ up: "inset-x-0 top-0 mb-24 rounded-b-[10px] border-b",
14
+ left: "inset-y-0 left-0 mr-24 w-3/4 rounded-r-[10px] border-r sm:max-w-sm",
15
+ right: "inset-y-0 right-0 ml-24 w-3/4 rounded-l-[10px] border-l sm:max-w-sm",
16
+ },
17
+ },
18
+ defaultVariants: {
19
+ direction: "down",
20
+ },
21
+ });
22
+
23
+ type ContentVariants = VariantProps<typeof drawerContent>;
24
+
25
+ interface Props {
26
+ direction?: ContentVariants["direction"];
27
+ class?: string;
28
+ [key: string]: unknown;
29
+ }
30
+
31
+ const { direction = "down", class: className, ...rest } = Astro.props;
32
+ ---
33
+
34
+ <div class={drawerBackdrop()} data-part="backdrop" data-state="closed" hidden></div>
35
+ <div
36
+ class={drawerContent({ direction, class: className })}
37
+ data-part="content"
38
+ data-direction={direction}
39
+ role="dialog"
40
+ aria-modal="true"
41
+ data-state="closed"
42
+ hidden
43
+ {...rest}
44
+ >
45
+ <slot />
46
+ </div>
@@ -0,0 +1,10 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const drawerDescription = tv({ base: "text-sm text-muted-foreground" });
5
+
6
+ interface Props { class?: string; [key: string]: unknown; }
7
+ const { class: className, ...rest } = Astro.props;
8
+ ---
9
+
10
+ <p class={drawerDescription({ class: className })} data-part="description" {...rest}><slot /></p>
@@ -0,0 +1,10 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const drawerFooter = tv({ base: "mt-auto flex flex-col gap-2 p-4" });
5
+
6
+ interface Props { class?: string; [key: string]: unknown; }
7
+ const { class: className, ...rest } = Astro.props;
8
+ ---
9
+
10
+ <div class={drawerFooter({ class: className })} {...rest}><slot /></div>
@@ -0,0 +1,16 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const drawerHandle = tv({
5
+ base: "mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted",
6
+ });
7
+
8
+ interface Props {
9
+ class?: string;
10
+ [key: string]: unknown;
11
+ }
12
+
13
+ const { class: className, ...rest } = Astro.props;
14
+ ---
15
+
16
+ <div class={drawerHandle({ class: className })} data-part="handle" aria-hidden="true" {...rest}></div>
@@ -0,0 +1,10 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const drawerHeader = tv({ base: "grid gap-1.5 p-4 text-center sm:text-left" });
5
+
6
+ interface Props { class?: string; [key: string]: unknown; }
7
+ const { class: className, ...rest } = Astro.props;
8
+ ---
9
+
10
+ <div class={drawerHeader({ class: className })} {...rest}><slot /></div>
@@ -0,0 +1,10 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const drawerTitle = tv({ base: "text-lg font-semibold leading-none tracking-tight" });
5
+
6
+ interface Props { class?: string; [key: string]: unknown; }
7
+ const { class: className, ...rest } = Astro.props;
8
+ ---
9
+
10
+ <h2 class={drawerTitle({ class: className })} data-part="title" {...rest}><slot /></h2>
@@ -0,0 +1,25 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const drawerTrigger = tv({
5
+ base: "inline-flex items-center justify-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
6
+ });
7
+
8
+ interface Props {
9
+ class?: string;
10
+ [key: string]: unknown;
11
+ }
12
+
13
+ const { class: className, ...rest } = Astro.props;
14
+ ---
15
+
16
+ <button
17
+ class={drawerTrigger({ class: className })}
18
+ data-part="trigger"
19
+ type="button"
20
+ aria-haspopup="dialog"
21
+ aria-expanded="false"
22
+ {...rest}
23
+ >
24
+ <slot />
25
+ </button>
@@ -0,0 +1,262 @@
1
+ import { FocusTrap } from "@dogsbay/primitives/focus-trap";
2
+ import { ScrollLock } from "@dogsbay/primitives/scroll-lock";
3
+ import { markOthersInert } from "@dogsbay/primitives/inert";
4
+ import { createPortal } from "@dogsbay/primitives/portal";
5
+
6
+ // Swipe constants (matching Base UI)
7
+ const SWIPE_CLOSE_RATIO = 0.5;
8
+ const VELOCITY_THRESHOLD = 0.4;
9
+ const DAMPING_FACTOR = 0.5;
10
+
11
+ interface DrawerInstance {
12
+ root: HTMLElement;
13
+ trigger: HTMLElement | null;
14
+ content: HTMLElement;
15
+ backdrop: HTMLElement;
16
+ handle: HTMLElement | null;
17
+ focusTrap: FocusTrap;
18
+ cleanupPortal: (() => void) | null;
19
+ cleanupInert: (() => void) | null;
20
+ open: boolean;
21
+ direction: "down" | "up" | "left" | "right";
22
+ // Swipe state
23
+ swiping: boolean;
24
+ swipeStart: { x: number; y: number; time: number };
25
+ currentDisplacement: number;
26
+ }
27
+
28
+ const instances = new WeakMap<HTMLElement, DrawerInstance>();
29
+
30
+ let idCounter = 0;
31
+ function uniqueId(prefix: string) {
32
+ return `${prefix}-${++idCounter}`;
33
+ }
34
+
35
+ function init(root: HTMLElement) {
36
+ if (root.hasAttribute("data-initialized")) return;
37
+ root.setAttribute("data-initialized", "");
38
+
39
+ const ownPart = (selector: string) => {
40
+ const all = root.querySelectorAll(selector);
41
+ for (let i = 0; i < all.length; i++) {
42
+ const el = all[i] as HTMLElement;
43
+ if (el.closest("[data-component='drawer']") === root) return el;
44
+ }
45
+ return null;
46
+ };
47
+
48
+ const content = ownPart("[data-part='content']");
49
+ const backdrop = ownPart("[data-part='backdrop']");
50
+ const trigger = ownPart("[data-part='trigger']");
51
+ const handle = ownPart("[data-part='handle']");
52
+ if (!content || !backdrop) return;
53
+
54
+ const direction = (content.getAttribute("data-direction") || "down") as DrawerInstance["direction"];
55
+ const dialogId = content.id || uniqueId("base-drawer");
56
+ content.id = dialogId;
57
+
58
+ if (trigger) {
59
+ trigger.setAttribute("aria-controls", dialogId);
60
+ trigger.setAttribute("aria-expanded", "false");
61
+ }
62
+
63
+ const title = content.querySelector("[data-part='title']") as HTMLElement | null;
64
+ const description = content.querySelector("[data-part='description']") as HTMLElement | null;
65
+ if (title) {
66
+ const titleId = title.id || uniqueId("base-drawer-title");
67
+ title.id = titleId;
68
+ content.setAttribute("aria-labelledby", titleId);
69
+ }
70
+ if (description) {
71
+ const descId = description.id || uniqueId("base-drawer-desc");
72
+ description.id = descId;
73
+ content.setAttribute("aria-describedby", descId);
74
+ }
75
+
76
+ const instance: DrawerInstance = {
77
+ root,
78
+ trigger,
79
+ content,
80
+ backdrop,
81
+ handle,
82
+ focusTrap: new FocusTrap(content),
83
+ cleanupPortal: null,
84
+ cleanupInert: null,
85
+ open: false,
86
+ direction,
87
+ swiping: false,
88
+ swipeStart: { x: 0, y: 0, time: 0 },
89
+ currentDisplacement: 0,
90
+ };
91
+
92
+ instances.set(root, instance);
93
+
94
+ // Trigger click
95
+ trigger?.addEventListener("click", () => openDrawer(instance));
96
+
97
+ // Close button
98
+ content.addEventListener("click", (e) => {
99
+ if ((e.target as HTMLElement).closest("[data-part='close']")) {
100
+ closeDrawer(instance);
101
+ }
102
+ });
103
+
104
+ // Backdrop click closes
105
+ backdrop.addEventListener("click", () => closeDrawer(instance));
106
+
107
+ // Escape closes
108
+ content.addEventListener("keydown", (e) => {
109
+ if (e.key === "Escape") {
110
+ e.preventDefault();
111
+ closeDrawer(instance);
112
+ }
113
+ });
114
+
115
+ // Swipe-to-dismiss on handle and content
116
+ setupSwipe(instance);
117
+ }
118
+
119
+ function setupSwipe(instance: DrawerInstance) {
120
+ const { content, handle, direction } = instance;
121
+ const swipeTarget = handle || content;
122
+
123
+ swipeTarget.addEventListener("pointerdown", (e) => {
124
+ if (!instance.open) return;
125
+ instance.swiping = true;
126
+ instance.swipeStart = { x: e.clientX, y: e.clientY, time: Date.now() };
127
+ instance.currentDisplacement = 0;
128
+ content.style.transition = "none";
129
+ swipeTarget.setPointerCapture(e.pointerId);
130
+ });
131
+
132
+ swipeTarget.addEventListener("pointermove", (e) => {
133
+ if (!instance.swiping) return;
134
+
135
+ const dx = e.clientX - instance.swipeStart.x;
136
+ const dy = e.clientY - instance.swipeStart.y;
137
+
138
+ // Get displacement in the swipe direction
139
+ let displacement: number;
140
+ switch (direction) {
141
+ case "down": displacement = Math.max(0, dy); break;
142
+ case "up": displacement = Math.max(0, -dy); break;
143
+ case "left": displacement = Math.max(0, -dx); break;
144
+ case "right": displacement = Math.max(0, dx); break;
145
+ }
146
+
147
+ instance.currentDisplacement = displacement;
148
+
149
+ // Apply transform with damping for over-drag
150
+ const size = getContentSize(instance);
151
+ const damped = displacement > size
152
+ ? size + (displacement - size) * DAMPING_FACTOR
153
+ : displacement;
154
+
155
+ applySwipeTransform(instance, damped);
156
+
157
+ // Update backdrop opacity
158
+ const progress = Math.max(0, 1 - damped / size);
159
+ instance.backdrop.style.opacity = String(progress);
160
+
161
+ content.setAttribute("data-swiping", "");
162
+ });
163
+
164
+ swipeTarget.addEventListener("pointerup", (e) => {
165
+ if (!instance.swiping) return;
166
+ instance.swiping = false;
167
+ content.removeAttribute("data-swiping");
168
+ content.style.transition = "";
169
+
170
+ const displacement = instance.currentDisplacement;
171
+ const size = getContentSize(instance);
172
+ const elapsed = Date.now() - instance.swipeStart.time;
173
+ const velocity = displacement / Math.max(elapsed, 1);
174
+
175
+ // Close if past threshold or velocity is high enough
176
+ if (displacement / size > SWIPE_CLOSE_RATIO || velocity > VELOCITY_THRESHOLD) {
177
+ // Animate to closed position then close
178
+ applySwipeTransform(instance, size);
179
+ instance.backdrop.style.opacity = "0";
180
+ setTimeout(() => closeDrawer(instance), 200);
181
+ } else {
182
+ // Snap back
183
+ applySwipeTransform(instance, 0);
184
+ instance.backdrop.style.opacity = "";
185
+ }
186
+ });
187
+ }
188
+
189
+ function getContentSize(instance: DrawerInstance): number {
190
+ const { content, direction } = instance;
191
+ return direction === "down" || direction === "up"
192
+ ? content.offsetHeight
193
+ : content.offsetWidth;
194
+ }
195
+
196
+ function applySwipeTransform(instance: DrawerInstance, displacement: number) {
197
+ const { content, direction } = instance;
198
+ switch (direction) {
199
+ case "down": content.style.transform = `translateY(${displacement}px)`; break;
200
+ case "up": content.style.transform = `translateY(${-displacement}px)`; break;
201
+ case "left": content.style.transform = `translateX(${-displacement}px)`; break;
202
+ case "right": content.style.transform = `translateX(${displacement}px)`; break;
203
+ }
204
+ }
205
+
206
+ function openDrawer(instance: DrawerInstance) {
207
+ if (instance.open) return;
208
+ instance.open = true;
209
+
210
+ const contentPortal = createPortal(instance.content, instance.content.id);
211
+ const backdropPortal = createPortal(instance.backdrop, `${instance.content.id}-backdrop`);
212
+ instance.cleanupPortal = () => { contentPortal(); backdropPortal(); };
213
+
214
+ instance.content.hidden = false;
215
+ instance.backdrop.hidden = false;
216
+ instance.content.setAttribute("data-state", "open");
217
+ instance.backdrop.setAttribute("data-state", "open");
218
+ instance.content.style.transform = "";
219
+ instance.backdrop.style.opacity = "";
220
+ instance.trigger?.setAttribute("aria-expanded", "true");
221
+
222
+ ScrollLock.activate();
223
+ instance.cleanupInert = markOthersInert(instance.content);
224
+ instance.focusTrap.activate();
225
+
226
+ instance.root.dispatchEvent(new CustomEvent("base-drawer:open", { bubbles: true }));
227
+ }
228
+
229
+ function closeDrawer(instance: DrawerInstance) {
230
+ if (!instance.open) return;
231
+ instance.open = false;
232
+
233
+ instance.focusTrap.deactivate();
234
+ instance.cleanupInert?.();
235
+ instance.cleanupInert = null;
236
+ ScrollLock.deactivate();
237
+
238
+ instance.content.setAttribute("data-state", "closed");
239
+ instance.backdrop.setAttribute("data-state", "closed");
240
+ instance.content.style.transform = "";
241
+ instance.backdrop.style.opacity = "";
242
+ instance.trigger?.setAttribute("aria-expanded", "false");
243
+
244
+ instance.content.hidden = true;
245
+ instance.backdrop.hidden = true;
246
+ instance.cleanupPortal?.();
247
+ instance.cleanupPortal = null;
248
+
249
+ instance.trigger?.focus();
250
+ instance.root.dispatchEvent(new CustomEvent("base-drawer:close", { bubbles: true }));
251
+ }
252
+
253
+ function initAll() {
254
+ document.querySelectorAll<HTMLElement>("[data-component='drawer']").forEach(init);
255
+ }
256
+
257
+ if (document.readyState === "loading") {
258
+ document.addEventListener("DOMContentLoaded", initAll);
259
+ } else {
260
+ initAll();
261
+ }
262
+ document.addEventListener("astro:page-load", initAll);
@@ -0,0 +1,9 @@
1
+ export { default as Drawer } from "./Drawer.astro";
2
+ export { default as DrawerTrigger } from "./DrawerTrigger.astro";
3
+ export { default as DrawerContent } from "./DrawerContent.astro";
4
+ export { default as DrawerHandle } from "./DrawerHandle.astro";
5
+ export { default as DrawerHeader } from "./DrawerHeader.astro";
6
+ export { default as DrawerTitle } from "./DrawerTitle.astro";
7
+ export { default as DrawerDescription } from "./DrawerDescription.astro";
8
+ export { default as DrawerFooter } from "./DrawerFooter.astro";
9
+ export { default as DrawerClose } from "./DrawerClose.astro";
@@ -0,0 +1,22 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const dropdownMenu = tv({
5
+ base: "relative inline-block",
6
+ });
7
+
8
+ interface Props {
9
+ class?: string;
10
+ [key: string]: unknown;
11
+ }
12
+
13
+ const { class: className, ...rest } = Astro.props;
14
+ ---
15
+
16
+ <div
17
+ class={dropdownMenu({ class: className })}
18
+ data-component="dropdown-menu"
19
+ {...rest}
20
+ >
21
+ <slot />
22
+ </div>
@@ -0,0 +1,33 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const dropdownMenuContent = tv({
5
+ base: "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
6
+ });
7
+
8
+ interface Props {
9
+ side?: "top" | "bottom" | "left" | "right";
10
+ align?: "start" | "center" | "end";
11
+ class?: string;
12
+ [key: string]: unknown;
13
+ }
14
+
15
+ const {
16
+ side = "bottom",
17
+ align = "start",
18
+ class: className,
19
+ ...rest
20
+ } = Astro.props;
21
+ ---
22
+
23
+ <div
24
+ class={dropdownMenuContent({ class: className })}
25
+ data-part="content"
26
+ data-side={side}
27
+ data-align={align}
28
+ role="menu"
29
+ hidden
30
+ {...rest}
31
+ >
32
+ <slot />
33
+ </div>