@mkbabb/glass-ui 0.2.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 (335) hide show
  1. package/README.md +172 -0
  2. package/dist/glass-ui.css +1 -0
  3. package/dist/glass-ui.js +10019 -0
  4. package/dist/index.d.ts +6619 -0
  5. package/package.json +65 -0
  6. package/src/components/custom/aurora/Aurora.vue +34 -0
  7. package/src/components/custom/aurora/composables/color.ts +122 -0
  8. package/src/components/custom/aurora/composables/useAurora.ts +355 -0
  9. package/src/components/custom/aurora/index.ts +8 -0
  10. package/src/components/custom/confirm-dialog/ConfirmDialog.vue +88 -0
  11. package/src/components/custom/confirm-dialog/index.ts +1 -0
  12. package/src/components/custom/controls/DarkModeToggle.vue +96 -0
  13. package/src/components/custom/controls/index.ts +1 -0
  14. package/src/components/custom/dock/DockLayerGroup.vue +21 -0
  15. package/src/components/custom/dock/DockPopover.vue +263 -0
  16. package/src/components/custom/dock/GlassDock.vue +276 -0
  17. package/src/components/custom/dock/composables/index.ts +16 -0
  18. package/src/components/custom/dock/composables/isTeleportedTarget.ts +19 -0
  19. package/src/components/custom/dock/composables/useDockActionBar.ts +33 -0
  20. package/src/components/custom/dock/composables/useDockState.ts +301 -0
  21. package/src/components/custom/dock/composables/useDockTransition.ts +146 -0
  22. package/src/components/custom/dock/composables/useLayerTransition.ts +135 -0
  23. package/src/components/custom/dock/composables/usePopupMutex.ts +83 -0
  24. package/src/components/custom/dock/index.ts +9 -0
  25. package/src/components/custom/expandable-container/ExpandableContainer.vue +64 -0
  26. package/src/components/custom/expandable-container/index.ts +1 -0
  27. package/src/components/custom/glass-panel/GlassPanel.vue +98 -0
  28. package/src/components/custom/glass-panel/index.ts +2 -0
  29. package/src/components/custom/icon-tooltip/IconTooltip.vue +20 -0
  30. package/src/components/custom/icon-tooltip/index.ts +1 -0
  31. package/src/components/custom/index.ts +15 -0
  32. package/src/components/custom/infinite-scroll/InfiniteScroll.vue +55 -0
  33. package/src/components/custom/infinite-scroll/composables/index.ts +2 -0
  34. package/src/components/custom/infinite-scroll/composables/types.ts +23 -0
  35. package/src/components/custom/infinite-scroll/composables/useInfiniteScroll.ts +73 -0
  36. package/src/components/custom/infinite-scroll/index.ts +1 -0
  37. package/src/components/custom/labeled-field/LabeledInput.vue +29 -0
  38. package/src/components/custom/labeled-field/LabeledSelect.vue +59 -0
  39. package/src/components/custom/labeled-field/LabeledSlider.vue +32 -0
  40. package/src/components/custom/labeled-field/LabeledSwitch.vue +27 -0
  41. package/src/components/custom/labeled-field/index.ts +4 -0
  42. package/src/components/custom/metaballs/MetaballCanvas.vue +23 -0
  43. package/src/components/custom/metaballs/index.ts +4 -0
  44. package/src/components/custom/metaballs/shaders.ts +63 -0
  45. package/src/components/custom/metaballs/types.ts +29 -0
  46. package/src/components/custom/metaballs/useMetaballs.ts +252 -0
  47. package/src/components/custom/search/FuzzySearch.vue +589 -0
  48. package/src/components/custom/search/SearchBar.vue +44 -0
  49. package/src/components/custom/search/composables/fuzzySearchIndex.ts +224 -0
  50. package/src/components/custom/search/composables/index.ts +5 -0
  51. package/src/components/custom/search/composables/types.ts +34 -0
  52. package/src/components/custom/search/composables/useFuzzySearch.ts +115 -0
  53. package/src/components/custom/search/index.ts +7 -0
  54. package/src/components/custom/sidebar/ProgressiveSidebar.vue +256 -0
  55. package/src/components/custom/sidebar/composables/index.ts +6 -0
  56. package/src/components/custom/sidebar/composables/useScrollTracker.ts +242 -0
  57. package/src/components/custom/sidebar/composables/useSidebarFollow.ts +247 -0
  58. package/src/components/custom/sidebar/composables/useSidebarState.ts +72 -0
  59. package/src/components/custom/sidebar/composables/useTreeIndex.ts +152 -0
  60. package/src/components/custom/sidebar/index.ts +15 -0
  61. package/src/components/custom/sidebar/types.ts +50 -0
  62. package/src/components/custom/tabs/BouncyTabs.vue +39 -0
  63. package/src/components/custom/tabs/BouncyToggle.vue +352 -0
  64. package/src/components/custom/tabs/UnderlineTabs.vue +115 -0
  65. package/src/components/custom/tabs/index.ts +5 -0
  66. package/src/components/custom/timeline/GlassTimeline.vue +174 -0
  67. package/src/components/custom/timeline/index.ts +1 -0
  68. package/src/components/custom/typewriter/TypewriterText.vue +239 -0
  69. package/src/components/custom/typewriter/composables/index.ts +1 -0
  70. package/src/components/custom/typewriter/composables/useTypewriter.ts +413 -0
  71. package/src/components/custom/typewriter/index.ts +7 -0
  72. package/src/components/custom/typewriter/types.ts +159 -0
  73. package/src/components/custom/typewriter/utils/keyboard.ts +213 -0
  74. package/src/components/custom/typewriter/utils/pausePatterns.ts +55 -0
  75. package/src/components/custom/typewriter/utils/timing.ts +104 -0
  76. package/src/components/custom/typewriter/utils/typoStateMachine.ts +197 -0
  77. package/src/components/index.ts +2 -0
  78. package/src/components/ui/accordion/Accordion.vue +19 -0
  79. package/src/components/ui/accordion/AccordionContent.vue +24 -0
  80. package/src/components/ui/accordion/AccordionItem.vue +24 -0
  81. package/src/components/ui/accordion/AccordionTrigger.vue +39 -0
  82. package/src/components/ui/accordion/index.ts +4 -0
  83. package/src/components/ui/alert/Alert.vue +20 -0
  84. package/src/components/ui/alert/AlertDescription.vue +17 -0
  85. package/src/components/ui/alert/AlertTitle.vue +17 -0
  86. package/src/components/ui/alert/index.ts +23 -0
  87. package/src/components/ui/avatar/Avatar.vue +21 -0
  88. package/src/components/ui/avatar/AvatarFallback.vue +11 -0
  89. package/src/components/ui/avatar/AvatarImage.vue +9 -0
  90. package/src/components/ui/avatar/index.ts +24 -0
  91. package/src/components/ui/badge/Badge.vue +16 -0
  92. package/src/components/ui/badge/index.ts +25 -0
  93. package/src/components/ui/button/Button.vue +26 -0
  94. package/src/components/ui/button/index.ts +43 -0
  95. package/src/components/ui/card/Card.vue +28 -0
  96. package/src/components/ui/card/CardContent.vue +14 -0
  97. package/src/components/ui/card/CardDescription.vue +14 -0
  98. package/src/components/ui/card/CardFooter.vue +14 -0
  99. package/src/components/ui/card/CardHeader.vue +14 -0
  100. package/src/components/ui/card/CardTitle.vue +21 -0
  101. package/src/components/ui/card/index.ts +6 -0
  102. package/src/components/ui/carousel/Carousel.vue +53 -0
  103. package/src/components/ui/carousel/CarouselContent.vue +35 -0
  104. package/src/components/ui/carousel/CarouselItem.vue +24 -0
  105. package/src/components/ui/carousel/CarouselNext.vue +40 -0
  106. package/src/components/ui/carousel/CarouselPrevious.vue +40 -0
  107. package/src/components/ui/carousel/index.ts +10 -0
  108. package/src/components/ui/carousel/interface.ts +26 -0
  109. package/src/components/ui/carousel/useCarousel.ts +56 -0
  110. package/src/components/ui/checkbox/Checkbox.vue +33 -0
  111. package/src/components/ui/checkbox/index.ts +1 -0
  112. package/src/components/ui/collapsible/Collapsible.vue +15 -0
  113. package/src/components/ui/collapsible/CollapsibleContent.vue +11 -0
  114. package/src/components/ui/collapsible/CollapsibleTrigger.vue +11 -0
  115. package/src/components/ui/collapsible/index.ts +3 -0
  116. package/src/components/ui/combobox/Combobox.vue +17 -0
  117. package/src/components/ui/combobox/ComboboxAnchor.vue +23 -0
  118. package/src/components/ui/combobox/ComboboxEmpty.vue +21 -0
  119. package/src/components/ui/combobox/ComboboxGroup.vue +27 -0
  120. package/src/components/ui/combobox/ComboboxInput.vue +41 -0
  121. package/src/components/ui/combobox/ComboboxItem.vue +24 -0
  122. package/src/components/ui/combobox/ComboboxItemIndicator.vue +23 -0
  123. package/src/components/ui/combobox/ComboboxList.vue +29 -0
  124. package/src/components/ui/combobox/ComboboxSeparator.vue +21 -0
  125. package/src/components/ui/combobox/ComboboxViewport.vue +23 -0
  126. package/src/components/ui/combobox/index.ts +12 -0
  127. package/src/components/ui/command/Command.vue +30 -0
  128. package/src/components/ui/command/CommandDialog.vue +21 -0
  129. package/src/components/ui/command/CommandEmpty.vue +20 -0
  130. package/src/components/ui/command/CommandGroup.vue +29 -0
  131. package/src/components/ui/command/CommandInput.vue +33 -0
  132. package/src/components/ui/command/CommandItem.vue +26 -0
  133. package/src/components/ui/command/CommandList.vue +27 -0
  134. package/src/components/ui/command/CommandSeparator.vue +23 -0
  135. package/src/components/ui/command/CommandShortcut.vue +14 -0
  136. package/src/components/ui/command/index.ts +9 -0
  137. package/src/components/ui/context-menu/ContextMenu.vue +15 -0
  138. package/src/components/ui/context-menu/ContextMenuCheckboxItem.vue +40 -0
  139. package/src/components/ui/context-menu/ContextMenuContent.vue +36 -0
  140. package/src/components/ui/context-menu/ContextMenuGroup.vue +11 -0
  141. package/src/components/ui/context-menu/ContextMenuItem.vue +34 -0
  142. package/src/components/ui/context-menu/ContextMenuLabel.vue +25 -0
  143. package/src/components/ui/context-menu/ContextMenuPortal.vue +11 -0
  144. package/src/components/ui/context-menu/ContextMenuRadioGroup.vue +19 -0
  145. package/src/components/ui/context-menu/ContextMenuRadioItem.vue +40 -0
  146. package/src/components/ui/context-menu/ContextMenuSeparator.vue +20 -0
  147. package/src/components/ui/context-menu/ContextMenuShortcut.vue +14 -0
  148. package/src/components/ui/context-menu/ContextMenuSub.vue +19 -0
  149. package/src/components/ui/context-menu/ContextMenuSubContent.vue +35 -0
  150. package/src/components/ui/context-menu/ContextMenuSubTrigger.vue +34 -0
  151. package/src/components/ui/context-menu/ContextMenuTrigger.vue +13 -0
  152. package/src/components/ui/context-menu/index.ts +14 -0
  153. package/src/components/ui/data-table/DataTable.vue +167 -0
  154. package/src/components/ui/data-table/DataTablePagination.vue +112 -0
  155. package/src/components/ui/data-table/index.ts +3 -0
  156. package/src/components/ui/data-table/types.ts +48 -0
  157. package/src/components/ui/dialog/Dialog.vue +14 -0
  158. package/src/components/ui/dialog/DialogClose.vue +11 -0
  159. package/src/components/ui/dialog/DialogContent.vue +61 -0
  160. package/src/components/ui/dialog/DialogDescription.vue +24 -0
  161. package/src/components/ui/dialog/DialogFooter.vue +19 -0
  162. package/src/components/ui/dialog/DialogHeader.vue +16 -0
  163. package/src/components/ui/dialog/DialogScrollContent.vue +65 -0
  164. package/src/components/ui/dialog/DialogTitle.vue +29 -0
  165. package/src/components/ui/dialog/DialogTrigger.vue +11 -0
  166. package/src/components/ui/dialog/index.ts +9 -0
  167. package/src/components/ui/drawer/Drawer.vue +19 -0
  168. package/src/components/ui/drawer/DrawerContent.vue +28 -0
  169. package/src/components/ui/drawer/DrawerDescription.vue +20 -0
  170. package/src/components/ui/drawer/DrawerFooter.vue +14 -0
  171. package/src/components/ui/drawer/DrawerHeader.vue +14 -0
  172. package/src/components/ui/drawer/DrawerOverlay.vue +18 -0
  173. package/src/components/ui/drawer/DrawerTitle.vue +20 -0
  174. package/src/components/ui/drawer/index.ts +8 -0
  175. package/src/components/ui/dropdown-menu/DropdownMenu.vue +14 -0
  176. package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +40 -0
  177. package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +44 -0
  178. package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +11 -0
  179. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +28 -0
  180. package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +24 -0
  181. package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +19 -0
  182. package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
  183. package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +22 -0
  184. package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +14 -0
  185. package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
  186. package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +36 -0
  187. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +33 -0
  188. package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +13 -0
  189. package/src/components/ui/dropdown-menu/index.ts +16 -0
  190. package/src/components/ui/hover-card/HoverCard.vue +14 -0
  191. package/src/components/ui/hover-card/HoverCardContent.vue +41 -0
  192. package/src/components/ui/hover-card/HoverCardTrigger.vue +11 -0
  193. package/src/components/ui/hover-card/index.ts +3 -0
  194. package/src/components/ui/index.ts +41 -0
  195. package/src/components/ui/input/Input.vue +24 -0
  196. package/src/components/ui/input/index.ts +1 -0
  197. package/src/components/ui/label/Label.vue +27 -0
  198. package/src/components/ui/label/index.ts +1 -0
  199. package/src/components/ui/multi-select/MultiSelect.vue +141 -0
  200. package/src/components/ui/multi-select/index.ts +7 -0
  201. package/src/components/ui/notification/Notification.vue +85 -0
  202. package/src/components/ui/notification/index.ts +1 -0
  203. package/src/components/ui/number-field/NumberField.vue +23 -0
  204. package/src/components/ui/number-field/NumberFieldContent.vue +14 -0
  205. package/src/components/ui/number-field/NumberFieldDecrement.vue +25 -0
  206. package/src/components/ui/number-field/NumberFieldIncrement.vue +25 -0
  207. package/src/components/ui/number-field/NumberFieldInput.vue +8 -0
  208. package/src/components/ui/number-field/index.ts +5 -0
  209. package/src/components/ui/popover/Popover.vue +15 -0
  210. package/src/components/ui/popover/PopoverContent.vue +61 -0
  211. package/src/components/ui/popover/PopoverTrigger.vue +11 -0
  212. package/src/components/ui/popover/index.ts +3 -0
  213. package/src/components/ui/progress/Progress.vue +39 -0
  214. package/src/components/ui/progress/index.ts +1 -0
  215. package/src/components/ui/radio-group/RadioGroup.vue +25 -0
  216. package/src/components/ui/radio-group/RadioGroupItem.vue +39 -0
  217. package/src/components/ui/radio-group/index.ts +2 -0
  218. package/src/components/ui/scroll-area/ScrollArea.vue +29 -0
  219. package/src/components/ui/scroll-area/ScrollBar.vue +30 -0
  220. package/src/components/ui/scroll-area/index.ts +2 -0
  221. package/src/components/ui/scroll-pane/ScrollPane.vue +25 -0
  222. package/src/components/ui/scroll-pane/ScrollPaneHeader.vue +75 -0
  223. package/src/components/ui/scroll-pane/index.ts +2 -0
  224. package/src/components/ui/select/Select.vue +15 -0
  225. package/src/components/ui/select/SelectContent.vue +57 -0
  226. package/src/components/ui/select/SelectGroup.vue +19 -0
  227. package/src/components/ui/select/SelectItem.vue +47 -0
  228. package/src/components/ui/select/SelectItemText.vue +11 -0
  229. package/src/components/ui/select/SelectLabel.vue +13 -0
  230. package/src/components/ui/select/SelectScrollDownButton.vue +24 -0
  231. package/src/components/ui/select/SelectScrollUpButton.vue +24 -0
  232. package/src/components/ui/select/SelectSeparator.vue +17 -0
  233. package/src/components/ui/select/SelectTrigger.vue +45 -0
  234. package/src/components/ui/select/SelectValue.vue +11 -0
  235. package/src/components/ui/select/index.ts +11 -0
  236. package/src/components/ui/separator/Separator.vue +35 -0
  237. package/src/components/ui/separator/index.ts +1 -0
  238. package/src/components/ui/sheet/Sheet.vue +14 -0
  239. package/src/components/ui/sheet/SheetClose.vue +11 -0
  240. package/src/components/ui/sheet/SheetContent.vue +56 -0
  241. package/src/components/ui/sheet/SheetDescription.vue +22 -0
  242. package/src/components/ui/sheet/SheetFooter.vue +19 -0
  243. package/src/components/ui/sheet/SheetHeader.vue +16 -0
  244. package/src/components/ui/sheet/SheetTitle.vue +22 -0
  245. package/src/components/ui/sheet/SheetTrigger.vue +11 -0
  246. package/src/components/ui/sheet/index.ts +31 -0
  247. package/src/components/ui/skeleton/Skeleton.vue +14 -0
  248. package/src/components/ui/skeleton/index.ts +1 -0
  249. package/src/components/ui/slider/Slider.vue +66 -0
  250. package/src/components/ui/slider/index.ts +1 -0
  251. package/src/components/ui/switch/Switch.vue +37 -0
  252. package/src/components/ui/switch/index.ts +1 -0
  253. package/src/components/ui/table/Table.vue +16 -0
  254. package/src/components/ui/table/TableBody.vue +14 -0
  255. package/src/components/ui/table/TableCaption.vue +14 -0
  256. package/src/components/ui/table/TableCell.vue +14 -0
  257. package/src/components/ui/table/TableEmpty.vue +39 -0
  258. package/src/components/ui/table/TableFooter.vue +16 -0
  259. package/src/components/ui/table/TableHead.vue +21 -0
  260. package/src/components/ui/table/TableHeader.vue +14 -0
  261. package/src/components/ui/table/TableRow.vue +21 -0
  262. package/src/components/ui/table/index.ts +9 -0
  263. package/src/components/ui/tabs/Tabs.vue +15 -0
  264. package/src/components/ui/tabs/TabsContent.vue +22 -0
  265. package/src/components/ui/tabs/TabsIndicator.vue +22 -0
  266. package/src/components/ui/tabs/TabsList.vue +25 -0
  267. package/src/components/ui/tabs/TabsTrigger.vue +29 -0
  268. package/src/components/ui/tabs/index.ts +5 -0
  269. package/src/components/ui/tags-input/TagsInput.vue +22 -0
  270. package/src/components/ui/tags-input/TagsInputInput.vue +19 -0
  271. package/src/components/ui/tags-input/TagsInputItem.vue +22 -0
  272. package/src/components/ui/tags-input/TagsInputItemDelete.vue +24 -0
  273. package/src/components/ui/tags-input/TagsInputItemText.vue +19 -0
  274. package/src/components/ui/tags-input/index.ts +5 -0
  275. package/src/components/ui/textarea/Textarea.vue +24 -0
  276. package/src/components/ui/textarea/index.ts +1 -0
  277. package/src/components/ui/toast/Toast.vue +57 -0
  278. package/src/components/ui/toast/ToastAction.vue +30 -0
  279. package/src/components/ui/toast/ToastClose.vue +31 -0
  280. package/src/components/ui/toast/ToastDescription.vue +25 -0
  281. package/src/components/ui/toast/ToastTitle.vue +25 -0
  282. package/src/components/ui/toast/Toaster.vue +31 -0
  283. package/src/components/ui/toast/index.ts +8 -0
  284. package/src/components/ui/toast/use-toast.ts +136 -0
  285. package/src/components/ui/toggle/Toggle.vue +35 -0
  286. package/src/components/ui/toggle/index.ts +27 -0
  287. package/src/components/ui/toggle-group/ToggleGroup.vue +34 -0
  288. package/src/components/ui/toggle-group/ToggleGroupItem.vue +35 -0
  289. package/src/components/ui/toggle-group/index.ts +2 -0
  290. package/src/components/ui/tooltip/Tooltip.vue +14 -0
  291. package/src/components/ui/tooltip/TooltipContent.vue +31 -0
  292. package/src/components/ui/tooltip/TooltipProvider.vue +11 -0
  293. package/src/components/ui/tooltip/TooltipTrigger.vue +11 -0
  294. package/src/components/ui/tooltip/index.ts +4 -0
  295. package/src/composables/glass/index.ts +8 -0
  296. package/src/composables/glass/useGlassRenderer.ts +252 -0
  297. package/src/composables/glass/webgl/frostShader.ts +221 -0
  298. package/src/composables/glass/webgpu/glassShader.wgsl +173 -0
  299. package/src/composables/index.ts +32 -0
  300. package/src/composables/infinite-scroll/index.ts +2 -0
  301. package/src/composables/infinite-scroll/types.ts +25 -0
  302. package/src/composables/infinite-scroll/useInfiniteScroll.ts +101 -0
  303. package/src/composables/interaction/index.ts +5 -0
  304. package/src/composables/interaction/useHeightTransition.ts +82 -0
  305. package/src/composables/interaction/useHoverPopover.ts +64 -0
  306. package/src/composables/interaction/useHoverToggle.ts +103 -0
  307. package/src/composables/interaction/useLeaveTimer.ts +17 -0
  308. package/src/composables/interaction/useTouchGate.ts +207 -0
  309. package/src/composables/pagination/index.ts +2 -0
  310. package/src/composables/pagination/useOffsetPagination.ts +70 -0
  311. package/src/composables/prng.ts +32 -0
  312. package/src/composables/useCharSplit.ts +31 -0
  313. package/src/composables/useClipboard.ts +46 -0
  314. package/src/composables/useGlobalDark.ts +61 -0
  315. package/src/composables/useKeyboardShortcuts.ts +205 -0
  316. package/src/composables/useWatercolorBlob.ts +136 -0
  317. package/src/composables/virtual/index.ts +22 -0
  318. package/src/composables/virtual/useVirtualSectionWindow.ts +338 -0
  319. package/src/composables/virtual/useWindowedStore.ts +86 -0
  320. package/src/composables/virtual/virtualSectionLayout.ts +212 -0
  321. package/src/index.ts +9 -0
  322. package/src/styles/animations.css +233 -0
  323. package/src/styles/cards.css +66 -0
  324. package/src/styles/dock.css +221 -0
  325. package/src/styles/floating-panel.css +49 -0
  326. package/src/styles/glass.css +266 -0
  327. package/src/styles/index.css +26 -0
  328. package/src/styles/scroll-pane.css +10 -0
  329. package/src/styles/theme.css +138 -0
  330. package/src/styles/tokens.css +333 -0
  331. package/src/styles/transitions.css +226 -0
  332. package/src/styles/typography.css +277 -0
  333. package/src/styles/utilities.css +697 -0
  334. package/src/utils/cn.ts +6 -0
  335. package/src/utils/index.ts +1 -0
