create-nala 3.0.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 (316) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/index.js +227 -0
  4. package/package.json +48 -0
  5. package/template/.env.example +45 -0
  6. package/template/_gitignore +33 -0
  7. package/template/components.json +24 -0
  8. package/template/index.html +30 -0
  9. package/template/package.json +51 -0
  10. package/template/public/favicon.svg +15 -0
  11. package/template/public/icons.svg +24 -0
  12. package/template/public/img/avatar.webp +0 -0
  13. package/template/public/screenshot.png +0 -0
  14. package/template/src/App.vue +15 -0
  15. package/template/src/auto-imports.d.ts +691 -0
  16. package/template/src/components/AppLogo.vue +159 -0
  17. package/template/src/components/EmptyState.vue +60 -0
  18. package/template/src/components/PageHeader.vue +60 -0
  19. package/template/src/components/layout/AppNavbar.vue +159 -0
  20. package/template/src/components/layout/AppSidebar.vue +74 -0
  21. package/template/src/components/layout/CommandSearchDialog.vue +120 -0
  22. package/template/src/components/ui/accordion/Accordion.vue +15 -0
  23. package/template/src/components/ui/accordion/AccordionContent.vue +20 -0
  24. package/template/src/components/ui/accordion/AccordionItem.vue +20 -0
  25. package/template/src/components/ui/accordion/AccordionTrigger.vue +34 -0
  26. package/template/src/components/ui/accordion/index.ts +4 -0
  27. package/template/src/components/ui/alert/Alert.vue +21 -0
  28. package/template/src/components/ui/alert/AlertDescription.vue +17 -0
  29. package/template/src/components/ui/alert/AlertTitle.vue +17 -0
  30. package/template/src/components/ui/alert/index.ts +30 -0
  31. package/template/src/components/ui/alert-dialog/AlertDialog.vue +15 -0
  32. package/template/src/components/ui/alert-dialog/AlertDialogAction.vue +18 -0
  33. package/template/src/components/ui/alert-dialog/AlertDialogCancel.vue +25 -0
  34. package/template/src/components/ui/alert-dialog/AlertDialogContent.vue +44 -0
  35. package/template/src/components/ui/alert-dialog/AlertDialogDescription.vue +23 -0
  36. package/template/src/components/ui/alert-dialog/AlertDialogFooter.vue +22 -0
  37. package/template/src/components/ui/alert-dialog/AlertDialogHeader.vue +17 -0
  38. package/template/src/components/ui/alert-dialog/AlertDialogTitle.vue +21 -0
  39. package/template/src/components/ui/alert-dialog/AlertDialogTrigger.vue +12 -0
  40. package/template/src/components/ui/alert-dialog/index.ts +9 -0
  41. package/template/src/components/ui/avatar/Avatar.vue +50 -0
  42. package/template/src/components/ui/avatar/AvatarFallback.vue +21 -0
  43. package/template/src/components/ui/avatar/AvatarGroup.vue +69 -0
  44. package/template/src/components/ui/avatar/AvatarImage.vue +16 -0
  45. package/template/src/components/ui/avatar/index.ts +4 -0
  46. package/template/src/components/ui/badge/Badge.vue +40 -0
  47. package/template/src/components/ui/badge/index.ts +45 -0
  48. package/template/src/components/ui/breadcrumb/Breadcrumb.vue +17 -0
  49. package/template/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
  50. package/template/src/components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
  51. package/template/src/components/ui/breadcrumb/BreadcrumbLink.vue +21 -0
  52. package/template/src/components/ui/breadcrumb/BreadcrumbList.vue +17 -0
  53. package/template/src/components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
  54. package/template/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
  55. package/template/src/components/ui/breadcrumb/index.ts +7 -0
  56. package/template/src/components/ui/button/Button.spec.ts +46 -0
  57. package/template/src/components/ui/button/Button.vue +31 -0
  58. package/template/src/components/ui/button/index.ts +41 -0
  59. package/template/src/components/ui/calendar/Calendar.vue +106 -0
  60. package/template/src/components/ui/calendar/index.ts +1 -0
  61. package/template/src/components/ui/card/Card.vue +29 -0
  62. package/template/src/components/ui/card/CardAction.vue +17 -0
  63. package/template/src/components/ui/card/CardContent.vue +17 -0
  64. package/template/src/components/ui/card/CardDescription.vue +17 -0
  65. package/template/src/components/ui/card/CardFooter.vue +17 -0
  66. package/template/src/components/ui/card/CardHeader.vue +29 -0
  67. package/template/src/components/ui/card/CardTitle.vue +17 -0
  68. package/template/src/components/ui/card/index.ts +7 -0
  69. package/template/src/components/ui/chart/AreaChart.vue +143 -0
  70. package/template/src/components/ui/chart/BarChart.vue +142 -0
  71. package/template/src/components/ui/chart/DonutChart.vue +75 -0
  72. package/template/src/components/ui/chart/LineChart.vue +132 -0
  73. package/template/src/components/ui/chart/index.ts +4 -0
  74. package/template/src/components/ui/checkbox/Checkbox.vue +57 -0
  75. package/template/src/components/ui/checkbox/index.ts +1 -0
  76. package/template/src/components/ui/collapsible/Collapsible.vue +15 -0
  77. package/template/src/components/ui/collapsible/CollapsibleContent.vue +18 -0
  78. package/template/src/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
  79. package/template/src/components/ui/collapsible/index.ts +3 -0
  80. package/template/src/components/ui/command/Command.vue +88 -0
  81. package/template/src/components/ui/command/CommandDialog.vue +31 -0
  82. package/template/src/components/ui/command/CommandEmpty.vue +27 -0
  83. package/template/src/components/ui/command/CommandGroup.vue +45 -0
  84. package/template/src/components/ui/command/CommandInput.vue +39 -0
  85. package/template/src/components/ui/command/CommandItem.vue +76 -0
  86. package/template/src/components/ui/command/CommandList.vue +25 -0
  87. package/template/src/components/ui/command/CommandSeparator.vue +21 -0
  88. package/template/src/components/ui/command/CommandShortcut.vue +17 -0
  89. package/template/src/components/ui/command/index.ts +25 -0
  90. package/template/src/components/ui/context-menu/ContextMenu.vue +15 -0
  91. package/template/src/components/ui/context-menu/ContextMenuCheckboxItem.vue +36 -0
  92. package/template/src/components/ui/context-menu/ContextMenuContent.vue +33 -0
  93. package/template/src/components/ui/context-menu/ContextMenuGroup.vue +12 -0
  94. package/template/src/components/ui/context-menu/ContextMenuItem.vue +36 -0
  95. package/template/src/components/ui/context-menu/ContextMenuLabel.vue +25 -0
  96. package/template/src/components/ui/context-menu/ContextMenuRadioGroup.vue +18 -0
  97. package/template/src/components/ui/context-menu/ContextMenuRadioItem.vue +36 -0
  98. package/template/src/components/ui/context-menu/ContextMenuSeparator.vue +18 -0
  99. package/template/src/components/ui/context-menu/ContextMenuShortcut.vue +17 -0
  100. package/template/src/components/ui/context-menu/ContextMenuSub.vue +15 -0
  101. package/template/src/components/ui/context-menu/ContextMenuSubContent.vue +36 -0
  102. package/template/src/components/ui/context-menu/ContextMenuSubTrigger.vue +32 -0
  103. package/template/src/components/ui/context-menu/ContextMenuTrigger.vue +13 -0
  104. package/template/src/components/ui/context-menu/index.ts +14 -0
  105. package/template/src/components/ui/date-picker/DatePicker.vue +233 -0
  106. package/template/src/components/ui/date-picker/index.ts +1 -0
  107. package/template/src/components/ui/dialog/Dialog.vue +19 -0
  108. package/template/src/components/ui/dialog/DialogClose.vue +15 -0
  109. package/template/src/components/ui/dialog/DialogContent.vue +53 -0
  110. package/template/src/components/ui/dialog/DialogDescription.vue +23 -0
  111. package/template/src/components/ui/dialog/DialogFooter.vue +27 -0
  112. package/template/src/components/ui/dialog/DialogHeader.vue +17 -0
  113. package/template/src/components/ui/dialog/DialogOverlay.vue +21 -0
  114. package/template/src/components/ui/dialog/DialogScrollContent.vue +59 -0
  115. package/template/src/components/ui/dialog/DialogTitle.vue +23 -0
  116. package/template/src/components/ui/dialog/DialogTrigger.vue +15 -0
  117. package/template/src/components/ui/dialog/index.ts +10 -0
  118. package/template/src/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
  119. package/template/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
  120. package/template/src/components/ui/dropdown-menu/DropdownMenuContent.vue +41 -0
  121. package/template/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
  122. package/template/src/components/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
  123. package/template/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
  124. package/template/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
  125. package/template/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
  126. package/template/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
  127. package/template/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
  128. package/template/src/components/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
  129. package/template/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
  130. package/template/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
  131. package/template/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
  132. package/template/src/components/ui/dropdown-menu/index.ts +16 -0
  133. package/template/src/components/ui/form/FormControl.vue +17 -0
  134. package/template/src/components/ui/form/FormDescription.vue +21 -0
  135. package/template/src/components/ui/form/FormItem.vue +23 -0
  136. package/template/src/components/ui/form/FormLabel.vue +25 -0
  137. package/template/src/components/ui/form/FormMessage.vue +23 -0
  138. package/template/src/components/ui/form/index.ts +7 -0
  139. package/template/src/components/ui/form/injectionKeys.ts +4 -0
  140. package/template/src/components/ui/form/useFormField.ts +30 -0
  141. package/template/src/components/ui/hover-card/HoverCard.vue +19 -0
  142. package/template/src/components/ui/hover-card/HoverCardContent.vue +43 -0
  143. package/template/src/components/ui/hover-card/HoverCardTrigger.vue +15 -0
  144. package/template/src/components/ui/hover-card/index.ts +3 -0
  145. package/template/src/components/ui/index.ts +47 -0
  146. package/template/src/components/ui/input/Input.vue +33 -0
  147. package/template/src/components/ui/input/InputAddon.vue +29 -0
  148. package/template/src/components/ui/input/InputGroup.vue +17 -0
  149. package/template/src/components/ui/input/InputIcon.vue +29 -0
  150. package/template/src/components/ui/input/index.ts +4 -0
  151. package/template/src/components/ui/kbd/Kbd.vue +30 -0
  152. package/template/src/components/ui/kbd/index.ts +1 -0
  153. package/template/src/components/ui/label/Label.vue +26 -0
  154. package/template/src/components/ui/label/index.ts +1 -0
  155. package/template/src/components/ui/loading-bar/LoadingBar.vue +41 -0
  156. package/template/src/components/ui/loading-bar/index.ts +2 -0
  157. package/template/src/components/ui/pagination/Pagination.vue +26 -0
  158. package/template/src/components/ui/pagination/PaginationContent.vue +22 -0
  159. package/template/src/components/ui/pagination/PaginationEllipsis.vue +25 -0
  160. package/template/src/components/ui/pagination/PaginationFirst.vue +33 -0
  161. package/template/src/components/ui/pagination/PaginationItem.vue +34 -0
  162. package/template/src/components/ui/pagination/PaginationLast.vue +33 -0
  163. package/template/src/components/ui/pagination/PaginationNext.vue +33 -0
  164. package/template/src/components/ui/pagination/PaginationPrevious.vue +33 -0
  165. package/template/src/components/ui/pagination/index.ts +8 -0
  166. package/template/src/components/ui/pin-input/PinInput.vue +28 -0
  167. package/template/src/components/ui/pin-input/PinInputGroup.vue +18 -0
  168. package/template/src/components/ui/pin-input/PinInputInput.vue +24 -0
  169. package/template/src/components/ui/pin-input/PinInputSeparator.vue +21 -0
  170. package/template/src/components/ui/pin-input/index.ts +4 -0
  171. package/template/src/components/ui/popover/Popover.vue +19 -0
  172. package/template/src/components/ui/popover/PopoverAnchor.vue +15 -0
  173. package/template/src/components/ui/popover/PopoverContent.vue +45 -0
  174. package/template/src/components/ui/popover/PopoverTrigger.vue +15 -0
  175. package/template/src/components/ui/popover/index.ts +4 -0
  176. package/template/src/components/ui/progress/Progress.vue +35 -0
  177. package/template/src/components/ui/progress/index.ts +1 -0
  178. package/template/src/components/ui/radio-group/RadioGroup.vue +23 -0
  179. package/template/src/components/ui/radio-group/RadioGroupItem.vue +30 -0
  180. package/template/src/components/ui/radio-group/index.ts +2 -0
  181. package/template/src/components/ui/scroll-area/ScrollArea.vue +40 -0
  182. package/template/src/components/ui/scroll-area/ScrollBar.vue +44 -0
  183. package/template/src/components/ui/scroll-area/index.ts +2 -0
  184. package/template/src/components/ui/select/Select.vue +19 -0
  185. package/template/src/components/ui/select/SelectContent.vue +51 -0
  186. package/template/src/components/ui/select/SelectGroup.vue +15 -0
  187. package/template/src/components/ui/select/SelectItem.vue +44 -0
  188. package/template/src/components/ui/select/SelectItemText.vue +15 -0
  189. package/template/src/components/ui/select/SelectLabel.vue +17 -0
  190. package/template/src/components/ui/select/SelectScrollDownButton.vue +26 -0
  191. package/template/src/components/ui/select/SelectScrollUpButton.vue +26 -0
  192. package/template/src/components/ui/select/SelectSeparator.vue +19 -0
  193. package/template/src/components/ui/select/SelectTrigger.vue +33 -0
  194. package/template/src/components/ui/select/SelectValue.vue +15 -0
  195. package/template/src/components/ui/select/index.ts +11 -0
  196. package/template/src/components/ui/separator/Separator.vue +29 -0
  197. package/template/src/components/ui/separator/index.ts +1 -0
  198. package/template/src/components/ui/sheet/Sheet.vue +19 -0
  199. package/template/src/components/ui/sheet/SheetClose.vue +15 -0
  200. package/template/src/components/ui/sheet/SheetContent.vue +62 -0
  201. package/template/src/components/ui/sheet/SheetDescription.vue +21 -0
  202. package/template/src/components/ui/sheet/SheetFooter.vue +16 -0
  203. package/template/src/components/ui/sheet/SheetHeader.vue +15 -0
  204. package/template/src/components/ui/sheet/SheetOverlay.vue +21 -0
  205. package/template/src/components/ui/sheet/SheetTitle.vue +21 -0
  206. package/template/src/components/ui/sheet/SheetTrigger.vue +15 -0
  207. package/template/src/components/ui/sheet/index.ts +8 -0
  208. package/template/src/components/ui/sidebar/Sidebar.vue +113 -0
  209. package/template/src/components/ui/sidebar/SidebarContent.vue +18 -0
  210. package/template/src/components/ui/sidebar/SidebarControl.vue +53 -0
  211. package/template/src/components/ui/sidebar/SidebarFooter.vue +18 -0
  212. package/template/src/components/ui/sidebar/SidebarGroup.vue +18 -0
  213. package/template/src/components/ui/sidebar/SidebarGroupAction.vue +27 -0
  214. package/template/src/components/ui/sidebar/SidebarGroupContent.vue +18 -0
  215. package/template/src/components/ui/sidebar/SidebarGroupLabel.vue +25 -0
  216. package/template/src/components/ui/sidebar/SidebarHeader.vue +18 -0
  217. package/template/src/components/ui/sidebar/SidebarInput.vue +22 -0
  218. package/template/src/components/ui/sidebar/SidebarInset.vue +21 -0
  219. package/template/src/components/ui/sidebar/SidebarMenu.vue +18 -0
  220. package/template/src/components/ui/sidebar/SidebarMenuAction.vue +35 -0
  221. package/template/src/components/ui/sidebar/SidebarMenuBadge.vue +26 -0
  222. package/template/src/components/ui/sidebar/SidebarMenuButton.vue +48 -0
  223. package/template/src/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
  224. package/template/src/components/ui/sidebar/SidebarMenuItem.vue +18 -0
  225. package/template/src/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
  226. package/template/src/components/ui/sidebar/SidebarMenuSub.vue +22 -0
  227. package/template/src/components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
  228. package/template/src/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
  229. package/template/src/components/ui/sidebar/SidebarProvider.vue +136 -0
  230. package/template/src/components/ui/sidebar/SidebarRail.vue +33 -0
  231. package/template/src/components/ui/sidebar/SidebarSeparator.vue +19 -0
  232. package/template/src/components/ui/sidebar/SidebarTrigger.vue +27 -0
  233. package/template/src/components/ui/sidebar/index.ts +61 -0
  234. package/template/src/components/ui/sidebar/utils.ts +27 -0
  235. package/template/src/components/ui/skeleton/Skeleton.vue +17 -0
  236. package/template/src/components/ui/skeleton/index.ts +1 -0
  237. package/template/src/components/ui/slider/Slider.vue +42 -0
  238. package/template/src/components/ui/slider/index.ts +1 -0
  239. package/template/src/components/ui/sonner/Sonner.vue +42 -0
  240. package/template/src/components/ui/sonner/index.ts +2 -0
  241. package/template/src/components/ui/stepper/Stepper.vue +102 -0
  242. package/template/src/components/ui/stepper/index.ts +1 -0
  243. package/template/src/components/ui/switch/Switch.vue +38 -0
  244. package/template/src/components/ui/switch/index.ts +1 -0
  245. package/template/src/components/ui/table/Table.vue +16 -0
  246. package/template/src/components/ui/table/TableBody.vue +17 -0
  247. package/template/src/components/ui/table/TableCaption.vue +17 -0
  248. package/template/src/components/ui/table/TableCell.vue +22 -0
  249. package/template/src/components/ui/table/TableEmpty.vue +34 -0
  250. package/template/src/components/ui/table/TableFooter.vue +17 -0
  251. package/template/src/components/ui/table/TableHead.vue +17 -0
  252. package/template/src/components/ui/table/TableHeader.vue +17 -0
  253. package/template/src/components/ui/table/TableRow.vue +17 -0
  254. package/template/src/components/ui/table/index.ts +9 -0
  255. package/template/src/components/ui/table/utils.ts +10 -0
  256. package/template/src/components/ui/tabs/Tabs.vue +24 -0
  257. package/template/src/components/ui/tabs/TabsContent.vue +21 -0
  258. package/template/src/components/ui/tabs/TabsList.vue +24 -0
  259. package/template/src/components/ui/tabs/TabsTrigger.vue +26 -0
  260. package/template/src/components/ui/tabs/index.ts +4 -0
  261. package/template/src/components/ui/textarea/Textarea.vue +28 -0
  262. package/template/src/components/ui/textarea/index.ts +1 -0
  263. package/template/src/components/ui/timeline/Timeline.vue +73 -0
  264. package/template/src/components/ui/timeline/index.ts +1 -0
  265. package/template/src/components/ui/toggle/Toggle.vue +35 -0
  266. package/template/src/components/ui/toggle/index.ts +28 -0
  267. package/template/src/components/ui/toggle-group/ToggleGroup.vue +41 -0
  268. package/template/src/components/ui/toggle-group/ToggleGroupItem.vue +43 -0
  269. package/template/src/components/ui/toggle-group/index.ts +12 -0
  270. package/template/src/components/ui/tooltip/Tooltip.vue +19 -0
  271. package/template/src/components/ui/tooltip/TooltipContent.vue +34 -0
  272. package/template/src/components/ui/tooltip/TooltipProvider.vue +14 -0
  273. package/template/src/components/ui/tooltip/TooltipTrigger.vue +15 -0
  274. package/template/src/components/ui/tooltip/index.ts +4 -0
  275. package/template/src/components.d.ts +228 -0
  276. package/template/src/composables/useFormatter.ts +105 -0
  277. package/template/src/composables/useThemeConfig.ts +252 -0
  278. package/template/src/layouts/AdminLayout.vue +55 -0
  279. package/template/src/layouts/AuthLayout.vue +42 -0
  280. package/template/src/lib/axios.spec.ts +46 -0
  281. package/template/src/lib/axios.ts +243 -0
  282. package/template/src/lib/formatters.spec.ts +137 -0
  283. package/template/src/lib/formatters.ts +332 -0
  284. package/template/src/lib/loading-bar.ts +103 -0
  285. package/template/src/lib/utils.spec.ts +26 -0
  286. package/template/src/lib/utils.ts +7 -0
  287. package/template/src/lib/validation.spec.ts +78 -0
  288. package/template/src/main.ts +13 -0
  289. package/template/src/router/index.ts +147 -0
  290. package/template/src/services/auth.service.spec.ts +93 -0
  291. package/template/src/services/auth.service.ts +243 -0
  292. package/template/src/services/index.ts +3 -0
  293. package/template/src/services/types.ts +28 -0
  294. package/template/src/services/user.service.spec.ts +91 -0
  295. package/template/src/services/user.service.ts +283 -0
  296. package/template/src/stores/auth.spec.ts +52 -0
  297. package/template/src/stores/auth.ts +130 -0
  298. package/template/src/style.css +587 -0
  299. package/template/src/views/_starter/BlankView.vue +47 -0
  300. package/template/src/views/auth/ConfirmEmailView.vue +209 -0
  301. package/template/src/views/auth/ForgotPasswordView.vue +151 -0
  302. package/template/src/views/auth/LockScreenView.vue +175 -0
  303. package/template/src/views/auth/LoginView.vue +54 -0
  304. package/template/src/views/auth/RegisterView.vue +265 -0
  305. package/template/src/views/auth/ResetPasswordView.vue +260 -0
  306. package/template/src/views/auth/VerifyOtpView.vue +261 -0
  307. package/template/src/views/dashboard/IndexView.vue +159 -0
  308. package/template/src/views/errors/ComingSoonView.vue +241 -0
  309. package/template/src/views/errors/MaintenanceView.vue +252 -0
  310. package/template/src/views/errors/NotFoundView.vue +81 -0
  311. package/template/src/views/errors/ServerErrorView.vue +101 -0
  312. package/template/src/views/errors/UnauthorizedView.vue +64 -0
  313. package/template/tsconfig.app.json +27 -0
  314. package/template/tsconfig.json +19 -0
  315. package/template/tsconfig.node.json +23 -0
  316. package/template/vite.config.ts +46 -0
