@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,18 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const tableRow = tv({
5
+ base: "border-b transition-colors hover:bg-muted/50 data-[state=selected]: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
+ <tr class={tableRow({ class: className })} {...rest}>
17
+ <slot />
18
+ </tr>
@@ -0,0 +1,8 @@
1
+ export { default as Table } from "./Table.astro";
2
+ export { default as TableHeader } from "./TableHeader.astro";
3
+ export { default as TableBody } from "./TableBody.astro";
4
+ export { default as TableFooter } from "./TableFooter.astro";
5
+ export { default as TableRow } from "./TableRow.astro";
6
+ export { default as TableHead } from "./TableHead.astro";
7
+ export { default as TableCell } from "./TableCell.astro";
8
+ export { default as TableCaption } from "./TableCaption.astro";
@@ -0,0 +1,37 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const tabs = tv({
5
+ base: "w-full my-4 border border-border rounded-lg overflow-hidden",
6
+ });
7
+
8
+ interface Props {
9
+ defaultValue?: string;
10
+ orientation?: "horizontal" | "vertical";
11
+ syncKey?: string;
12
+ class?: string;
13
+ [key: string]: unknown;
14
+ }
15
+
16
+ const {
17
+ defaultValue,
18
+ orientation = "horizontal",
19
+ syncKey,
20
+ class: className,
21
+ ...rest
22
+ } = Astro.props;
23
+ ---
24
+
25
+ <db-tabs
26
+ class={tabs({ class: className })}
27
+ default-value={defaultValue}
28
+ orientation={orientation}
29
+ sync-key={syncKey}
30
+ {...rest}
31
+ >
32
+ <slot />
33
+ </db-tabs>
34
+
35
+ <script>
36
+ import "@dogsbay/elements/db-tabs";
37
+ </script>
@@ -0,0 +1,32 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const tabsContent = tv({
5
+ base: "px-4 py-3 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 [&>:first-child]:mt-0 [&>:last-child]:mb-0",
6
+ });
7
+
8
+ interface Props {
9
+ value: string;
10
+ default?: boolean;
11
+ class?: string;
12
+ [key: string]: unknown;
13
+ }
14
+
15
+ const {
16
+ value,
17
+ default: isDefault = false,
18
+ class: className,
19
+ ...rest
20
+ } = Astro.props;
21
+ ---
22
+
23
+ <div
24
+ class={tabsContent({ class: className })}
25
+ data-part="content"
26
+ data-value={value}
27
+ role="tabpanel"
28
+ hidden={!isDefault}
29
+ {...rest}
30
+ >
31
+ <slot />
32
+ </div>
@@ -0,0 +1,23 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const tabsList = tv({
5
+ base: "flex items-center bg-muted p-1 text-muted-foreground border-b border-border",
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={tabsList({ class: className })}
18
+ data-part="list"
19
+ role="tablist"
20
+ {...rest}
21
+ >
22
+ <slot />
23
+ </div>
@@ -0,0 +1,29 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const tabsTrigger = tv({
5
+ base: "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 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 aria-selected:bg-background aria-selected:text-foreground aria-selected:shadow-sm",
6
+ });
7
+
8
+ interface Props {
9
+ value: string;
10
+ disabled?: boolean;
11
+ class?: string;
12
+ [key: string]: unknown;
13
+ }
14
+
15
+ const { value, disabled = false, class: className, ...rest } = Astro.props;
16
+ ---
17
+
18
+ <button
19
+ class={tabsTrigger({ class: className })}
20
+ data-part="trigger"
21
+ data-value={value}
22
+ role="tab"
23
+ type="button"
24
+ disabled={disabled || undefined}
25
+ aria-disabled={disabled ? "true" : undefined}
26
+ {...rest}
27
+ >
28
+ <slot />
29
+ </button>
@@ -0,0 +1,4 @@
1
+ export { default as Tabs } from "./Tabs.astro";
2
+ export { default as TabsList } from "./TabsList.astro";
3
+ export { default as TabsTrigger } from "./TabsTrigger.astro";
4
+ export { default as TabsContent } from "./TabsContent.astro";
@@ -0,0 +1,220 @@
1
+ import { CompositeNavigation } from "@dogsbay/primitives/composite";
2
+ import type { Orientation } from "@dogsbay/primitives/composite";
3
+
4
+ interface TabsInstance {
5
+ root: HTMLElement;
6
+ list: HTMLElement;
7
+ triggers: HTMLElement[];
8
+ panels: HTMLElement[];
9
+ nav: CompositeNavigation;
10
+ value: string;
11
+ syncKey: string | null;
12
+ orientation: Orientation;
13
+ }
14
+
15
+ const instances = new WeakMap<HTMLElement, TabsInstance>();
16
+
17
+ let idCounter = 0;
18
+ function uniqueId(prefix: string) {
19
+ return `${prefix}-${++idCounter}`;
20
+ }
21
+
22
+ function initTabs(root: HTMLElement) {
23
+ if (instances.has(root)) return;
24
+
25
+ const list = root.querySelector("[data-part='list']") as HTMLElement | null;
26
+ if (!list) return;
27
+
28
+ const triggers = Array.from(
29
+ list.querySelectorAll("[data-part='trigger']"),
30
+ ) as HTMLElement[];
31
+ const panels = Array.from(
32
+ root.querySelectorAll("[data-part='content']"),
33
+ ) as HTMLElement[];
34
+
35
+ const orientation = (root.dataset.orientation as Orientation) || "horizontal";
36
+ const defaultValue =
37
+ root.dataset.defaultValue || triggers[0]?.dataset.value || "";
38
+ const syncKey = root.dataset.syncKey || null;
39
+
40
+ // Set orientation on tablist
41
+ list.setAttribute("aria-orientation", orientation);
42
+
43
+ // Generate IDs and link triggers ↔ panels
44
+ triggers.forEach((trigger) => {
45
+ const value = trigger.dataset.value!;
46
+ const triggerId = trigger.id || uniqueId("base-tab");
47
+ trigger.id = triggerId;
48
+
49
+ const panel = panels.find((p) => p.dataset.value === value);
50
+ if (panel) {
51
+ const panelId = panel.id || uniqueId("base-tabpanel");
52
+ panel.id = panelId;
53
+ trigger.setAttribute("aria-controls", panelId);
54
+ panel.setAttribute("aria-labelledby", triggerId);
55
+ }
56
+ });
57
+
58
+ // Set up composite navigation
59
+ const nav = new CompositeNavigation(list, "[data-part='trigger']", {
60
+ orientation,
61
+ loop: true,
62
+ homeEnd: true,
63
+ onHighlightedIndexChange: () => {
64
+ // Selection follows focus — activate the focused tab
65
+ const focused = document.activeElement as HTMLElement;
66
+ const value = focused?.dataset?.value;
67
+ if (value) selectTab(root, value);
68
+ },
69
+ });
70
+
71
+ const instance: TabsInstance = {
72
+ root,
73
+ list,
74
+ triggers,
75
+ panels,
76
+ nav,
77
+ value: "",
78
+ syncKey,
79
+ orientation,
80
+ };
81
+
82
+ instances.set(root, instance);
83
+
84
+ // Click handler — also handles non-keyboard activation
85
+ list.addEventListener("click", (e) => {
86
+ const trigger = (e.target as HTMLElement).closest(
87
+ "[data-part='trigger']",
88
+ ) as HTMLElement | null;
89
+ if (!trigger || trigger.hasAttribute("disabled")) return;
90
+ const value = trigger.dataset.value;
91
+ if (value) selectTab(root, value);
92
+ });
93
+
94
+ // Activate navigation
95
+ nav.activate();
96
+
97
+ // Sync: read initial value from localStorage or hash
98
+ let initialValue = defaultValue;
99
+ if (syncKey) {
100
+ const stored = localStorage.getItem(`base-tabs-sync:${syncKey}`);
101
+ if (stored && triggers.some((t) => t.dataset.value === stored)) {
102
+ initialValue = stored;
103
+ }
104
+ }
105
+
106
+ // Deep link: check URL hash
107
+ const hash = window.location.hash.slice(1);
108
+ if (hash && triggers.some((t) => t.dataset.value === hash)) {
109
+ initialValue = hash;
110
+ }
111
+
112
+ // Set initial selection
113
+ selectTab(root, initialValue);
114
+
115
+ // Listen for sync events from other tabs instances with same syncKey
116
+ if (syncKey) {
117
+ window.addEventListener("storage", (e) => {
118
+ if (e.key !== `base-tabs-sync:${syncKey}`) return;
119
+ const newValue = e.newValue;
120
+ if (newValue && instance.value !== newValue) {
121
+ selectTab(root, newValue);
122
+ }
123
+ });
124
+
125
+ // Listen for same-page sync (custom event)
126
+ document.addEventListener(`base-tabs-sync:${syncKey}`, ((
127
+ e: CustomEvent,
128
+ ) => {
129
+ const newValue = e.detail.value;
130
+ if (newValue && instance.value !== newValue) {
131
+ selectTab(root, newValue);
132
+ }
133
+ }) as EventListener);
134
+ }
135
+ }
136
+
137
+ function selectTab(root: HTMLElement, value: string) {
138
+ const instance = instances.get(root);
139
+ if (!instance) return;
140
+ if (instance.value === value) return;
141
+
142
+ instance.value = value;
143
+
144
+ // Update triggers
145
+ instance.triggers.forEach((trigger) => {
146
+ const isSelected = trigger.dataset.value === value;
147
+ trigger.setAttribute("aria-selected", String(isSelected));
148
+ trigger.setAttribute("tabindex", isSelected ? "0" : "-1");
149
+ if (isSelected) {
150
+ trigger.setAttribute("data-state", "active");
151
+ } else {
152
+ trigger.removeAttribute("data-state");
153
+ }
154
+ });
155
+
156
+ // Update panels
157
+ instance.panels.forEach((panel) => {
158
+ const isActive = panel.dataset.value === value;
159
+ panel.hidden = !isActive;
160
+ if (isActive) {
161
+ panel.setAttribute("data-state", "active");
162
+ // Ensure panel is focusable for keyboard users
163
+ if (!panel.hasAttribute("tabindex")) {
164
+ panel.setAttribute("tabindex", "0");
165
+ }
166
+ // Lazy init: initialize any nested tabs that haven't been set up yet
167
+ panel.querySelectorAll("[data-component='tabs']").forEach((nestedEl) => {
168
+ if (!instances.has(nestedEl as HTMLElement)) {
169
+ initTabs(nestedEl as HTMLElement);
170
+ }
171
+ });
172
+ } else {
173
+ panel.removeAttribute("data-state");
174
+ }
175
+ });
176
+
177
+ // Update hash
178
+ if (window.location.hash.slice(1) !== value) {
179
+ // Only update hash if the user interacted (not on initial load)
180
+ // We skip this to avoid scroll jumps on page load
181
+ }
182
+
183
+ // Sync to localStorage and other instances
184
+ if (instance.syncKey) {
185
+ localStorage.setItem(`base-tabs-sync:${instance.syncKey}`, value);
186
+ document.dispatchEvent(
187
+ new CustomEvent(`base-tabs-sync:${instance.syncKey}`, {
188
+ detail: { value, source: root },
189
+ }),
190
+ );
191
+ }
192
+
193
+ // Dispatch change event
194
+ root.dispatchEvent(
195
+ new CustomEvent("base-tabs:change", {
196
+ detail: { value },
197
+ bubbles: true,
198
+ }),
199
+ );
200
+ }
201
+
202
+ // Auto-initialize — skip tabs inside hidden panels (they'll be lazy-inited)
203
+ function init() {
204
+ document.querySelectorAll("[data-component='tabs']").forEach((el) => {
205
+ const tabsEl = el as HTMLElement;
206
+ // Skip if inside a hidden tabpanel (will be lazy-inited when panel activates)
207
+ const parentPanel = tabsEl.closest("[data-part='content'][hidden]");
208
+ if (parentPanel) return;
209
+ initTabs(tabsEl);
210
+ });
211
+ }
212
+
213
+ if (document.readyState === "loading") {
214
+ document.addEventListener("DOMContentLoaded", init);
215
+ } else {
216
+ init();
217
+ }
218
+ document.addEventListener("astro:page-load", init);
219
+
220
+ export { initTabs, selectTab };
@@ -0,0 +1,25 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const textarea = tv({
5
+ base: "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
6
+ variants: {
7
+ invalid: {
8
+ true: "border-destructive focus-visible:ring-destructive",
9
+ },
10
+ },
11
+ });
12
+
13
+ interface Props {
14
+ invalid?: boolean;
15
+ class?: string;
16
+ [key: string]: unknown;
17
+ }
18
+ const { invalid = false, class: className, ...rest } = Astro.props;
19
+ ---
20
+
21
+ <textarea
22
+ class={textarea({ invalid: invalid || undefined, class: className })}
23
+ aria-invalid={invalid ? "true" : undefined}
24
+ data-invalid={invalid || undefined}
25
+ {...rest}></textarea>
@@ -0,0 +1 @@
1
+ export { default as Textarea } from "./Textarea.astro";
@@ -0,0 +1,67 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const themeToggle = tv({
5
+ base: "inline-flex h-9 w-9 items-center justify-center rounded-md border border-border bg-background text-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
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={themeToggle({ class: className })}
18
+ data-component="theme-toggle"
19
+ type="button"
20
+ aria-label="Toggle dark mode"
21
+ {...rest}
22
+ >
23
+ <svg
24
+ class="dark:hidden h-4 w-4"
25
+ xmlns="http://www.w3.org/2000/svg"
26
+ fill="none"
27
+ viewBox="0 0 24 24"
28
+ stroke-width="2"
29
+ stroke="currentColor"
30
+ aria-hidden="true"
31
+ >
32
+ <path
33
+ stroke-linecap="round"
34
+ stroke-linejoin="round"
35
+ d="M21.752 15.002A9.72 9.72 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z"
36
+ ></path>
37
+ </svg>
38
+ <svg
39
+ class="hidden dark:block h-4 w-4"
40
+ xmlns="http://www.w3.org/2000/svg"
41
+ fill="none"
42
+ viewBox="0 0 24 24"
43
+ stroke-width="2"
44
+ stroke="currentColor"
45
+ aria-hidden="true"
46
+ >
47
+ <path
48
+ stroke-linecap="round"
49
+ stroke-linejoin="round"
50
+ d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z"
51
+ ></path>
52
+ </svg>
53
+ </button>
54
+
55
+ <script>
56
+ function setupThemeToggles() {
57
+ document.querySelectorAll("[data-component='theme-toggle']").forEach((btn) => {
58
+ btn.addEventListener("click", () => {
59
+ const isDark = document.documentElement.classList.toggle("dark");
60
+ localStorage.setItem("theme", isDark ? "dark" : "light");
61
+ });
62
+ });
63
+ }
64
+
65
+ setupThemeToggles();
66
+ document.addEventListener("astro:after-swap", setupThemeToggles);
67
+ </script>
@@ -0,0 +1 @@
1
+ export { default as ThemeToggle } from "./ThemeToggle.astro";
@@ -0,0 +1,39 @@
1
+ ---
2
+ import { tv } from "tailwind-variants";
3
+
4
+ const toaster = tv({
5
+ base: "fixed z-[100] flex max-h-screen w-full flex-col-reverse gap-2 p-4 sm:flex-col",
6
+ variants: {
7
+ position: {
8
+ "top-right": "top-0 right-0 sm:max-w-[420px]",
9
+ "top-left": "top-0 left-0 sm:max-w-[420px]",
10
+ "bottom-right": "bottom-0 right-0 sm:max-w-[420px]",
11
+ "bottom-left": "bottom-0 left-0 sm:max-w-[420px]",
12
+ "top-center": "top-0 left-1/2 -translate-x-1/2 sm:max-w-[420px]",
13
+ "bottom-center": "bottom-0 left-1/2 -translate-x-1/2 sm:max-w-[420px]",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ position: "bottom-right",
18
+ },
19
+ });
20
+
21
+ interface Props {
22
+ position?: "top-right" | "top-left" | "bottom-right" | "bottom-left" | "top-center" | "bottom-center";
23
+ class?: string;
24
+ [key: string]: unknown;
25
+ }
26
+
27
+ const { position = "bottom-right", class: className, ...rest } = Astro.props;
28
+ ---
29
+
30
+ <div
31
+ class={toaster({ position, class: className })}
32
+ data-component="toaster"
33
+ data-position={position}
34
+ role="region"
35
+ aria-label="Notifications"
36
+ aria-live="polite"
37
+ {...rest}
38
+ >
39
+ </div>
@@ -0,0 +1,3 @@
1
+ export { default as Toaster } from "./Toaster.astro";
2
+ export { toast } from "./toast.ts";
3
+ export type { ToastOptions } from "./toast.ts";
@@ -0,0 +1,104 @@
1
+ export interface ToastOptions {
2
+ title: string;
3
+ description?: string;
4
+ variant?: "default" | "destructive";
5
+ duration?: number;
6
+ action?: { label: string; onClick: () => void };
7
+ }
8
+
9
+ let toastId = 0;
10
+
11
+ const variantClasses = {
12
+ default:
13
+ "border bg-background text-foreground",
14
+ destructive:
15
+ "border-destructive bg-destructive text-destructive-foreground",
16
+ };
17
+
18
+ function getToaster(): HTMLElement | null {
19
+ return document.querySelector("[data-component='toaster']");
20
+ }
21
+
22
+ function createToastElement(options: ToastOptions): HTMLElement {
23
+ const id = `toast-${++toastId}`;
24
+ const variant = options.variant ?? "default";
25
+
26
+ const el = document.createElement("div");
27
+ el.id = id;
28
+ el.setAttribute("role", "status");
29
+ el.setAttribute("aria-live", "polite");
30
+ el.setAttribute("aria-atomic", "true");
31
+ el.setAttribute("data-part", "toast");
32
+ el.setAttribute("data-state", "open");
33
+ el.setAttribute("data-variant", variant);
34
+ el.className = `group pointer-events-auto relative flex w-full items-center justify-between gap-4 overflow-hidden rounded-md p-4 shadow-lg transition-all data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-bottom-full data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-right-full ${variantClasses[variant]}`;
35
+
36
+ const content = document.createElement("div");
37
+ content.className = "grid gap-1";
38
+
39
+ const title = document.createElement("div");
40
+ title.className = "text-sm font-semibold";
41
+ title.textContent = options.title;
42
+ content.appendChild(title);
43
+
44
+ if (options.description) {
45
+ const desc = document.createElement("div");
46
+ desc.className = "text-sm opacity-90";
47
+ desc.textContent = options.description;
48
+ content.appendChild(desc);
49
+ }
50
+
51
+ el.appendChild(content);
52
+
53
+ // Action button
54
+ if (options.action) {
55
+ const actionBtn = document.createElement("button");
56
+ actionBtn.className =
57
+ "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2";
58
+ actionBtn.textContent = options.action.label;
59
+ actionBtn.addEventListener("click", () => {
60
+ options.action!.onClick();
61
+ dismiss(el);
62
+ });
63
+ el.appendChild(actionBtn);
64
+ }
65
+
66
+ // Close button
67
+ const closeBtn = document.createElement("button");
68
+ closeBtn.className =
69
+ "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100";
70
+ closeBtn.setAttribute("aria-label", "Close");
71
+ closeBtn.innerHTML =
72
+ '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
73
+ closeBtn.addEventListener("click", () => dismiss(el));
74
+ el.appendChild(closeBtn);
75
+
76
+ return el;
77
+ }
78
+
79
+ function dismiss(el: HTMLElement) {
80
+ el.setAttribute("data-state", "closed");
81
+ setTimeout(() => el.remove(), 200);
82
+ }
83
+
84
+ export function toast(options: ToastOptions) {
85
+ const toaster = getToaster();
86
+ if (!toaster) {
87
+ console.warn("No <Toaster> found in the DOM.");
88
+ return;
89
+ }
90
+
91
+ const el = createToastElement(options);
92
+ toaster.appendChild(el);
93
+
94
+ // Auto-dismiss
95
+ const duration = options.duration ?? 5000;
96
+ if (duration > 0) {
97
+ setTimeout(() => {
98
+ if (el.parentElement) dismiss(el);
99
+ }, duration);
100
+ }
101
+ }
102
+
103
+ // Expose globally for demo page scripts
104
+ (window as unknown as Record<string, unknown>).__toast = toast;