@vestow/vue 0.1.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 (491) hide show
  1. package/README.md +233 -0
  2. package/index.ts +8 -0
  3. package/package.json +68 -0
  4. package/src/components/accordion/Accordion.vue +45 -0
  5. package/src/components/accordion/AccordionContent.vue +31 -0
  6. package/src/components/accordion/AccordionItem.vue +26 -0
  7. package/src/components/accordion/AccordionTrigger.vue +39 -0
  8. package/src/components/accordion/index.ts +18 -0
  9. package/src/components/alert/Alert.vue +17 -0
  10. package/src/components/alert/AlertDescription.vue +12 -0
  11. package/src/components/alert/AlertTitle.vue +12 -0
  12. package/src/components/alert/index.ts +5 -0
  13. package/src/components/avatar/Avatar.vue +32 -0
  14. package/src/components/avatar/AvatarFallback.vue +23 -0
  15. package/src/components/avatar/AvatarImage.vue +18 -0
  16. package/src/components/avatar/index.ts +3 -0
  17. package/src/components/badge/Badge.vue +25 -0
  18. package/src/components/badge/index.ts +3 -0
  19. package/src/components/breadcrumb/Breadcrumb.vue +14 -0
  20. package/src/components/breadcrumb/BreadcrumbItem.vue +12 -0
  21. package/src/components/breadcrumb/BreadcrumbLink.vue +19 -0
  22. package/src/components/breadcrumb/BreadcrumbPage.vue +12 -0
  23. package/src/components/breadcrumb/BreadcrumbSeparator.vue +15 -0
  24. package/src/components/breadcrumb/index.ts +5 -0
  25. package/src/components/button/Button.vue +55 -0
  26. package/src/components/button/index.ts +3 -0
  27. package/src/components/button-group/ButtonGroup.vue +29 -0
  28. package/src/components/button-group/ButtonGroupSeparator.vue +30 -0
  29. package/src/components/button-group/ButtonGroupText.vue +29 -0
  30. package/src/components/button-group/SplitButton.vue +103 -0
  31. package/src/components/button-group/SplitButtonItems.vue +50 -0
  32. package/src/components/button-group/index.ts +34 -0
  33. package/src/components/calendar/Calendar.vue +120 -0
  34. package/src/components/calendar/RangeCalendar.vue +127 -0
  35. package/src/components/calendar/index.ts +2 -0
  36. package/src/components/card/Card.vue +12 -0
  37. package/src/components/card/CardAction.vue +12 -0
  38. package/src/components/card/CardContent.vue +12 -0
  39. package/src/components/card/CardDescription.vue +12 -0
  40. package/src/components/card/CardFooter.vue +12 -0
  41. package/src/components/card/CardHeader.vue +15 -0
  42. package/src/components/card/CardTitle.vue +12 -0
  43. package/src/components/card/index.ts +7 -0
  44. package/src/components/carousel/Carousel.vue +164 -0
  45. package/src/components/carousel/CarouselContent.vue +43 -0
  46. package/src/components/carousel/CarouselDots.vue +42 -0
  47. package/src/components/carousel/CarouselItem.vue +58 -0
  48. package/src/components/carousel/CarouselNext.vue +45 -0
  49. package/src/components/carousel/CarouselPrevious.vue +45 -0
  50. package/src/components/carousel/index.ts +44 -0
  51. package/src/components/cascade-select/CascadeSelect.vue +314 -0
  52. package/src/components/cascade-select/index.ts +11 -0
  53. package/src/components/chart/AreaChart.vue +201 -0
  54. package/src/components/chart/BarChart.vue +389 -0
  55. package/src/components/chart/ChartContainer.vue +127 -0
  56. package/src/components/chart/ChartCrosshair.vue +92 -0
  57. package/src/components/chart/ChartLegend.vue +63 -0
  58. package/src/components/chart/ChartTooltipCard.vue +49 -0
  59. package/src/components/chart/LineChart.vue +214 -0
  60. package/src/components/chart/PieChart.vue +280 -0
  61. package/src/components/chart/RadarChart.vue +291 -0
  62. package/src/components/chart/RadialChart.vue +354 -0
  63. package/src/components/chart/index.ts +30 -0
  64. package/src/components/chart/polar.ts +5 -0
  65. package/src/components/chart/tooltip.ts +5 -0
  66. package/src/components/chart/types.ts +52 -0
  67. package/src/components/chart/useChartSeries.ts +138 -0
  68. package/src/components/chart/xy-label.ts +5 -0
  69. package/src/components/checkbox/Checkbox.vue +81 -0
  70. package/src/components/checkbox/CheckboxField.vue +129 -0
  71. package/src/components/checkbox/CheckboxGroup.vue +62 -0
  72. package/src/components/checkbox/index.ts +21 -0
  73. package/src/components/chip/Chip.vue +73 -0
  74. package/src/components/chip/index.ts +3 -0
  75. package/src/components/code-block/CodeBlock.vue +100 -0
  76. package/src/components/code-block/index.ts +4 -0
  77. package/src/components/collapsible/Collapsible.vue +15 -0
  78. package/src/components/collapsible/CollapsibleContent.vue +26 -0
  79. package/src/components/collapsible/CollapsibleTrigger.vue +12 -0
  80. package/src/components/collapsible/index.ts +3 -0
  81. package/src/components/combobox/Combobox.vue +23 -0
  82. package/src/components/combobox/ComboboxAnchor.vue +20 -0
  83. package/src/components/combobox/ComboboxEmpty.vue +21 -0
  84. package/src/components/combobox/ComboboxGroup.vue +23 -0
  85. package/src/components/combobox/ComboboxInput.vue +32 -0
  86. package/src/components/combobox/ComboboxItem.vue +38 -0
  87. package/src/components/combobox/ComboboxList.vue +43 -0
  88. package/src/components/combobox/ComboboxSeparator.vue +18 -0
  89. package/src/components/combobox/ComboboxTrigger.vue +12 -0
  90. package/src/components/combobox/index.ts +9 -0
  91. package/src/components/command/Command.vue +54 -0
  92. package/src/components/command/CommandDialog.vue +41 -0
  93. package/src/components/command/CommandEmpty.vue +19 -0
  94. package/src/components/command/CommandGroup.vue +28 -0
  95. package/src/components/command/CommandInput.vue +25 -0
  96. package/src/components/command/CommandItem.vue +40 -0
  97. package/src/components/command/CommandList.vue +13 -0
  98. package/src/components/command/CommandSeparator.vue +11 -0
  99. package/src/components/command/CommandShortcut.vue +13 -0
  100. package/src/components/command/index.ts +34 -0
  101. package/src/components/command-block/CommandBlock.vue +90 -0
  102. package/src/components/command-block/index.ts +2 -0
  103. package/src/components/confirm-dialog/ConfirmDialog.vue +37 -0
  104. package/src/components/confirm-dialog/index.ts +1 -0
  105. package/src/components/confirm-popup/ConfirmPopup.vue +123 -0
  106. package/src/components/confirm-popup/index.ts +1 -0
  107. package/src/components/context-menu/ContextMenu.vue +15 -0
  108. package/src/components/context-menu/ContextMenuCheckboxItem.vue +30 -0
  109. package/src/components/context-menu/ContextMenuContent.vue +36 -0
  110. package/src/components/context-menu/ContextMenuGroup.vue +12 -0
  111. package/src/components/context-menu/ContextMenuItem.vue +32 -0
  112. package/src/components/context-menu/ContextMenuLabel.vue +21 -0
  113. package/src/components/context-menu/ContextMenuRadioGroup.vue +15 -0
  114. package/src/components/context-menu/ContextMenuRadioItem.vue +30 -0
  115. package/src/components/context-menu/ContextMenuSeparator.vue +19 -0
  116. package/src/components/context-menu/ContextMenuShortcut.vue +13 -0
  117. package/src/components/context-menu/ContextMenuSub.vue +15 -0
  118. package/src/components/context-menu/ContextMenuSubContent.vue +26 -0
  119. package/src/components/context-menu/ContextMenuSubTrigger.vue +26 -0
  120. package/src/components/context-menu/ContextMenuTrigger.vue +12 -0
  121. package/src/components/context-menu/index.ts +14 -0
  122. package/src/components/data-table/DataTable.vue +458 -0
  123. package/src/components/data-table/DataTableFacetedFilter.vue +97 -0
  124. package/src/components/data-table/DataTablePagination.vue +103 -0
  125. package/src/components/data-table/DataTableViewOptions.vue +69 -0
  126. package/src/components/data-table/features.ts +87 -0
  127. package/src/components/data-table/index.ts +27 -0
  128. package/src/components/data-table/pagination-adapter.ts +67 -0
  129. package/src/components/data-table/selection-column.ts +48 -0
  130. package/src/components/data-table/types.ts +36 -0
  131. package/src/components/data-view/DataView.vue +136 -0
  132. package/src/components/data-view/index.ts +1 -0
  133. package/src/components/date-picker/DatePicker.vue +117 -0
  134. package/src/components/date-picker/DateRangePicker.vue +122 -0
  135. package/src/components/date-picker/index.ts +2 -0
  136. package/src/components/deferred/Deferred.vue +80 -0
  137. package/src/components/deferred/index.ts +1 -0
  138. package/src/components/dialog/Dialog.vue +15 -0
  139. package/src/components/dialog/DialogClose.vue +12 -0
  140. package/src/components/dialog/DialogContent.vue +46 -0
  141. package/src/components/dialog/DialogDescription.vue +19 -0
  142. package/src/components/dialog/DialogFooter.vue +12 -0
  143. package/src/components/dialog/DialogHeader.vue +12 -0
  144. package/src/components/dialog/DialogTitle.vue +19 -0
  145. package/src/components/dialog/DialogTrigger.vue +12 -0
  146. package/src/components/dialog/index.ts +10 -0
  147. package/src/components/doc-example/DocExample.vue +41 -0
  148. package/src/components/doc-example/index.ts +1 -0
  149. package/src/components/doc-text/DocText.vue +64 -0
  150. package/src/components/doc-text/index.ts +1 -0
  151. package/src/components/dropdown-menu/DropdownMenu.vue +15 -0
  152. package/src/components/dropdown-menu/DropdownMenuCheckboxItem.vue +33 -0
  153. package/src/components/dropdown-menu/DropdownMenuContent.vue +36 -0
  154. package/src/components/dropdown-menu/DropdownMenuGroup.vue +12 -0
  155. package/src/components/dropdown-menu/DropdownMenuItem.vue +32 -0
  156. package/src/components/dropdown-menu/DropdownMenuLabel.vue +24 -0
  157. package/src/components/dropdown-menu/DropdownMenuRadioGroup.vue +15 -0
  158. package/src/components/dropdown-menu/DropdownMenuRadioItem.vue +33 -0
  159. package/src/components/dropdown-menu/DropdownMenuSeparator.vue +19 -0
  160. package/src/components/dropdown-menu/DropdownMenuShortcut.vue +13 -0
  161. package/src/components/dropdown-menu/DropdownMenuSub.vue +15 -0
  162. package/src/components/dropdown-menu/DropdownMenuSubContent.vue +29 -0
  163. package/src/components/dropdown-menu/DropdownMenuSubTrigger.vue +26 -0
  164. package/src/components/dropdown-menu/DropdownMenuTrigger.vue +12 -0
  165. package/src/components/dropdown-menu/index.ts +14 -0
  166. package/src/components/editable/Editable.vue +41 -0
  167. package/src/components/editable/EditableArea.vue +22 -0
  168. package/src/components/editable/EditableCancelTrigger.vue +40 -0
  169. package/src/components/editable/EditableEditTrigger.vue +40 -0
  170. package/src/components/editable/EditableInput.vue +28 -0
  171. package/src/components/editable/EditablePreview.vue +34 -0
  172. package/src/components/editable/EditableSubmitTrigger.vue +40 -0
  173. package/src/components/editable/index.ts +17 -0
  174. package/src/components/empty/Empty.vue +17 -0
  175. package/src/components/empty/EmptyActions.vue +14 -0
  176. package/src/components/empty/EmptyDescription.vue +12 -0
  177. package/src/components/empty/EmptyMedia.vue +16 -0
  178. package/src/components/empty/EmptyTitle.vue +17 -0
  179. package/src/components/empty/index.ts +7 -0
  180. package/src/components/field/Field.vue +22 -0
  181. package/src/components/field/FieldDescription.vue +20 -0
  182. package/src/components/field/FieldGroup.vue +22 -0
  183. package/src/components/field/FieldLabel.vue +27 -0
  184. package/src/components/field/FieldSeparator.vue +28 -0
  185. package/src/components/field/index.ts +7 -0
  186. package/src/components/fieldset/Fieldset.vue +126 -0
  187. package/src/components/fieldset/index.ts +1 -0
  188. package/src/components/file-upload/FileUpload.vue +149 -0
  189. package/src/components/file-upload/index.ts +8 -0
  190. package/src/components/float-label/FloatLabel.vue +26 -0
  191. package/src/components/float-label/index.ts +3 -0
  192. package/src/components/form/Form.vue +126 -0
  193. package/src/components/form/FormDescription.vue +12 -0
  194. package/src/components/form/FormField.vue +127 -0
  195. package/src/components/form/FormItem.vue +12 -0
  196. package/src/components/form/FormLabel.vue +14 -0
  197. package/src/components/form/FormMessage.vue +31 -0
  198. package/src/components/form/context.ts +54 -0
  199. package/src/components/form/errors.ts +6 -0
  200. package/src/components/form/index.ts +7 -0
  201. package/src/components/form/types.ts +55 -0
  202. package/src/components/hover-card/HoverCard.vue +27 -0
  203. package/src/components/hover-card/HoverCardContent.vue +36 -0
  204. package/src/components/hover-card/HoverCardTrigger.vue +12 -0
  205. package/src/components/hover-card/index.ts +3 -0
  206. package/src/components/ifta-label/IftaLabel.vue +26 -0
  207. package/src/components/ifta-label/index.ts +1 -0
  208. package/src/components/index.ts +89 -0
  209. package/src/components/input/Input.vue +87 -0
  210. package/src/components/input/index.ts +1 -0
  211. package/src/components/input-color/ColorArea.vue +36 -0
  212. package/src/components/input-color/ColorField.vue +32 -0
  213. package/src/components/input-color/ColorSlider.vue +31 -0
  214. package/src/components/input-color/InputColor.vue +65 -0
  215. package/src/components/input-color/index.ts +16 -0
  216. package/src/components/input-group/InputGroup.vue +26 -0
  217. package/src/components/input-group/InputGroupAddon.vue +32 -0
  218. package/src/components/input-group/InputGroupInput.vue +33 -0
  219. package/src/components/input-group/index.ts +17 -0
  220. package/src/components/input-number/InputNumber.vue +82 -0
  221. package/src/components/input-number/index.ts +1 -0
  222. package/src/components/input-otp/InputOtp.vue +186 -0
  223. package/src/components/input-otp/index.ts +1 -0
  224. package/src/components/input-password/InputPassword.vue +116 -0
  225. package/src/components/input-password/index.ts +5 -0
  226. package/src/components/input-tags/InputTags.vue +38 -0
  227. package/src/components/input-tags/InputTagsClear.vue +39 -0
  228. package/src/components/input-tags/InputTagsInput.vue +27 -0
  229. package/src/components/input-tags/InputTagsItem.vue +40 -0
  230. package/src/components/input-tags/InputTagsItemDelete.vue +32 -0
  231. package/src/components/input-tags/InputTagsItemText.vue +18 -0
  232. package/src/components/input-tags/index.ts +16 -0
  233. package/src/components/kbd/Kbd.vue +17 -0
  234. package/src/components/kbd/KbdGroup.vue +15 -0
  235. package/src/components/kbd/index.ts +4 -0
  236. package/src/components/label/Label.vue +23 -0
  237. package/src/components/label/index.ts +1 -0
  238. package/src/components/listbox/Listbox.vue +42 -0
  239. package/src/components/listbox/ListboxContent.vue +22 -0
  240. package/src/components/listbox/ListboxFilter.vue +34 -0
  241. package/src/components/listbox/ListboxGroup.vue +31 -0
  242. package/src/components/listbox/ListboxItem.vue +46 -0
  243. package/src/components/listbox/index.ts +19 -0
  244. package/src/components/mask/Mask.vue +205 -0
  245. package/src/components/mask/index.ts +15 -0
  246. package/src/components/menubar/Menubar.vue +26 -0
  247. package/src/components/menubar/MenubarCheckboxItem.vue +30 -0
  248. package/src/components/menubar/MenubarContent.vue +52 -0
  249. package/src/components/menubar/MenubarGroup.vue +12 -0
  250. package/src/components/menubar/MenubarItem.vue +32 -0
  251. package/src/components/menubar/MenubarLabel.vue +21 -0
  252. package/src/components/menubar/MenubarMenu.vue +12 -0
  253. package/src/components/menubar/MenubarRadioGroup.vue +15 -0
  254. package/src/components/menubar/MenubarRadioItem.vue +30 -0
  255. package/src/components/menubar/MenubarSeparator.vue +12 -0
  256. package/src/components/menubar/MenubarShortcut.vue +13 -0
  257. package/src/components/menubar/MenubarSub.vue +15 -0
  258. package/src/components/menubar/MenubarSubContent.vue +26 -0
  259. package/src/components/menubar/MenubarSubTrigger.vue +26 -0
  260. package/src/components/menubar/MenubarTrigger.vue +30 -0
  261. package/src/components/menubar/index.ts +29 -0
  262. package/src/components/navigation-menu/NavigationMenu.vue +43 -0
  263. package/src/components/navigation-menu/NavigationMenuContent.vue +63 -0
  264. package/src/components/navigation-menu/NavigationMenuIndicator.vue +31 -0
  265. package/src/components/navigation-menu/NavigationMenuItem.vue +20 -0
  266. package/src/components/navigation-menu/NavigationMenuLink.vue +34 -0
  267. package/src/components/navigation-menu/NavigationMenuList.vue +23 -0
  268. package/src/components/navigation-menu/NavigationMenuTrigger.vue +38 -0
  269. package/src/components/navigation-menu/NavigationMenuViewport.vue +34 -0
  270. package/src/components/navigation-menu/index.ts +18 -0
  271. package/src/components/order-list/OrderList.vue +168 -0
  272. package/src/components/order-list/SelectableList.vue +200 -0
  273. package/src/components/order-list/index.ts +1 -0
  274. package/src/components/pagination/Pagination.vue +133 -0
  275. package/src/components/pagination/index.ts +1 -0
  276. package/src/components/panel/Panel.vue +69 -0
  277. package/src/components/panel/index.ts +1 -0
  278. package/src/components/pick-list/PickList.vue +211 -0
  279. package/src/components/pick-list/index.ts +1 -0
  280. package/src/components/popover/Popover.vue +15 -0
  281. package/src/components/popover/PopoverAnchor.vue +12 -0
  282. package/src/components/popover/PopoverContent.vue +39 -0
  283. package/src/components/popover/PopoverTrigger.vue +12 -0
  284. package/src/components/popover/index.ts +4 -0
  285. package/src/components/progress/Progress.vue +39 -0
  286. package/src/components/progress/index.ts +1 -0
  287. package/src/components/props-table/PropsTable.vue +62 -0
  288. package/src/components/props-table/index.ts +2 -0
  289. package/src/components/radio-group/RadioGroup.vue +52 -0
  290. package/src/components/radio-group/RadioGroupItem.vue +80 -0
  291. package/src/components/radio-group/RadioGroupItemField.vue +128 -0
  292. package/src/components/radio-group/index.ts +21 -0
  293. package/src/components/rating/Rating.vue +70 -0
  294. package/src/components/rating/RatingItem.vue +26 -0
  295. package/src/components/rating/RatingItemIndicator.vue +28 -0
  296. package/src/components/rating/RatingStar.vue +16 -0
  297. package/src/components/rating/index.ts +13 -0
  298. package/src/components/scroll-area/ScrollArea.vue +52 -0
  299. package/src/components/scroll-area/index.ts +1 -0
  300. package/src/components/search-input/SearchInput.vue +46 -0
  301. package/src/components/search-input/index.ts +1 -0
  302. package/src/components/select/Select.vue +15 -0
  303. package/src/components/select/SelectContent.vue +67 -0
  304. package/src/components/select/SelectGroup.vue +20 -0
  305. package/src/components/select/SelectItem.vue +59 -0
  306. package/src/components/select/SelectLabel.vue +20 -0
  307. package/src/components/select/SelectSeparator.vue +18 -0
  308. package/src/components/select/SelectTrigger.vue +101 -0
  309. package/src/components/select/SelectValue.vue +46 -0
  310. package/src/components/select/index.ts +8 -0
  311. package/src/components/separator/Separator.vue +25 -0
  312. package/src/components/separator/index.ts +1 -0
  313. package/src/components/sheet/Sheet.vue +15 -0
  314. package/src/components/sheet/SheetClose.vue +12 -0
  315. package/src/components/sheet/SheetContent.vue +45 -0
  316. package/src/components/sheet/SheetDescription.vue +19 -0
  317. package/src/components/sheet/SheetFooter.vue +12 -0
  318. package/src/components/sheet/SheetHeader.vue +12 -0
  319. package/src/components/sheet/SheetTitle.vue +19 -0
  320. package/src/components/sheet/SheetTrigger.vue +12 -0
  321. package/src/components/sheet/index.ts +10 -0
  322. package/src/components/sidebar/Sidebar.vue +108 -0
  323. package/src/components/sidebar/SidebarContent.vue +19 -0
  324. package/src/components/sidebar/SidebarFooter.vue +19 -0
  325. package/src/components/sidebar/SidebarGroup.vue +19 -0
  326. package/src/components/sidebar/SidebarGroupAction.vue +28 -0
  327. package/src/components/sidebar/SidebarGroupContent.vue +19 -0
  328. package/src/components/sidebar/SidebarGroupLabel.vue +25 -0
  329. package/src/components/sidebar/SidebarHeader.vue +19 -0
  330. package/src/components/sidebar/SidebarInput.vue +15 -0
  331. package/src/components/sidebar/SidebarInset.vue +22 -0
  332. package/src/components/sidebar/SidebarMenu.vue +19 -0
  333. package/src/components/sidebar/SidebarMenuAction.vue +41 -0
  334. package/src/components/sidebar/SidebarMenuBadge.vue +24 -0
  335. package/src/components/sidebar/SidebarMenuButton.vue +94 -0
  336. package/src/components/sidebar/SidebarMenuItem.vue +19 -0
  337. package/src/components/sidebar/SidebarMenuSkeleton.vue +36 -0
  338. package/src/components/sidebar/SidebarMenuSub.vue +22 -0
  339. package/src/components/sidebar/SidebarMenuSubButton.vue +39 -0
  340. package/src/components/sidebar/SidebarMenuSubItem.vue +19 -0
  341. package/src/components/sidebar/SidebarProvider.vue +114 -0
  342. package/src/components/sidebar/SidebarRail.vue +37 -0
  343. package/src/components/sidebar/SidebarSeparator.vue +15 -0
  344. package/src/components/sidebar/SidebarTrigger.vue +27 -0
  345. package/src/components/sidebar/context.ts +39 -0
  346. package/src/components/sidebar/index.ts +34 -0
  347. package/src/components/skeleton/Skeleton.vue +10 -0
  348. package/src/components/skeleton/index.ts +1 -0
  349. package/src/components/skip-link/SkipLink.vue +45 -0
  350. package/src/components/skip-link/index.ts +1 -0
  351. package/src/components/slider/Slider.vue +57 -0
  352. package/src/components/slider/index.ts +3 -0
  353. package/src/components/speed-dial/SpeedDial.vue +362 -0
  354. package/src/components/speed-dial/index.ts +28 -0
  355. package/src/components/spinner/Spinner.vue +20 -0
  356. package/src/components/spinner/index.ts +1 -0
  357. package/src/components/splitter/Splitter.vue +24 -0
  358. package/src/components/splitter/SplitterHandle.vue +51 -0
  359. package/src/components/splitter/SplitterPanel.vue +30 -0
  360. package/src/components/splitter/index.ts +14 -0
  361. package/src/components/stepper/Stepper.vue +34 -0
  362. package/src/components/stepper/StepperItem.vue +113 -0
  363. package/src/components/stepper/index.ts +2 -0
  364. package/src/components/switch/Switch.vue +35 -0
  365. package/src/components/switch/index.ts +1 -0
  366. package/src/components/table/Table.vue +24 -0
  367. package/src/components/table/TableBody.vue +12 -0
  368. package/src/components/table/TableCell.vue +12 -0
  369. package/src/components/table/TableEmpty.vue +20 -0
  370. package/src/components/table/TableHead.vue +17 -0
  371. package/src/components/table/TableHeader.vue +12 -0
  372. package/src/components/table/TableRow.vue +19 -0
  373. package/src/components/table/index.ts +7 -0
  374. package/src/components/tabs/Tabs.vue +23 -0
  375. package/src/components/tabs/TabsContent.vue +23 -0
  376. package/src/components/tabs/TabsList.vue +26 -0
  377. package/src/components/tabs/TabsTrigger.vue +30 -0
  378. package/src/components/tabs/index.ts +4 -0
  379. package/src/components/textarea/Textarea.vue +25 -0
  380. package/src/components/textarea/index.ts +1 -0
  381. package/src/components/theme-controls/LocaleMenu.vue +95 -0
  382. package/src/components/theme-controls/PaletteMenu.vue +155 -0
  383. package/src/components/theme-controls/ThemeToggle.vue +21 -0
  384. package/src/components/theme-controls/index.ts +15 -0
  385. package/src/components/timeline/Timeline.vue +20 -0
  386. package/src/components/timeline/TimelineItem.vue +68 -0
  387. package/src/components/timeline/index.ts +15 -0
  388. package/src/components/toast/Toaster.vue +62 -0
  389. package/src/components/toast/index.ts +4 -0
  390. package/src/components/toast/toast.ts +57 -0
  391. package/src/components/toggle/Toggle.vue +32 -0
  392. package/src/components/toggle/ToggleGroup.vue +56 -0
  393. package/src/components/toggle/ToggleGroupItem.vue +33 -0
  394. package/src/components/toggle/index.ts +18 -0
  395. package/src/components/token-table/TokenTable.vue +67 -0
  396. package/src/components/token-table/index.ts +2 -0
  397. package/src/components/toolbar/Toolbar.vue +34 -0
  398. package/src/components/toolbar/ToolbarButton.vue +33 -0
  399. package/src/components/toolbar/ToolbarSeparator.vue +21 -0
  400. package/src/components/toolbar/index.ts +3 -0
  401. package/src/components/tooltip/Tooltip.vue +15 -0
  402. package/src/components/tooltip/TooltipContent.vue +38 -0
  403. package/src/components/tooltip/TooltipProvider.vue +16 -0
  404. package/src/components/tooltip/TooltipTrigger.vue +12 -0
  405. package/src/components/tooltip/index.ts +4 -0
  406. package/src/components/tree/Tree.vue +53 -0
  407. package/src/components/tree/TreeItem.vue +59 -0
  408. package/src/components/tree/index.ts +21 -0
  409. package/src/components/tree-select/TreeSelect.vue +141 -0
  410. package/src/components/tree-select/index.ts +1 -0
  411. package/src/components/tree-table/TreeTable.vue +302 -0
  412. package/src/components/tree-table/features.ts +49 -0
  413. package/src/components/tree-table/index.ts +3 -0
  414. package/src/components/video-player/VideoPlayer.vue +472 -0
  415. package/src/components/video-player/index.ts +20 -0
  416. package/src/composables/index.ts +15 -0
  417. package/src/composables/useBase.ts +32 -0
  418. package/src/composables/useChartPalette.ts +33 -0
  419. package/src/composables/useColorMode.ts +29 -0
  420. package/src/composables/useConfirm.ts +81 -0
  421. package/src/composables/useDelayedFlag.ts +62 -0
  422. package/src/composables/useI18n.ts +107 -0
  423. package/src/composables/useIsMobile.ts +34 -0
  424. package/src/composables/useMenuAccent.ts +32 -0
  425. package/src/composables/useMenuAppearance.ts +31 -0
  426. package/src/composables/useMenuColor.ts +31 -0
  427. package/src/composables/usePalette.ts +30 -0
  428. package/src/composables/useStyle.ts +35 -0
  429. package/src/composables/useSurface.ts +30 -0
  430. package/src/composables/useTableQuery.ts +73 -0
  431. package/src/composables/useThemeController.ts +48 -0
  432. package/src/i18n/README.md +68 -0
  433. package/src/i18n/controller.ts +233 -0
  434. package/src/i18n/dictionary.ts +43 -0
  435. package/src/i18n/index.ts +34 -0
  436. package/src/i18n/registry.ts +64 -0
  437. package/src/i18n/ui/en/ui.json +114 -0
  438. package/src/i18n/ui/en/zod.json +16 -0
  439. package/src/i18n/ui/index.ts +48 -0
  440. package/src/i18n/ui/vi/ui.json +114 -0
  441. package/src/i18n/ui/vi/zod.json +16 -0
  442. package/src/lib/date.ts +24 -0
  443. package/src/lib/focus.ts +12 -0
  444. package/src/lib/icons.ts +345 -0
  445. package/src/lib/menu-styles.ts +16 -0
  446. package/src/lib/menu.ts +20 -0
  447. package/src/lib/platform.ts +5 -0
  448. package/src/lib/types.ts +6 -0
  449. package/src/lib/utils.ts +9 -0
  450. package/src/lib/zod-messages.ts +127 -0
  451. package/src/styles/chart.css +30 -0
  452. package/src/styles/index.css +18 -0
  453. package/src/variants/README.md +55 -0
  454. package/src/variants/components/accordion.ts +71 -0
  455. package/src/variants/components/alert.ts +20 -0
  456. package/src/variants/components/badge.ts +76 -0
  457. package/src/variants/components/button-group.ts +136 -0
  458. package/src/variants/components/button.ts +160 -0
  459. package/src/variants/components/checkbox.ts +67 -0
  460. package/src/variants/components/chip.ts +51 -0
  461. package/src/variants/components/code-block.ts +22 -0
  462. package/src/variants/components/dialog.ts +28 -0
  463. package/src/variants/components/empty.ts +60 -0
  464. package/src/variants/components/field.ts +35 -0
  465. package/src/variants/components/float-label.ts +95 -0
  466. package/src/variants/components/input-group.ts +73 -0
  467. package/src/variants/components/input-password.ts +68 -0
  468. package/src/variants/components/kbd.ts +47 -0
  469. package/src/variants/components/picker-trigger.ts +30 -0
  470. package/src/variants/components/radio-group.ts +63 -0
  471. package/src/variants/components/sheet.ts +23 -0
  472. package/src/variants/components/sidebar.ts +33 -0
  473. package/src/variants/components/slider.ts +49 -0
  474. package/src/variants/components/timeline.ts +45 -0
  475. package/src/variants/components/toggle.ts +58 -0
  476. package/src/variants/index.ts +35 -0
  477. package/src/variants/lib/chart/polar.ts +139 -0
  478. package/src/variants/lib/chart/tooltip.ts +101 -0
  479. package/src/variants/lib/chart/types.ts +172 -0
  480. package/src/variants/lib/chart/xy-label.ts +42 -0
  481. package/src/variants/lib/focus.ts +66 -0
  482. package/src/variants/lib/form-errors.ts +59 -0
  483. package/src/variants/lib/highlight.ts +548 -0
  484. package/src/variants/lib/list-move.ts +63 -0
  485. package/src/variants/lib/mask.ts +106 -0
  486. package/src/variants/lib/media.ts +56 -0
  487. package/src/variants/lib/menu-styles.ts +71 -0
  488. package/src/variants/lib/platform.ts +68 -0
  489. package/src/variants/lib/tree.ts +119 -0
  490. package/src/variants/lib/upload.ts +58 -0
  491. package/src/variants/lib/utils.ts +11 -0