@@ -0,0 +1,15 @@
1
+ <script setup lang="ts">
2
+ import { ContextMenuRoot, useForwardPropsEmits } from 'reka-ui'
3
+ import type { ContextMenuRootEmits, ContextMenuRootProps } from 'reka-ui'
4
+
5
+ const props = defineProps<ContextMenuRootProps>()
6
+ const emits = defineEmits<ContextMenuRootEmits>()
7
+
8
+ const forwarded = useForwardPropsEmits(props, emits)
9
+ </script>
10
+
11
+ <template>
12
+ <ContextMenuRoot v-bind="forwarded">
13
+ <slot />
14
+ </ContextMenuRoot>
15
+ </template>
@@ -0,0 +1,40 @@
1
+ <script setup lang="ts">
2
+ import { type HTMLAttributes, computed } from 'vue'
3
+ import {
4
+ ContextMenuCheckboxItem,
5
+ type ContextMenuCheckboxItemEmits,
6
+ type ContextMenuCheckboxItemProps,
7
+ ContextMenuItemIndicator,
8
+ useForwardPropsEmits,
9
+ } from 'reka-ui'
10
+ import { Check } from 'lucide-vue-next'
11
+ import { cn } from '../../../utils'
12
+
13
+ const props = defineProps<ContextMenuCheckboxItemProps & { class?: HTMLAttributes['class'] }>()
14
+ const emits = defineEmits<ContextMenuCheckboxItemEmits>()
15
+
16
+ const delegatedProps = computed(() => {
17
+ const { class: _, ...delegated } = props
18
+
19
+ return delegated
20
+ })
21
+
22
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
23
+ </script>
24
+
25
+ <template>
26
+ <ContextMenuCheckboxItem
27
+ v-bind="forwarded"
28
+ :class="cn(
29
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
30
+ props.class,
31
+ )"
32
+ >
33
+ <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
34
+ <ContextMenuItemIndicator>
35
+ <Check class="h-4 w-4" />
36
+ </ContextMenuItemIndicator>
37
+ </span>
38
+ <slot />
39
+ </ContextMenuCheckboxItem>
40
+ </template>
@@ -0,0 +1,36 @@
1
+ <script setup lang="ts">
2
+ import { type HTMLAttributes, computed } from 'vue'
3
+ import {
4
+ ContextMenuContent,
5
+ type ContextMenuContentEmits,
6
+ type ContextMenuContentProps,
7
+ ContextMenuPortal,
8
+ useForwardPropsEmits,
9
+ } from 'reka-ui'
10
+ import { cn } from '../../../utils'
11
+
12
+ const props = defineProps<ContextMenuContentProps & { class?: HTMLAttributes['class'] }>()
13
+ const emits = defineEmits<ContextMenuContentEmits>()
14
+
15
+ const delegatedProps = computed(() => {
16
+ const { class: _, ...delegated } = props
17
+
18
+ return delegated
19
+ })
20
+
21
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
22
+ </script>
23
+
24
+ <template>
25
+ <ContextMenuPortal>
26
+ <ContextMenuContent
27
+ v-bind="forwarded"
28
+ :class="cn(
29
+ 'z-popover min-w-32 overflow-hidden rounded-xl border bg-popover glass-elevated [backdrop-filter:var(--glass-blur-elevated)] p-1 text-popover-foreground shadow-md 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',
30
+ props.class,
31
+ )"
32
+ >
33
+ <slot />
34
+ </ContextMenuContent>
35
+ </ContextMenuPortal>
36
+ </template>
@@ -0,0 +1,11 @@
1
+ <script setup lang="ts">
2
+ import { ContextMenuGroup, type ContextMenuGroupProps } from 'reka-ui'
3
+
4
+ const props = defineProps<ContextMenuGroupProps>()
5
+ </script>
6
+
7
+ <template>
8
+ <ContextMenuGroup v-bind="props">
9
+ <slot />
10
+ </ContextMenuGroup>
11
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import { type HTMLAttributes, computed } from 'vue'
3
+ import {
4
+ ContextMenuItem,
5
+ type ContextMenuItemEmits,
6
+ type ContextMenuItemProps,
7
+ useForwardPropsEmits,
8
+ } from 'reka-ui'
9
+ import { cn } from '../../../utils'
10
+
11
+ const props = defineProps<ContextMenuItemProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
12
+ const emits = defineEmits<ContextMenuItemEmits>()
13
+
14
+ const delegatedProps = computed(() => {
15
+ const { class: _, ...delegated } = props
16
+
17
+ return delegated
18
+ })
19
+
20
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
21
+ </script>
22
+
23
+ <template>
24
+ <ContextMenuItem
25
+ v-bind="forwarded"
26
+ :class="cn(
27
+ 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
28
+ inset && 'pl-8',
29
+ props.class,
30
+ )"
31
+ >
32
+ <slot />
33
+ </ContextMenuItem>
34
+ </template>
@@ -0,0 +1,25 @@
1
+ <script setup lang="ts">
2
+ import { type HTMLAttributes, computed } from 'vue'
3
+ import { ContextMenuLabel, type ContextMenuLabelProps } from 'reka-ui'
4
+ import { cn } from '../../../utils'
5
+
6
+ const props = defineProps<ContextMenuLabelProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
7
+
8
+ const delegatedProps = computed(() => {
9
+ const { class: _, ...delegated } = props
10
+
11
+ return delegated
12
+ })
13
+ </script>
14
+
15
+ <template>
16
+ <ContextMenuLabel
17
+ v-bind="delegatedProps"
18
+ :class="
19
+ cn('px-2 py-1.5 text-sm font-semibold text-foreground',
20
+ inset && 'pl-8', props.class,
21
+ )"
22
+ >
23
+ <slot />
24
+ </ContextMenuLabel>
25
+ </template>
@@ -0,0 +1,11 @@
1
+ <script setup lang="ts">
2
+ import { ContextMenuPortal, type ContextMenuPortalProps } from 'reka-ui'
3
+
4
+ const props = defineProps<ContextMenuPortalProps>()
5
+ </script>
6
+
7
+ <template>
8
+ <ContextMenuPortal v-bind="props">
9
+ <slot />
10
+ </ContextMenuPortal>
11
+ </template>
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ ContextMenuRadioGroup,
4
+ type ContextMenuRadioGroupEmits,
5
+ type ContextMenuRadioGroupProps,
6
+ useForwardPropsEmits,
7
+ } from 'reka-ui'
8
+
9
+ const props = defineProps<ContextMenuRadioGroupProps>()
10
+ const emits = defineEmits<ContextMenuRadioGroupEmits>()
11
+
12
+ const forwarded = useForwardPropsEmits(props, emits)
13
+ </script>
14
+
15
+ <template>
16
+ <ContextMenuRadioGroup v-bind="forwarded">
17
+ <slot />
18
+ </ContextMenuRadioGroup>
19
+ </template>
@@ -0,0 +1,40 @@
1
+ <script setup lang="ts">
2
+ import { type HTMLAttributes, computed } from 'vue'
3
+ import {
4
+ ContextMenuItemIndicator,
5
+ ContextMenuRadioItem,
6
+ type ContextMenuRadioItemEmits,
7
+ type ContextMenuRadioItemProps,
8
+ useForwardPropsEmits,
9
+ } from 'reka-ui'
10
+ import { Circle } from 'lucide-vue-next'
11
+ import { cn } from '../../../utils'
12
+
13
+ const props = defineProps<ContextMenuRadioItemProps & { class?: HTMLAttributes['class'] }>()
14
+ const emits = defineEmits<ContextMenuRadioItemEmits>()
15
+
16
+ const delegatedProps = computed(() => {
17
+ const { class: _, ...delegated } = props
18
+
19
+ return delegated
20
+ })
21
+
22
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
23
+ </script>
24
+
25
+ <template>
26
+ <ContextMenuRadioItem
27
+ v-bind="forwarded"
28
+ :class="cn(
29
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
30
+ props.class,
31
+ )"
32
+ >
33
+ <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
34
+ <ContextMenuItemIndicator>
35
+ <Circle class="h-2 w-2 fill-current" />
36
+ </ContextMenuItemIndicator>
37
+ </span>
38
+ <slot />
39
+ </ContextMenuRadioItem>
40
+ </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ import { type HTMLAttributes, computed } from 'vue'
3
+ import {
4
+ ContextMenuSeparator,
5
+ type ContextMenuSeparatorProps,
6
+ } from 'reka-ui'
7
+ import { cn } from '../../../utils'
8
+
9
+ const props = defineProps<ContextMenuSeparatorProps & { class?: HTMLAttributes['class'] }>()
10
+
11
+ const delegatedProps = computed(() => {
12
+ const { class: _, ...delegated } = props
13
+
14
+ return delegated
15
+ })
16
+ </script>
17
+
18
+ <template>
19
+ <ContextMenuSeparator v-bind="delegatedProps" :class="cn('-mx-1 my-1 h-px bg-border', props.class)" />
20
+ </template>
@@ -0,0 +1,14 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { cn } from '../../../utils'
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class']
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <span :class="cn('ml-auto text-xs tracking-widest text-muted-foreground', props.class)">
12
+ <slot />
13
+ </span>
14
+ </template>
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ ContextMenuSub,
4
+ type ContextMenuSubEmits,
5
+ type ContextMenuSubProps,
6
+ useForwardPropsEmits,
7
+ } from 'reka-ui'
8
+
9
+ const props = defineProps<ContextMenuSubProps>()
10
+ const emits = defineEmits<ContextMenuSubEmits>()
11
+
12
+ const forwarded = useForwardPropsEmits(props, emits)
13
+ </script>
14
+
15
+ <template>
16
+ <ContextMenuSub v-bind="forwarded">
17
+ <slot />
18
+ </ContextMenuSub>
19
+ </template>
@@ -0,0 +1,35 @@
1
+ <script setup lang="ts">
2
+ import { type HTMLAttributes, computed } from 'vue'
3
+ import {
4
+ ContextMenuSubContent,
5
+ type DropdownMenuSubContentEmits,
6
+ type DropdownMenuSubContentProps,
7
+ useForwardPropsEmits,
8
+ } from 'reka-ui'
9
+ import { cn } from '../../../utils'
10
+
11
+ const props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes['class'] }>()
12
+ const emits = defineEmits<DropdownMenuSubContentEmits>()
13
+
14
+ const delegatedProps = computed(() => {
15
+ const { class: _, ...delegated } = props
16
+
17
+ return delegated
18
+ })
19
+
20
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
21
+ </script>
22
+
23
+ <template>
24
+ <ContextMenuSubContent
25
+ v-bind="forwarded"
26
+ :class="
27
+ cn(
28
+ 'z-popover min-w-32 overflow-hidden rounded-md border bg-popover glass-elevated [backdrop-filter:var(--glass-blur-elevated)] p-1 text-popover-foreground shadow-md 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',
29
+ props.class,
30
+ )
31
+ "
32
+ >
33
+ <slot />
34
+ </ContextMenuSubContent>
35
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import { type HTMLAttributes, computed } from 'vue'
3
+ import {
4
+ ContextMenuSubTrigger,
5
+ type ContextMenuSubTriggerProps,
6
+ useForwardProps,
7
+ } from 'reka-ui'
8
+ import { ChevronRight } from 'lucide-vue-next'
9
+ import { cn } from '../../../utils'
10
+
11
+ const props = defineProps<ContextMenuSubTriggerProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
12
+
13
+ const delegatedProps = computed(() => {
14
+ const { class: _, ...delegated } = props
15
+
16
+ return delegated
17
+ })
18
+
19
+ const forwardedProps = useForwardProps(delegatedProps)
20
+ </script>
21
+
22
+ <template>
23
+ <ContextMenuSubTrigger
24
+ v-bind="forwardedProps"
25
+ :class="cn(
26
+ 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
27
+ inset && 'pl-8',
28
+ props.class,
29
+ )"
30
+ >
31
+ <slot />
32
+ <ChevronRight class="ml-auto h-4 w-4" />
33
+ </ContextMenuSubTrigger>
34
+ </template>
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts">
2
+ import { ContextMenuTrigger, type ContextMenuTriggerProps, useForwardProps } from 'reka-ui'
3
+
4
+ const props = defineProps<ContextMenuTriggerProps>()
5
+
6
+ const forwardedProps = useForwardProps(props)
7
+ </script>
8
+
9
+ <template>
10
+ <ContextMenuTrigger v-bind="forwardedProps">
11
+ <slot />
12
+ </ContextMenuTrigger>
13
+ </template>
@@ -0,0 +1,14 @@
1
+ export { default as ContextMenu } from './ContextMenu.vue'
2
+ export { default as ContextMenuTrigger } from './ContextMenuTrigger.vue'
3
+ export { default as ContextMenuContent } from './ContextMenuContent.vue'
4
+ export { default as ContextMenuGroup } from './ContextMenuGroup.vue'
5
+ export { default as ContextMenuRadioGroup } from './ContextMenuRadioGroup.vue'
6
+ export { default as ContextMenuItem } from './ContextMenuItem.vue'
7
+ export { default as ContextMenuCheckboxItem } from './ContextMenuCheckboxItem.vue'
8
+ export { default as ContextMenuRadioItem } from './ContextMenuRadioItem.vue'
9
+ export { default as ContextMenuShortcut } from './ContextMenuShortcut.vue'
10
+ export { default as ContextMenuSeparator } from './ContextMenuSeparator.vue'
11
+ export { default as ContextMenuLabel } from './ContextMenuLabel.vue'
12
+ export { default as ContextMenuSub } from './ContextMenuSub.vue'
13
+ export { default as ContextMenuSubTrigger } from './ContextMenuSubTrigger.vue'
14
+ export { default as ContextMenuSubContent } from './ContextMenuSubContent.vue'
@@ -0,0 +1,167 @@
1
+ <script setup lang="ts" generic="T extends Record<string, any>">
2
+ import { computed } from "vue";
3
+ import {
4
+ Table,
5
+ TableBody,
6
+ TableCell,
7
+ TableHead,
8
+ TableHeader,
9
+ TableRow,
10
+ TableEmpty,
11
+ } from "../table";
12
+ import { Skeleton } from "../skeleton";
13
+ import DataTablePagination from "./DataTablePagination.vue";
14
+ import type { DataTableColumn, DataTableSort } from "./types";
15
+ import { cn } from "../../../utils";
16
+
17
+ const props = withDefaults(
18
+ defineProps<{
19
+ columns: DataTableColumn<T>[];
20
+ rows: T[];
21
+ total: number;
22
+ page: number;
23
+ pageSize: number;
24
+ isLoading?: boolean;
25
+ rowKey?: string;
26
+ sort?: DataTableSort;
27
+ infinite?: boolean;
28
+ hasMore?: boolean;
29
+ class?: string;
30
+ }>(),
31
+ {
32
+ isLoading: false,
33
+ rowKey: "_id",
34
+ infinite: false,
35
+ hasMore: false,
36
+ },
37
+ );
38
+
39
+ const emit = defineEmits<{
40
+ "update:page": [page: number];
41
+ "update:sort": [sort: DataTableSort];
42
+ select: [row: T];
43
+ "load-more": [];
44
+ }>();
45
+
46
+ const skeletonRows = computed(() =>
47
+ Array.from({ length: Math.min(props.pageSize, 5) }, (_, i) => i),
48
+ );
49
+
50
+ function getNestedValue(obj: any, key: string): any {
51
+ return key.split(".").reduce((o, k) => o?.[k], obj);
52
+ }
53
+
54
+ function getCellValue(row: T, col: DataTableColumn<T>): string {
55
+ const raw = getNestedValue(row, col.key);
56
+ if (col.formatter) return col.formatter(raw, row);
57
+ return raw == null ? "\u2014" : String(raw);
58
+ }
59
+
60
+ function getAlignClass(align?: string): string {
61
+ if (align === "right") return "text-right";
62
+ if (align === "center") return "text-center";
63
+ return "text-left";
64
+ }
65
+
66
+ function toggleSort(col: DataTableColumn<T>) {
67
+ if (!col.sortable) return;
68
+ const current = props.sort;
69
+ if (current?.key === col.key) {
70
+ emit("update:sort", {
71
+ key: col.key,
72
+ direction: current.direction === "asc" ? "desc" : "asc",
73
+ });
74
+ } else {
75
+ emit("update:sort", { key: col.key, direction: "asc" });
76
+ }
77
+ }
78
+
79
+ function sortIndicator(col: DataTableColumn<T>): string {
80
+ if (!col.sortable) return "";
81
+ if (props.sort?.key !== col.key) return " \u2195";
82
+ return props.sort.direction === "asc" ? " \u2191" : " \u2193";
83
+ }
84
+ </script>
85
+
86
+ <template>
87
+ <div :class="cn('overflow-hidden', props.class)">
88
+ <Table>
89
+ <TableHeader>
90
+ <TableRow class="text-muted-foreground">
91
+ <TableHead
92
+ v-for="col in columns"
93
+ :key="col.key"
94
+ :class="
95
+ cn(
96
+ getAlignClass(col.align),
97
+ col.sortable && 'cursor-pointer select-none',
98
+ col.headerClass,
99
+ )
100
+ "
101
+ @click="toggleSort(col)"
102
+ >
103
+ {{ col.label }}{{ sortIndicator(col) }}
104
+ </TableHead>
105
+ </TableRow>
106
+ </TableHeader>
107
+
108
+ <TableBody>
109
+ <!-- Loading skeleton -->
110
+ <template v-if="isLoading && rows.length === 0">
111
+ <TableRow v-for="i in skeletonRows" :key="'skel-' + i">
112
+ <TableCell
113
+ v-for="col in columns"
114
+ :key="col.key"
115
+ :class="getAlignClass(col.align)"
116
+ >
117
+ <Skeleton class="h-4 w-3/4" />
118
+ </TableCell>
119
+ </TableRow>
120
+ </template>
121
+
122
+ <!-- Data rows -->
123
+ <template v-else-if="rows.length > 0">
124
+ <TableRow
125
+ v-for="row in rows"
126
+ :key="getNestedValue(row, rowKey) ?? undefined"
127
+ class="cursor-pointer"
128
+ @click="emit('select', row)"
129
+ >
130
+ <TableCell
131
+ v-for="col in columns"
132
+ :key="col.key"
133
+ :class="cn(getAlignClass(col.align), col.class)"
134
+ >
135
+ <component
136
+ v-if="col.component"
137
+ :is="col.component"
138
+ :value="getNestedValue(row, col.key)"
139
+ :row="row"
140
+ />
141
+ <template v-else>
142
+ {{ getCellValue(row, col) }}
143
+ </template>
144
+ </TableCell>
145
+ </TableRow>
146
+ </template>
147
+
148
+ <!-- Empty state -->
149
+ <TableEmpty v-else :colspan="columns.length">
150
+ <slot name="empty">No results found</slot>
151
+ </TableEmpty>
152
+ </TableBody>
153
+ </Table>
154
+
155
+ <!-- Infinite scroll sentinel -->
156
+ <slot v-if="infinite" name="sentinel" />
157
+
158
+ <!-- Pagination -->
159
+ <DataTablePagination
160
+ v-if="!infinite && total > 0"
161
+ :page="page"
162
+ :page-size="pageSize"
163
+ :total="total"
164
+ @update:page="emit('update:page', $event)"
165
+ />
166
+ </div>
167
+ </template>
@@ -0,0 +1,112 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import { Button } from "../button";
4
+ import { cn } from "../../../utils";
5
+
6
+ const props = defineProps<{
7
+ page: number;
8
+ pageSize: number;
9
+ total: number;
10
+ class?: string;
11
+ }>();
12
+
13
+ const emit = defineEmits<{
14
+ "update:page": [page: number];
15
+ }>();
16
+
17
+ const totalPages = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)));
18
+
19
+ const clampedPage = computed(() =>
20
+ Math.min(Math.max(1, props.page), totalPages.value),
21
+ );
22
+
23
+ /** Visible page numbers with ellipsis markers (null = ellipsis). */
24
+ const visiblePages = computed(() => {
25
+ const total = totalPages.value;
26
+ const current = clampedPage.value;
27
+ if (total <= 7) {
28
+ return Array.from({ length: total }, (_, i) => i + 1);
29
+ }
30
+ const pages: (number | null)[] = [1];
31
+ if (current > 3) pages.push(null);
32
+ const start = Math.max(2, current - 1);
33
+ const end = Math.min(total - 1, current + 1);
34
+ for (let i = start; i <= end; i++) pages.push(i);
35
+ if (current < total - 2) pages.push(null);
36
+ pages.push(total);
37
+ return pages;
38
+ });
39
+
40
+ function goTo(p: number) {
41
+ const clamped = Math.min(Math.max(1, p), totalPages.value);
42
+ if (clamped !== props.page) emit("update:page", clamped);
43
+ }
44
+ </script>
45
+
46
+ <template>
47
+ <div
48
+ :class="
49
+ cn(
50
+ 'flex items-center justify-between border-t border-border px-4 py-2 text-sm text-muted-foreground',
51
+ props.class,
52
+ )
53
+ "
54
+ >
55
+ <span class="tabular-nums">
56
+ {{ total.toLocaleString() }} result{{ total === 1 ? "" : "s" }}
57
+ </span>
58
+
59
+ <div class="flex items-center gap-1">
60
+ <Button
61
+ variant="ghost"
62
+ size="icon"
63
+ :disabled="clampedPage <= 1"
64
+ @click="goTo(1)"
65
+ aria-label="First page"
66
+ >
67
+ ⟨⟨
68
+ </Button>
69
+ <Button
70
+ variant="ghost"
71
+ size="icon"
72
+ :disabled="clampedPage <= 1"
73
+ @click="goTo(clampedPage - 1)"
74
+ aria-label="Previous page"
75
+ >
76
+
77
+ </Button>
78
+
79
+ <template v-for="(p, i) in visiblePages" :key="i">
80
+ <span v-if="p === null" class="px-1 select-none">...</span>
81
+ <Button
82
+ v-else
83
+ :variant="p === clampedPage ? 'default' : 'ghost'"
84
+ size="icon"
85
+ class="tabular-nums"
86
+ @click="goTo(p)"
87
+ >
88
+ {{ p }}
89
+ </Button>
90
+ </template>
91
+
92
+ <Button
93
+ variant="ghost"
94
+ size="icon"
95
+ :disabled="clampedPage >= totalPages"
96
+ @click="goTo(clampedPage + 1)"
97
+ aria-label="Next page"
98
+ >
99
+
100
+ </Button>
101
+ <Button
102
+ variant="ghost"
103
+ size="icon"
104
+ :disabled="clampedPage >= totalPages"
105
+ @click="goTo(totalPages)"
106
+ aria-label="Last page"
107
+ >
108
+ ⟩⟩
109
+ </Button>
110
+ </div>
111
+ </div>
112
+ </template>