@@ -0,0 +1,233 @@
1
+ <script setup lang="ts">
2
+ import { CalendarDays, ChevronLeft, ChevronRight } from '@lucide/vue'
3
+
4
+ // ─── Mini Calendar State ──────────────────────────────────────────────────────
5
+ const today = new Date()
6
+ const viewYear = ref(today.getFullYear())
7
+ const viewMonth = ref(today.getMonth()) // 0-based
8
+
9
+ const selectedDate = ref<Date | null>(null)
10
+ const selectedRange = ref<{ start: Date | null; end: Date | null }>({ start: null, end: null })
11
+
12
+ const isOpen = ref(false)
13
+ const isRangeOpen = ref(false)
14
+
15
+ const MONTH_NAMES = [
16
+ 'January', 'February', 'March', 'April', 'May', 'June',
17
+ 'July', 'August', 'September', 'October', 'November', 'December',
18
+ ]
19
+ const DAYS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']
20
+
21
+ interface CalDay { date: Date; inMonth: boolean }
22
+
23
+ function buildCalendar(year: number, month: number): CalDay[] {
24
+ const first = new Date(year, month, 1)
25
+ const last = new Date(year, month + 1, 0)
26
+ const days: CalDay[] = []
27
+
28
+ // Pad from Sunday
29
+ for (let i = 0; i < first.getDay(); i++) {
30
+ const d = new Date(year, month, -first.getDay() + i + 1)
31
+ days.push({ date: d, inMonth: false })
32
+ }
33
+ for (let d = 1; d <= last.getDate(); d++) {
34
+ days.push({ date: new Date(year, month, d), inMonth: true })
35
+ }
36
+ // Pad to complete row
37
+ const remaining = (7 - (days.length % 7)) % 7
38
+ for (let i = 1; i <= remaining; i++) {
39
+ days.push({ date: new Date(year, month + 1, i), inMonth: false })
40
+ }
41
+ return days
42
+ }
43
+
44
+ const calendarDays = computed(() => buildCalendar(viewYear.value, viewMonth.value))
45
+
46
+ function prevMonth() {
47
+ if (viewMonth.value === 0) { viewYear.value--; viewMonth.value = 11 }
48
+ else viewMonth.value--
49
+ }
50
+ function nextMonth() {
51
+ if (viewMonth.value === 11) { viewYear.value++; viewMonth.value = 0 }
52
+ else viewMonth.value++
53
+ }
54
+
55
+ function isSameDay(a: Date, b: Date | null) {
56
+ return b && a.toDateString() === b.toDateString()
57
+ }
58
+ function isToday(d: Date) {
59
+ return d.toDateString() === today.toDateString()
60
+ }
61
+
62
+ function selectDate(d: Date) {
63
+ selectedDate.value = d
64
+ isOpen.value = false
65
+ }
66
+
67
+ function formatDate(d: Date | null) {
68
+ if (!d) return ''
69
+ return d.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })
70
+ }
71
+
72
+ // ─── Range Picker Logic ──────────────────────────────────────────────────────
73
+ function selectRange(d: Date) {
74
+ const { start, end } = selectedRange.value
75
+ if (!start || (start && end)) {
76
+ selectedRange.value = { start: d, end: null }
77
+ } else {
78
+ const sorted = d < start ? { start: d, end: start } : { start, end: d }
79
+ selectedRange.value = sorted
80
+ isRangeOpen.value = false
81
+ }
82
+ }
83
+
84
+ function isInRange(d: Date) {
85
+ const { start, end } = selectedRange.value
86
+ return start && end && d > start && d < end
87
+ }
88
+ function isRangeStart(d: Date) { return isSameDay(d, selectedRange.value.start) }
89
+ function isRangeEnd(d: Date) { return isSameDay(d, selectedRange.value.end) }
90
+
91
+ function formatRange() {
92
+ const { start, end } = selectedRange.value
93
+ if (!start) return 'Pick a date range'
94
+ if (!end) return `${formatDate(start)} → ...`
95
+ return `${formatDate(start)} → ${formatDate(end)}`
96
+ }
97
+ </script>
98
+
99
+ <template>
100
+ <div class="space-y-6">
101
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
102
+
103
+ <!-- ── Single Date Picker ──────────────────────────────────── -->
104
+ <div class="space-y-1.5">
105
+ <Label>Deployment Date</Label>
106
+ <Popover v-model:open="isOpen">
107
+ <PopoverTrigger as-child>
108
+ <button
109
+ class="flex h-9 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm shadow-xs ring-offset-background placeholder:text-muted-foreground hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring transition-colors"
110
+ :class="{ 'text-muted-foreground': !selectedDate }"
111
+ >
112
+ <span>{{ selectedDate ? formatDate(selectedDate) : 'Pick a date' }}</span>
113
+ <CalendarDays class="h-4 w-4 text-muted-foreground shrink-0" />
114
+ </button>
115
+ </PopoverTrigger>
116
+ <PopoverContent class="w-auto p-0 shadow-xl" align="start">
117
+ <div class="p-3 w-72">
118
+ <!-- Month Nav -->
119
+ <div class="flex items-center justify-between mb-3">
120
+ <button @click="prevMonth" class="p-1.5 rounded-md hover:bg-accent transition-colors cursor-pointer">
121
+ <ChevronLeft class="h-4 w-4" />
122
+ </button>
123
+ <span class="text-sm font-semibold">{{ MONTH_NAMES[viewMonth] }} {{ viewYear }}</span>
124
+ <button @click="nextMonth" class="p-1.5 rounded-md hover:bg-accent transition-colors cursor-pointer">
125
+ <ChevronRight class="h-4 w-4" />
126
+ </button>
127
+ </div>
128
+
129
+ <!-- Day Headers -->
130
+ <div class="grid grid-cols-7 mb-1">
131
+ <span
132
+ v-for="d in DAYS" :key="d"
133
+ class="text-center text-[11px] font-medium text-muted-foreground py-1"
134
+ >{{ d }}</span>
135
+ </div>
136
+
137
+ <!-- Day Grid -->
138
+ <div class="grid grid-cols-7 gap-y-0.5">
139
+ <button
140
+ v-for="(day, i) in calendarDays" :key="i"
141
+ @click="selectDate(day.date)"
142
+ class="h-8 w-full flex items-center justify-center rounded-md text-sm transition-colors cursor-pointer"
143
+ :class="[
144
+ !day.inMonth && 'text-muted-foreground/35 hover:bg-accent/50',
145
+ day.inMonth && !isSameDay(day.date, selectedDate) && !isToday(day.date) && 'hover:bg-accent',
146
+ isToday(day.date) && !isSameDay(day.date, selectedDate) && 'text-primary font-semibold bg-primary/10',
147
+ isSameDay(day.date, selectedDate) && 'bg-primary text-primary-foreground font-semibold hover:bg-primary/90',
148
+ ]"
149
+ >
150
+ {{ day.date.getDate() }}
151
+ </button>
152
+ </div>
153
+
154
+ <!-- Footer -->
155
+ <div class="mt-2 pt-2 border-t border-border flex justify-between text-xs text-muted-foreground">
156
+ <span>{{ selectedDate ? formatDate(selectedDate) : 'No date selected' }}</span>
157
+ <button v-if="selectedDate" @click="selectedDate = null; isOpen = true" class="text-primary hover:underline cursor-pointer">
158
+ Clear
159
+ </button>
160
+ </div>
161
+ </div>
162
+ </PopoverContent>
163
+ </Popover>
164
+ <p class="text-xs text-muted-foreground">Select the scheduled deployment date.</p>
165
+ </div>
166
+
167
+ <!-- ── Date Range Picker ───────────────────────────────────── -->
168
+ <div class="space-y-1.5">
169
+ <Label>Maintenance Window</Label>
170
+ <Popover v-model:open="isRangeOpen">
171
+ <PopoverTrigger as-child>
172
+ <button
173
+ class="flex h-9 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm shadow-xs hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring transition-colors"
174
+ :class="{ 'text-muted-foreground': !selectedRange.start }"
175
+ >
176
+ <span class="truncate">{{ formatRange() }}</span>
177
+ <CalendarDays class="h-4 w-4 text-muted-foreground shrink-0 ml-2" />
178
+ </button>
179
+ </PopoverTrigger>
180
+ <PopoverContent class="w-auto p-0 shadow-xl" align="start">
181
+ <div class="p-3 w-72">
182
+ <!-- Month Nav -->
183
+ <div class="flex items-center justify-between mb-3">
184
+ <button @click="prevMonth" class="p-1.5 rounded-md hover:bg-accent transition-colors cursor-pointer">
185
+ <ChevronLeft class="h-4 w-4" />
186
+ </button>
187
+ <span class="text-sm font-semibold">{{ MONTH_NAMES[viewMonth] }} {{ viewYear }}</span>
188
+ <button @click="nextMonth" class="p-1.5 rounded-md hover:bg-accent transition-colors cursor-pointer">
189
+ <ChevronRight class="h-4 w-4" />
190
+ </button>
191
+ </div>
192
+
193
+ <!-- Day Headers -->
194
+ <div class="grid grid-cols-7 mb-1">
195
+ <span v-for="d in DAYS" :key="d" class="text-center text-[11px] font-medium text-muted-foreground py-1">{{ d }}</span>
196
+ </div>
197
+
198
+ <!-- Day Grid with range highlight -->
199
+ <div class="grid grid-cols-7 gap-y-0.5">
200
+ <button
201
+ v-for="(day, i) in calendarDays" :key="i"
202
+ @click="selectRange(day.date)"
203
+ class="h-8 w-full flex items-center justify-center text-sm transition-colors cursor-pointer relative"
204
+ :class="[
205
+ !day.inMonth && 'text-muted-foreground/35',
206
+ isInRange(day.date) && 'bg-primary/15 rounded-none',
207
+ isRangeStart(day.date) && 'bg-primary text-primary-foreground rounded-l-md font-semibold',
208
+ isRangeEnd(day.date) && 'bg-primary text-primary-foreground rounded-r-md font-semibold',
209
+ !isRangeStart(day.date) && !isRangeEnd(day.date) && !isInRange(day.date) && day.inMonth && 'rounded-md hover:bg-accent',
210
+ isToday(day.date) && !isRangeStart(day.date) && !isRangeEnd(day.date) && 'text-primary font-semibold',
211
+ ]"
212
+ >
213
+ {{ day.date.getDate() }}
214
+ </button>
215
+ </div>
216
+
217
+ <!-- Range Footer -->
218
+ <div class="mt-2 pt-2 border-t border-border text-xs text-muted-foreground">
219
+ <template v-if="!selectedRange.start">Click a start date</template>
220
+ <template v-else-if="!selectedRange.end">Now click an end date</template>
221
+ <div v-else class="flex justify-between">
222
+ <span>{{ formatDate(selectedRange.start) }} → {{ formatDate(selectedRange.end) }}</span>
223
+ <button @click="selectedRange = { start: null, end: null }" class="text-primary hover:underline cursor-pointer">Clear</button>
224
+ </div>
225
+ </div>
226
+ </div>
227
+ </PopoverContent>
228
+ </Popover>
229
+ <p class="text-xs text-muted-foreground">Select a start and end date for the maintenance window.</p>
230
+ </div>
231
+ </div>
232
+ </div>
233
+ </template>
@@ -0,0 +1 @@
1
+ export { default as DatePicker } from './DatePicker.vue'
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ import type { DialogRootEmits, DialogRootProps } from "reka-ui"
3
+ import { DialogRoot, useForwardPropsEmits } from "reka-ui"
4
+
5
+ const props = defineProps<DialogRootProps>()
6
+ const emits = defineEmits<DialogRootEmits>()
7
+
8
+ const forwarded = useForwardPropsEmits(props, emits)
9
+ </script>
10
+
11
+ <template>
12
+ <DialogRoot
13
+ v-slot="slotProps"
14
+ data-slot="dialog"
15
+ v-bind="forwarded"
16
+ >
17
+ <slot v-bind="slotProps" />
18
+ </DialogRoot>
19
+ </template>
@@ -0,0 +1,15 @@
1
+ <script setup lang="ts">
2
+ import type { DialogCloseProps } from "reka-ui"
3
+ import { DialogClose } from "reka-ui"
4
+
5
+ const props = defineProps<DialogCloseProps>()
6
+ </script>
7
+
8
+ <template>
9
+ <DialogClose
10
+ data-slot="dialog-close"
11
+ v-bind="props"
12
+ >
13
+ <slot />
14
+ </DialogClose>
15
+ </template>
@@ -0,0 +1,53 @@
1
+ <script setup lang="ts">
2
+ import type { DialogContentEmits, DialogContentProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { X } from "@lucide/vue"
5
+ import { reactiveOmit } from "@vueuse/core"
6
+ import {
7
+ DialogClose,
8
+ DialogContent,
9
+ DialogPortal,
10
+ useForwardPropsEmits,
11
+ } from "reka-ui"
12
+ import { cn } from "@/lib/utils"
13
+ import DialogOverlay from "./DialogOverlay.vue"
14
+
15
+ defineOptions({
16
+ inheritAttrs: false,
17
+ })
18
+
19
+ const props = withDefaults(defineProps<DialogContentProps & { class?: HTMLAttributes["class"], showCloseButton?: boolean }>(), {
20
+ showCloseButton: true,
21
+ })
22
+ const emits = defineEmits<DialogContentEmits>()
23
+
24
+ const delegatedProps = reactiveOmit(props, "class")
25
+
26
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
27
+ </script>
28
+
29
+ <template>
30
+ <DialogPortal>
31
+ <DialogOverlay />
32
+ <DialogContent
33
+ data-slot="dialog-content"
34
+ v-bind="{ ...$attrs, ...forwarded }"
35
+ :class="
36
+ cn(
37
+ 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
38
+ props.class,
39
+ )"
40
+ >
41
+ <slot />
42
+
43
+ <DialogClose
44
+ v-if="showCloseButton"
45
+ data-slot="dialog-close"
46
+ class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
47
+ >
48
+ <X />
49
+ <span class="sr-only">Close</span>
50
+ </DialogClose>
51
+ </DialogContent>
52
+ </DialogPortal>
53
+ </template>
@@ -0,0 +1,23 @@
1
+ <script setup lang="ts">
2
+ import type { DialogDescriptionProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { DialogDescription, useForwardProps } from "reka-ui"
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes["class"] }>()
9
+
10
+ const delegatedProps = reactiveOmit(props, "class")
11
+
12
+ const forwardedProps = useForwardProps(delegatedProps)
13
+ </script>
14
+
15
+ <template>
16
+ <DialogDescription
17
+ data-slot="dialog-description"
18
+ v-bind="forwardedProps"
19
+ :class="cn('text-muted-foreground text-sm', props.class)"
20
+ >
21
+ <slot />
22
+ </DialogDescription>
23
+ </template>
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from "vue"
3
+ import { DialogClose } from "reka-ui"
4
+ import { cn } from "@/lib/utils"
5
+ import { Button } from '@/components/ui/button'
6
+
7
+ const props = withDefaults(defineProps<{
8
+ class?: HTMLAttributes["class"]
9
+ showCloseButton?: boolean
10
+ }>(), {
11
+ showCloseButton: false,
12
+ })
13
+ </script>
14
+
15
+ <template>
16
+ <div
17
+ data-slot="dialog-footer"
18
+ :class="cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', props.class)"
19
+ >
20
+ <slot />
21
+ <DialogClose v-if="showCloseButton" as-child>
22
+ <Button variant="outline">
23
+ Close
24
+ </Button>
25
+ </DialogClose>
26
+ </div>
27
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from "vue"
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes["class"]
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ data-slot="dialog-header"
13
+ :class="cn('flex flex-col gap-2 text-center sm:text-left', props.class)"
14
+ >
15
+ <slot />
16
+ </div>
17
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import type { DialogOverlayProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { DialogOverlay } from "reka-ui"
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const props = defineProps<DialogOverlayProps & { class?: HTMLAttributes["class"] }>()
9
+
10
+ const delegatedProps = reactiveOmit(props, "class")
11
+ </script>
12
+
13
+ <template>
14
+ <DialogOverlay
15
+ data-slot="dialog-overlay"
16
+ v-bind="delegatedProps"
17
+ :class="cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', props.class)"
18
+ >
19
+ <slot />
20
+ </DialogOverlay>
21
+ </template>
@@ -0,0 +1,59 @@
1
+ <script setup lang="ts">
2
+ import type { DialogContentEmits, DialogContentProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { X } from "@lucide/vue"
5
+ import { reactiveOmit } from "@vueuse/core"
6
+ import {
7
+ DialogClose,
8
+ DialogContent,
9
+ DialogOverlay,
10
+ DialogPortal,
11
+ useForwardPropsEmits,
12
+ } from "reka-ui"
13
+ import { cn } from "@/lib/utils"
14
+
15
+ defineOptions({
16
+ inheritAttrs: false,
17
+ })
18
+
19
+ const props = defineProps<DialogContentProps & { class?: HTMLAttributes["class"] }>()
20
+ const emits = defineEmits<DialogContentEmits>()
21
+
22
+ const delegatedProps = reactiveOmit(props, "class")
23
+
24
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
25
+ </script>
26
+
27
+ <template>
28
+ <DialogPortal>
29
+ <DialogOverlay
30
+ class="fixed inset-0 z-50 grid place-items-center overflow-y-auto bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
31
+ >
32
+ <DialogContent
33
+ :class="
34
+ cn(
35
+ 'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-border bg-background p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',
36
+ props.class,
37
+ )
38
+ "
39
+ v-bind="{ ...$attrs, ...forwarded }"
40
+ @pointer-down-outside="(event) => {
41
+ const originalEvent = event.detail.originalEvent;
42
+ const target = originalEvent.target as HTMLElement;
43
+ if (originalEvent.offsetX > target.clientWidth || originalEvent.offsetY > target.clientHeight) {
44
+ event.preventDefault();
45
+ }
46
+ }"
47
+ >
48
+ <slot />
49
+
50
+ <DialogClose
51
+ class="absolute top-4 right-4 p-0.5 transition-colors rounded-md hover:bg-secondary"
52
+ >
53
+ <X class="w-4 h-4" />
54
+ <span class="sr-only">Close</span>
55
+ </DialogClose>
56
+ </DialogContent>
57
+ </DialogOverlay>
58
+ </DialogPortal>
59
+ </template>
@@ -0,0 +1,23 @@
1
+ <script setup lang="ts">
2
+ import type { DialogTitleProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import { DialogTitle, useForwardProps } from "reka-ui"
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const props = defineProps<DialogTitleProps & { class?: HTMLAttributes["class"] }>()
9
+
10
+ const delegatedProps = reactiveOmit(props, "class")
11
+
12
+ const forwardedProps = useForwardProps(delegatedProps)
13
+ </script>
14
+
15
+ <template>
16
+ <DialogTitle
17
+ data-slot="dialog-title"
18
+ v-bind="forwardedProps"
19
+ :class="cn('text-lg leading-none font-semibold', props.class)"
20
+ >
21
+ <slot />
22
+ </DialogTitle>
23
+ </template>
@@ -0,0 +1,15 @@
1
+ <script setup lang="ts">
2
+ import type { DialogTriggerProps } from "reka-ui"
3
+ import { DialogTrigger } from "reka-ui"
4
+
5
+ const props = defineProps<DialogTriggerProps>()
6
+ </script>
7
+
8
+ <template>
9
+ <DialogTrigger
10
+ data-slot="dialog-trigger"
11
+ v-bind="props"
12
+ >
13
+ <slot />
14
+ </DialogTrigger>
15
+ </template>
@@ -0,0 +1,10 @@
1
+ export { default as Dialog } from "./Dialog.vue"
2
+ export { default as DialogClose } from "./DialogClose.vue"
3
+ export { default as DialogContent } from "./DialogContent.vue"
4
+ export { default as DialogDescription } from "./DialogDescription.vue"
5
+ export { default as DialogFooter } from "./DialogFooter.vue"
6
+ export { default as DialogHeader } from "./DialogHeader.vue"
7
+ export { default as DialogOverlay } from "./DialogOverlay.vue"
8
+ export { default as DialogScrollContent } from "./DialogScrollContent.vue"
9
+ export { default as DialogTitle } from "./DialogTitle.vue"
10
+ export { default as DialogTrigger } from "./DialogTrigger.vue"
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ import type { DropdownMenuRootEmits, DropdownMenuRootProps } from "reka-ui"
3
+ import { DropdownMenuRoot, useForwardPropsEmits } from "reka-ui"
4
+
5
+ const props = defineProps<DropdownMenuRootProps>()
6
+ const emits = defineEmits<DropdownMenuRootEmits>()
7
+
8
+ const forwarded = useForwardPropsEmits(props, emits)
9
+ </script>
10
+
11
+ <template>
12
+ <DropdownMenuRoot
13
+ v-slot="slotProps"
14
+ data-slot="dropdown-menu"
15
+ v-bind="forwarded"
16
+ >
17
+ <slot v-bind="slotProps" />
18
+ </DropdownMenuRoot>
19
+ </template>
@@ -0,0 +1,39 @@
1
+ <script setup lang="ts">
2
+ import type { DropdownMenuCheckboxItemEmits, DropdownMenuCheckboxItemProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { Check } from "@lucide/vue"
5
+ import { reactiveOmit } from "@vueuse/core"
6
+ import {
7
+ DropdownMenuCheckboxItem,
8
+ DropdownMenuItemIndicator,
9
+ useForwardPropsEmits,
10
+ } from "reka-ui"
11
+ import { cn } from "@/lib/utils"
12
+
13
+ const props = defineProps<DropdownMenuCheckboxItemProps & { class?: HTMLAttributes["class"] }>()
14
+ const emits = defineEmits<DropdownMenuCheckboxItemEmits>()
15
+
16
+ const delegatedProps = reactiveOmit(props, "class")
17
+
18
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
19
+ </script>
20
+
21
+ <template>
22
+ <DropdownMenuCheckboxItem
23
+ data-slot="dropdown-menu-checkbox-item"
24
+ v-bind="forwarded"
25
+ :class=" cn(
26
+ `focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,
27
+ props.class,
28
+ )"
29
+ >
30
+ <span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
31
+ <DropdownMenuItemIndicator>
32
+ <slot name="indicator-icon">
33
+ <Check class="size-4" />
34
+ </slot>
35
+ </DropdownMenuItemIndicator>
36
+ </span>
37
+ <slot />
38
+ </DropdownMenuCheckboxItem>
39
+ </template>
@@ -0,0 +1,41 @@
1
+ <script setup lang="ts">
2
+ import type { DropdownMenuContentEmits, DropdownMenuContentProps } from "reka-ui"
3
+ import type { HTMLAttributes } from "vue"
4
+ import { reactiveOmit } from "@vueuse/core"
5
+ import {
6
+ DropdownMenuContent,
7
+ DropdownMenuPortal,
8
+ useForwardPropsEmits,
9
+ } from "reka-ui"
10
+ import { cn } from "@/lib/utils"
11
+
12
+ defineOptions({
13
+ inheritAttrs: false,
14
+ })
15
+
16
+ const props = withDefaults(
17
+ defineProps<DropdownMenuContentProps & { class?: HTMLAttributes["class"] }>(),
18
+ {
19
+ sideOffset: 4,
20
+ disableOutsidePointerEvents: false,
21
+ },
22
+ )
23
+ const emits = defineEmits<DropdownMenuContentEmits>()
24
+
25
+ const delegatedProps = reactiveOmit(props, "class")
26
+
27
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
28
+ </script>
29
+
30
+ <template>
31
+ <DropdownMenuPortal>
32
+ <DropdownMenuContent
33
+ data-slot="dropdown-menu-content"
34
+ :disable-outside-pointer-events="false"
35
+ v-bind="{ ...$attrs, ...forwarded }"
36
+ :class="cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--reka-dropdown-menu-content-available-height) min-w-32 origin-(--reka-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md', props.class)"
37
+ >
38
+ <slot />
39
+ </DropdownMenuContent>
40
+ </DropdownMenuPortal>
41
+ </template>
@@ -0,0 +1,15 @@
1
+ <script setup lang="ts">
2
+ import type { DropdownMenuGroupProps } from "reka-ui"
3
+ import { DropdownMenuGroup } from "reka-ui"
4
+
5
+ const props = defineProps<DropdownMenuGroupProps>()
6
+ </script>
7
+
8
+ <template>
9
+ <DropdownMenuGroup
10
+ data-slot="dropdown-menu-group"
11
+ v-bind="props"
12
+ >
13
+ <slot />
14
+ </DropdownMenuGroup>
15
+ </template>