@@ -0,0 +1,168 @@
1
+ <script setup lang="ts" generic="TItem">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import { computed, ref } from 'vue'
4
+ import { useI18n } from '../../composables/useI18n'
5
+ import { ChevronDown, ChevronsDown, ChevronsUp, ChevronUp } from '../../lib/icons'
6
+ import { cn } from '../../lib/utils'
7
+ import { moveItem, moveSelectionByStep, moveSelectionToEdge } from '../../variants/lib/list-move'
8
+ import Button from '../button/Button.vue'
9
+ import SelectableList from './SelectableList.vue'
10
+
11
+ const props = withDefaults(
12
+ defineProps<ClassProps & {
13
+ itemKey: (item: TItem, index: number) => string | number
14
+ header?: string
15
+ disabled?: boolean
16
+ /** Bật kéo thả. Mấy cái nút vẫn ở đó — xem ghi chú trợ năng. */
17
+ dragging?: boolean
18
+ maxHeight?: string
19
+ emptyMessage?: string
20
+ }>(),
21
+ { disabled: false, dragging: true },
22
+ )
23
+
24
+ const items = defineModel<TItem[]>({ default: () => [] })
25
+ const selection = defineModel<(string | number)[]>('selection', { default: () => [] })
26
+
27
+ const { t } = useI18n()
28
+
29
+ const keys = computed(() => items.value.map((item, index) => props.itemKey(item, index)))
30
+
31
+ /** Thông báo lần dời gần nhất. Xem ghi chú trợ năng ở template. */
32
+ const announcement = ref('')
33
+
34
+ const selectedIndexes = computed(() => {
35
+ const set = new Set<number>()
36
+ items.value.forEach((item, index) => {
37
+ if (selection.value.includes(props.itemKey(item, index)))
38
+ set.add(index)
39
+ })
40
+ return set
41
+ })
42
+
43
+ const canMoveUp = computed(() =>
44
+ selectedIndexes.value.size > 0 && !selectedIndexes.value.has(0),
45
+ )
46
+
47
+ const canMoveDown = computed(() =>
48
+ selectedIndexes.value.size > 0 && !selectedIndexes.value.has(items.value.length - 1),
49
+ )
50
+
51
+ function apply(next: TItem[], indexes: Set<number>): void {
52
+ items.value = next
53
+ selection.value = [...indexes].map(index => props.itemKey(next[index], index))
54
+ announcement.value = t('ui.orderList.moved', { count: indexes.size })
55
+ }
56
+
57
+ function step(direction: -1 | 1): void {
58
+ const indexes = new Set(selectedIndexes.value)
59
+ apply(moveSelectionByStep(items.value, indexes, direction), indexes)
60
+ }
61
+
62
+ function edge(target: 'start' | 'end'): void {
63
+ const indexes = new Set(selectedIndexes.value)
64
+ apply(moveSelectionToEdge(items.value, indexes, target), indexes)
65
+ }
66
+
67
+ function onDrop(payload: { fromListId: string, fromIndex: number, toIndex: number }): void {
68
+ if (payload.fromListId !== 'order')
69
+ return
70
+ // Bỏ chính mục đang kéo ra khỏi phép đếm: thả xuống ngay dưới chỗ cũ của nó
71
+ // thì chỉ số đích đã bị dịch đi một.
72
+ const to = payload.toIndex > payload.fromIndex ? payload.toIndex - 1 : payload.toIndex
73
+ items.value = moveItem(items.value, payload.fromIndex, to)
74
+ announcement.value = t('ui.orderList.moved', { count: 1 })
75
+ }
76
+ </script>
77
+
78
+ <template>
79
+ <div :class="cn('flex flex-col gap-2', props.class)">
80
+ <p v-if="props.header || $slots.header" class="text-sm font-medium">
81
+ <slot name="header">
82
+ {{ props.header }}
83
+ </slot>
84
+ </p>
85
+
86
+ <div class="flex items-start gap-2">
87
+ <!--
88
+ Bốn cái nút KHÔNG phải phương án dự phòng cho việc kéo thả — chúng là
89
+ cơ chế chính, và kéo thả là phần thêm.
90
+
91
+ WCAG 2.5.7 đòi mọi thao tác kéo phải có một đường thay thế chỉ cần một
92
+ cú chạm. Dựng nút trước rồi thêm kéo sau thì thứ tự đó tự đúng; làm
93
+ ngược lại thì phần trợ năng luôn là thứ được ghép vào sau cùng.
94
+ -->
95
+ <div class="flex flex-col gap-1">
96
+ <Button
97
+ type="button"
98
+ variant="outline"
99
+ size="icon-sm"
100
+ :disabled="props.disabled || !canMoveUp"
101
+ :aria-label="t('ui.orderList.toTop')"
102
+ @click="edge('start')"
103
+ >
104
+ <ChevronsUp aria-hidden="true" />
105
+ </Button>
106
+ <Button
107
+ type="button"
108
+ variant="outline"
109
+ size="icon-sm"
110
+ :disabled="props.disabled || !canMoveUp"
111
+ :aria-label="t('ui.orderList.up')"
112
+ @click="step(-1)"
113
+ >
114
+ <ChevronUp aria-hidden="true" />
115
+ </Button>
116
+ <Button
117
+ type="button"
118
+ variant="outline"
119
+ size="icon-sm"
120
+ :disabled="props.disabled || !canMoveDown"
121
+ :aria-label="t('ui.orderList.down')"
122
+ @click="step(1)"
123
+ >
124
+ <ChevronDown aria-hidden="true" />
125
+ </Button>
126
+ <Button
127
+ type="button"
128
+ variant="outline"
129
+ size="icon-sm"
130
+ :disabled="props.disabled || !canMoveDown"
131
+ :aria-label="t('ui.orderList.toBottom')"
132
+ @click="edge('end')"
133
+ >
134
+ <ChevronsDown aria-hidden="true" />
135
+ </Button>
136
+ </div>
137
+
138
+ <SelectableList
139
+ v-model:selection="selection"
140
+ :items="items"
141
+ :keys="keys"
142
+ list-id="order"
143
+ :label="props.header ?? t('ui.orderList.label')"
144
+ :disabled="props.disabled"
145
+ :dragging="props.dragging"
146
+ :max-height="props.maxHeight"
147
+ :empty-message="props.emptyMessage ?? t('ui.table.empty')"
148
+ class="flex-1"
149
+ @move="onDrop"
150
+ >
151
+ <template #item="slotProps">
152
+ <slot name="item" v-bind="slotProps" />
153
+ </template>
154
+ </SelectableList>
155
+ </div>
156
+
157
+ <!--
158
+ Dời một dòng là một thay đổi KHÔNG nhìn thấy được nếu bạn không nhìn.
159
+
160
+ Focus không đi đâu cả, chữ trên nút không đổi, và thứ tự mới thì phải đọc
161
+ lại cả danh sách mới biết. Một vùng live nói "đã dời 2 mục" là chỗ duy
162
+ nhất phản hồi đó tồn tại.
163
+ -->
164
+ <p class="sr-only" role="status" aria-live="polite">
165
+ {{ announcement }}
166
+ </p>
167
+ </div>
168
+ </template>
@@ -0,0 +1,200 @@
1
+ <script setup lang="ts" generic="TItem">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import { ref } from 'vue'
4
+ import { cn } from '../../lib/utils'
5
+
6
+ /**
7
+ * Danh sách chọn được nhiều mục, kéo thả được — phần chung của `OrderList` và
8
+ * `PickList`. KHÔNG export ra ngoài package: nó là bộ phận, không phải sản phẩm.
9
+ *
10
+ * PickList đúng nghĩa là hai cái này cộng mấy nút chuyển, nên nó nằm ở thư mục
11
+ * của `OrderList` — chỗ nó sinh ra — thay vì được nhân bản ở cả hai nơi.
12
+ */
13
+ const props = withDefaults(
14
+ defineProps<ClassProps & {
15
+ items: TItem[]
16
+ /**
17
+ * Khoá của từng mục, tính SẴN bởi bên gọi.
18
+ *
19
+ * Không nhận `itemKey` là một hàm: tham số hàm contravariant, nên một
20
+ * `(item: TItem) => …` không gán được vào một prop khai `(item: unknown) => …`,
21
+ * và suy luận generic của Vue rơi về `unknown` khi component này nằm trong
22
+ * một component generic khác. Cùng cái bẫy đã ghi cho `FormField`.
23
+ *
24
+ * Mà con cũng đâu cần biết khoá được tính thế nào — nó chỉ cần khoá.
25
+ */
26
+ keys: (string | number)[]
27
+ /** Phân biệt danh sách nguồn với danh sách đích lúc thả. */
28
+ listId: string
29
+ /**
30
+ * Tên trợ năng. Đặt tên là `label` chứ KHÔNG phải `ariaLabel`.
31
+ *
32
+ * `:aria-label="…"` trong template không camel-hoá thành prop `ariaLabel`:
33
+ * Vue coi đó là một thuộc tính HTML thật và để nguyên, nên prop vẫn thiếu.
34
+ * Cùng họ với cái bẫy `VisCrosshair` đã ghi ở mục Chart.
35
+ */
36
+ label: string
37
+ disabled?: boolean
38
+ dragging?: boolean
39
+ maxHeight?: string
40
+ emptyMessage?: string
41
+ }>(),
42
+ { disabled: false, dragging: true, maxHeight: '220px' },
43
+ )
44
+
45
+ const emit = defineEmits<{
46
+ move: [payload: { fromListId: string, fromIndex: number, toIndex: number }]
47
+ }>()
48
+
49
+ /** Khoá của các mục đang chọn. Dùng khoá chứ không dùng chỉ số: chỉ số đổi sau mỗi lần dời. */
50
+ const selection = defineModel<(string | number)[]>('selection', { default: () => [] })
51
+
52
+ const activeIndex = ref(0)
53
+ /** Vị trí mà mục đang kéo sẽ rơi vào. `undefined` là không kéo gì. */
54
+ const dropIndex = ref<number>()
55
+
56
+ function isSelected(index: number): boolean {
57
+ return selection.value.includes(props.keys[index])
58
+ }
59
+
60
+ function toggle(index: number, additive: boolean): void {
61
+ if (props.disabled)
62
+ return
63
+ const key = props.keys[index]
64
+ if (!additive) {
65
+ selection.value = isSelected(index) && selection.value.length === 1 ? [] : [key]
66
+ return
67
+ }
68
+ selection.value = isSelected(index)
69
+ ? selection.value.filter(existing => existing !== key)
70
+ : [...selection.value, key]
71
+ }
72
+
73
+ function focusOption(container: HTMLElement, index: number): void {
74
+ activeIndex.value = index
75
+ container.querySelector<HTMLElement>(`[data-option="${index}"]`)?.focus()
76
+ }
77
+
78
+ function onKeydown(event: KeyboardEvent): void {
79
+ const container = event.currentTarget as HTMLElement
80
+ const last = props.items.length - 1
81
+ if (last < 0)
82
+ return
83
+
84
+ switch (event.key) {
85
+ case 'ArrowDown':
86
+ event.preventDefault()
87
+ focusOption(container, Math.min(activeIndex.value + 1, last))
88
+ break
89
+ case 'ArrowUp':
90
+ event.preventDefault()
91
+ focusOption(container, Math.max(activeIndex.value - 1, 0))
92
+ break
93
+ case 'Home':
94
+ event.preventDefault()
95
+ focusOption(container, 0)
96
+ break
97
+ case 'End':
98
+ event.preventDefault()
99
+ focusOption(container, last)
100
+ break
101
+ case ' ':
102
+ event.preventDefault()
103
+ toggle(activeIndex.value, true)
104
+ break
105
+ }
106
+ }
107
+
108
+ const DRAG_TYPE = 'application/x-woodblock-list'
109
+
110
+ function onDragStart(event: DragEvent, index: number): void {
111
+ if (event.dataTransfer === null)
112
+ return
113
+ event.dataTransfer.effectAllowed = 'move'
114
+ event.dataTransfer.setData(DRAG_TYPE, JSON.stringify({ listId: props.listId, index }))
115
+ // Firefox không bắt đầu kéo nếu không có dữ liệu kiểu text/plain.
116
+ event.dataTransfer.setData('text/plain', String(props.keys[index]))
117
+ }
118
+
119
+ /**
120
+ * `preventDefault()` trong `dragover` là BẮT BUỘC.
121
+ *
122
+ * Mặc định mọi phần tử đều từ chối nhận thả, và cách nói "tôi nhận" là huỷ sự
123
+ * kiện này. Thiếu nó thì `drop` không bao giờ bắn — con trỏ hiện dấu cấm, mà
124
+ * không có lỗi nào cả. Đây là cái bẫy số một của kéo thả gốc.
125
+ */
126
+ function onDragOver(event: DragEvent, index: number): void {
127
+ event.preventDefault()
128
+ if (event.dataTransfer !== null)
129
+ event.dataTransfer.dropEffect = 'move'
130
+
131
+ // Nửa dưới của một mục nghĩa là thả xuống SAU nó.
132
+ const box = (event.currentTarget as HTMLElement).getBoundingClientRect()
133
+ dropIndex.value = event.clientY - box.top > box.height / 2 ? index + 1 : index
134
+ }
135
+
136
+ function onDrop(event: DragEvent): void {
137
+ event.preventDefault()
138
+ const raw = event.dataTransfer?.getData(DRAG_TYPE)
139
+ const to = dropIndex.value
140
+ dropIndex.value = undefined
141
+ if (raw === undefined || raw === '' || to === undefined)
142
+ return
143
+
144
+ const payload = JSON.parse(raw) as { listId: string, index: number }
145
+ emit('move', { fromListId: payload.listId, fromIndex: payload.index, toIndex: to })
146
+ }
147
+ </script>
148
+
149
+ <template>
150
+ <!--
151
+ `listbox` với `aria-multiselectable`: các mục là LỰA CHỌN, không phải nút.
152
+ Trạng thái chọn nằm ở `aria-selected` nên nó được đọc ra, thay vì chỉ là một
153
+ mảng màu nổi mà người không nhìn thấy phải tự đoán.
154
+ -->
155
+ <div
156
+ role="listbox"
157
+ aria-multiselectable="true"
158
+ :aria-label="props.label"
159
+ :aria-disabled="props.disabled || undefined"
160
+ :style="{ maxHeight: props.maxHeight }"
161
+ :class="cn(
162
+ 'overflow-y-auto rounded-md border border-border bg-background p-1',
163
+ props.disabled && 'pointer-events-none opacity-50',
164
+ props.class,
165
+ )"
166
+ @keydown="onKeydown"
167
+ @dragover.prevent
168
+ @drop="onDrop"
169
+ >
170
+ <p v-if="props.items.length === 0" class="px-2 py-6 text-center text-sm text-muted-foreground">
171
+ {{ props.emptyMessage }}
172
+ </p>
173
+
174
+ <div
175
+ v-for="(item, index) in props.items"
176
+ :key="props.keys[index]"
177
+ role="option"
178
+ :data-option="index"
179
+ :aria-selected="isSelected(index)"
180
+ :tabindex="index === activeIndex ? 0 : -1"
181
+ :draggable="props.dragging && !props.disabled"
182
+ :class="cn(
183
+ 'flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-none transition-colors',
184
+ 'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring',
185
+ isSelected(index) ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/50',
186
+ // Vạch chỉ chỗ thả — vẽ bằng viền của chính mục, không chèn thêm thẻ,
187
+ // để danh sách không nhảy một dòng mỗi lần con trỏ đi qua.
188
+ dropIndex === index && 'border-t-2 border-t-primary',
189
+ dropIndex === index + 1 && 'border-b-2 border-b-primary',
190
+ )"
191
+ @click="toggle(index, $event.ctrlKey || $event.metaKey)"
192
+ @focus="activeIndex = index"
193
+ @dragstart="onDragStart($event, index)"
194
+ @dragover="onDragOver($event, index)"
195
+ @dragend="dropIndex = undefined"
196
+ >
197
+ <slot name="item" :item="item" :index="index" :selected="isSelected(index)" />
198
+ </div>
199
+ </div>
200
+ </template>
@@ -0,0 +1 @@
1
+ export { default as OrderList } from './OrderList.vue'
@@ -0,0 +1,133 @@
1
+ <script setup lang="ts">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import type { ButtonVariants } from '../button'
4
+ import {
5
+ PaginationEllipsis,
6
+ PaginationFirst,
7
+ PaginationLast,
8
+ PaginationList,
9
+ PaginationListItem,
10
+ PaginationNext,
11
+ PaginationPrev,
12
+ PaginationRoot,
13
+ } from 'reka-ui'
14
+ import { computed } from 'vue'
15
+ import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, MoreHorizontal } from '../../lib/icons'
16
+ import { cn } from '../../lib/utils'
17
+ import { buttonVariants } from '../button'
18
+
19
+ const props = withDefaults(
20
+ defineProps<ClassProps & {
21
+ /** Tổng số bản ghi, không phải số trang. */
22
+ total?: number
23
+ pageSize?: number
24
+ /** Số nút trang hiện mỗi bên trang hiện tại. */
25
+ siblingCount?: number
26
+ /** Thêm nút về đầu và về cuối. */
27
+ showEdges?: boolean
28
+ /** Dòng "1–20 of 137" ở bên trái. */
29
+ showSummary?: boolean
30
+ size?: Extract<ButtonVariants['size'], 'sm' | 'default' | 'lg'>
31
+ disabled?: boolean
32
+ }>(),
33
+ {
34
+ total: 0,
35
+ pageSize: 20,
36
+ siblingCount: 1,
37
+ showEdges: false,
38
+ showSummary: false,
39
+ size: 'default',
40
+ disabled: false,
41
+ },
42
+ )
43
+
44
+ /**
45
+ * Chỉ số trang tính từ **0**, khớp `TableQuery` của DataTable.
46
+ *
47
+ * Reka đánh số trang từ 1. Nếu để nguyên thì mọi chỗ nối Pagination với
48
+ * DataTable đều phải tự cộng trừ 1 — đúng chỗ sinh ra lỗi lệch một đơn vị. Quy
49
+ * đổi gói gọn ở đây, đúng một lần.
50
+ */
51
+ const pageIndex = defineModel<number>('pageIndex', { default: 0 })
52
+
53
+ const page = computed({
54
+ get: () => pageIndex.value + 1,
55
+ set: (value: number) => { pageIndex.value = value - 1 },
56
+ })
57
+
58
+ /** Nút số trang là nút vuông; nút trước/sau cùng cỡ để hàng không gợn. */
59
+ const ICON_SIZE = { sm: 'icon-sm', default: 'icon', lg: 'icon-lg' } as const
60
+ const iconSize = computed(() => ICON_SIZE[props.size])
61
+
62
+ const from = computed(() => (props.total === 0 ? 0 : pageIndex.value * props.pageSize + 1))
63
+ const to = computed(() => Math.min((pageIndex.value + 1) * props.pageSize, props.total))
64
+ </script>
65
+
66
+ <template>
67
+ <PaginationRoot
68
+ v-model:page="page"
69
+ :total="props.total"
70
+ :items-per-page="props.pageSize"
71
+ :sibling-count="props.siblingCount"
72
+ :show-edges="props.showEdges"
73
+ :disabled="props.disabled"
74
+ :class="cn('flex w-full flex-wrap items-center justify-between gap-3', props.class)"
75
+ >
76
+ <p v-if="props.showSummary" class="text-sm text-muted-foreground">
77
+ <template v-if="props.total > 0">
78
+ {{ from }}–{{ to }} of {{ props.total }}
79
+ </template>
80
+ <template v-else>
81
+ No results
82
+ </template>
83
+ </p>
84
+
85
+ <!--
86
+ `flex-wrap` ở đây chứ không chỉ ở gốc: bật `showEdges` là thêm hai nút,
87
+ hàng nút vượt 390px và tràn ra ngoài khung ở màn 375px.
88
+ -->
89
+ <PaginationList v-slot="{ items }" class="flex flex-wrap items-center justify-center gap-1">
90
+ <PaginationFirst v-if="props.showEdges" :class="buttonVariants({ variant: 'outline', size: iconSize })">
91
+ <ChevronsLeft />
92
+ </PaginationFirst>
93
+ <PaginationPrev :class="buttonVariants({ variant: 'outline', size: iconSize })">
94
+ <ChevronLeft />
95
+ </PaginationPrev>
96
+
97
+ <template v-for="(item, index) in items">
98
+ <!--
99
+ Trang hiện tại dùng nút ĐẶC, không phải outline: đây là thông tin "bạn
100
+ đang ở đâu", nó phải đọc ra trước các nút xung quanh chứ không chỉ là
101
+ một nút được chọn.
102
+ -->
103
+ <PaginationListItem
104
+ v-if="item.type === 'page'"
105
+ :key="`page-${item.value}`"
106
+ :value="item.value"
107
+ :class="buttonVariants({
108
+ variant: item.value === page ? 'default' : 'outline',
109
+ size: iconSize,
110
+ })"
111
+ >
112
+ {{ item.value }}
113
+ </PaginationListItem>
114
+
115
+ <!-- Dấu ba chấm KHÔNG bấm được, nên không tô như nút. -->
116
+ <PaginationEllipsis
117
+ v-else
118
+ :key="`ellipsis-${index}`"
119
+ class="grid size-8 place-items-center text-muted-foreground"
120
+ >
121
+ <MoreHorizontal class="size-4" />
122
+ </PaginationEllipsis>
123
+ </template>
124
+
125
+ <PaginationNext :class="buttonVariants({ variant: 'outline', size: iconSize })">
126
+ <ChevronRight />
127
+ </PaginationNext>
128
+ <PaginationLast v-if="props.showEdges" :class="buttonVariants({ variant: 'outline', size: iconSize })">
129
+ <ChevronsRight />
130
+ </PaginationLast>
131
+ </PaginationList>
132
+ </PaginationRoot>
133
+ </template>
@@ -0,0 +1 @@
1
+ export { default as Pagination } from './Pagination.vue'
@@ -0,0 +1,69 @@
1
+ <script setup lang="ts">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from 'reka-ui'
4
+ import { ChevronDown } from '../../lib/icons'
5
+ import { cn } from '../../lib/utils'
6
+
7
+ const props = withDefaults(
8
+ defineProps<ClassProps & {
9
+ title?: string
10
+ /** Cho gập lại. Không bật thì tiêu đề không phải là nút. */
11
+ collapsible?: boolean
12
+ defaultOpen?: boolean
13
+ }>(),
14
+ { collapsible: false, defaultOpen: true },
15
+ )
16
+
17
+ const open = defineModel<boolean>('open')
18
+ </script>
19
+
20
+ <template>
21
+ <!--
22
+ Không gập được thì render `<section>` thường, KHÔNG bọc Collapsible.
23
+ Collapsible luôn dựng một `<button>` cho tiêu đề; tiêu đề bấm vào không làm
24
+ gì là thứ người dùng bàn phím sẽ tab vào rồi mắc kẹt.
25
+ -->
26
+ <CollapsibleRoot
27
+ v-if="props.collapsible"
28
+ v-model:open="open"
29
+ :default-open="props.defaultOpen"
30
+ :class="cn('overflow-hidden rounded-xl border border-border bg-card', props.class)"
31
+ >
32
+ <CollapsibleTrigger
33
+ class="group/panel flex w-full items-center justify-between gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
34
+ >
35
+ <span class="text-sm font-medium">
36
+ <slot name="title">{{ props.title }}</slot>
37
+ </span>
38
+ <span class="flex items-center gap-2">
39
+ <slot name="actions" />
40
+ <ChevronDown
41
+ class="size-4 shrink-0 text-muted-foreground transition-transform duration-(--duration-base) group-data-[state=open]/panel:rotate-180 motion-reduce:transition-none"
42
+ aria-hidden="true"
43
+ />
44
+ </span>
45
+ </CollapsibleTrigger>
46
+ <CollapsibleContent class="overflow-hidden data-[state=open]:animate-collapsible-down data-[state=closed]:animate-collapsible-up">
47
+ <div class="border-t border-border px-4 py-3 text-sm">
48
+ <slot />
49
+ </div>
50
+ </CollapsibleContent>
51
+ </CollapsibleRoot>
52
+
53
+ <section v-else :class="cn('overflow-hidden rounded-xl border border-border bg-card', props.class)">
54
+ <header
55
+ v-if="props.title || $slots.title || $slots.actions"
56
+ class="flex items-center justify-between gap-3 px-4 py-3"
57
+ >
58
+ <h3 class="text-sm font-medium">
59
+ <slot name="title">
60
+ {{ props.title }}
61
+ </slot>
62
+ </h3>
63
+ <slot name="actions" />
64
+ </header>
65
+ <div :class="cn('px-4 py-3 text-sm', (props.title || $slots.title) && 'border-t border-border')">
66
+ <slot />
67
+ </div>
68
+ </section>
69
+ </template>
@@ -0,0 +1 @@
1
+ export { default as Panel } from './Panel.vue'