@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,53 @@
1
+ <script setup lang="ts">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import type { TreeNode } from './index'
4
+ import { TreeRoot } from 'reka-ui'
5
+ import { cn } from '../../lib/utils'
6
+
7
+ const props = withDefaults(
8
+ defineProps<ClassProps & {
9
+ items: TreeNode[]
10
+ multiple?: boolean
11
+ /**
12
+ * Chọn một nút cha thì chọn luôn toàn bộ con.
13
+ *
14
+ * Đúng cho cây phân quyền ("cấp quyền cả nhánh"), sai cho cây thư mục
15
+ * ("mở thư mục" khác "chọn mọi file trong đó"). Nên mặc định tắt.
16
+ */
17
+ propagateSelect?: boolean
18
+ }>(),
19
+ { multiple: false, propagateSelect: false },
20
+ )
21
+
22
+ const model = defineModel<TreeNode | TreeNode[] | undefined>()
23
+ const expanded = defineModel<string[]>('expanded', { default: () => [] })
24
+
25
+ function getKey(item: TreeNode): string {
26
+ return item.id
27
+ }
28
+
29
+ function getChildren(item: TreeNode): TreeNode[] | undefined {
30
+ return item.children
31
+ }
32
+ </script>
33
+
34
+ <template>
35
+ <TreeRoot
36
+ v-slot="{ flattenItems }"
37
+ v-model="model"
38
+ v-model:expanded="expanded"
39
+ :items="props.items"
40
+ :multiple="props.multiple"
41
+ :propagate-select="props.propagateSelect"
42
+ :get-key="getKey"
43
+ :get-children="getChildren"
44
+ :class="cn('flex w-full select-none flex-col gap-0.5', props.class)"
45
+ >
46
+ <!--
47
+ Reka trả về danh sách đã **làm phẳng**: mỗi nút đang thấy là một dòng, kèm
48
+ `level` để thụt lề. Nhờ vậy điều hướng bàn phím chạy trên một danh sách
49
+ tuyến tính thay vì phải đệ quy qua các component lồng nhau.
50
+ -->
51
+ <slot :items="flattenItems" />
52
+ </TreeRoot>
53
+ </template>
@@ -0,0 +1,59 @@
1
+ <script setup lang="ts">
2
+ import type { TreeItemProps } from 'reka-ui'
3
+ import type { ClassProps } from '../../lib/types'
4
+ import type { TreeNode } from './index'
5
+ import { TreeItem } from 'reka-ui'
6
+ import { computed } from 'vue'
7
+ import { ChevronRight } from '../../lib/icons'
8
+ import { cn } from '../../lib/utils'
9
+
10
+ const props = defineProps<TreeItemProps<TreeNode> & ClassProps & {
11
+ /** Nút này có con hay không — quyết định có vẽ mũi tên. */
12
+ hasChildren?: boolean
13
+ }>()
14
+
15
+ const delegated = computed(() => {
16
+ const { class: _class, hasChildren: _hasChildren, ...rest } = props
17
+ return rest
18
+ })
19
+
20
+ /**
21
+ * Thụt lề bằng padding tính ra px, không dùng thang `pl-*` của Tailwind.
22
+ *
23
+ * Cây sâu tuỳ ý; khai sẵn một class cho mỗi cấp là không khả thi, mà
24
+ * `pl-[calc(...)]` với biến thì Tailwind không sinh ra được vì nó cần biết giá
25
+ * trị lúc build.
26
+ */
27
+ const indent = computed(() => ({ paddingLeft: `${(props.level - 1) * 18 + 8}px` }))
28
+ </script>
29
+
30
+ <template>
31
+ <TreeItem
32
+ v-slot="{ isExpanded, isSelected }"
33
+ v-bind="delegated"
34
+ :style="indent"
35
+ :class="cn(
36
+ 'flex h-8 items-center gap-1.5 rounded-md pr-2 text-sm outline-none transition-colors',
37
+ 'focus-visible:ring-2 focus-visible:ring-ring',
38
+ 'hover:bg-accent/50',
39
+ 'data-[selected]:bg-accent data-[selected]:text-accent-foreground',
40
+ 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
41
+ props.class,
42
+ )"
43
+ >
44
+ <!--
45
+ Lá vẫn chừa đúng chỗ của mũi tên, chỉ để trống. Bỏ hẳn thì nhãn của lá
46
+ thụt sang trái so với nhãn của nhánh cùng cấp và cột chữ gãy.
47
+ -->
48
+ <span class="grid size-4 shrink-0 place-items-center text-muted-foreground">
49
+ <ChevronRight
50
+ v-if="props.hasChildren"
51
+ class="size-3.5 transition-transform duration-(--duration-fast) motion-reduce:transition-none"
52
+ :class="isExpanded && 'rotate-90'"
53
+ aria-hidden="true"
54
+ />
55
+ </span>
56
+
57
+ <slot :is-expanded="isExpanded" :is-selected="isSelected" />
58
+ </TreeItem>
59
+ </template>
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Khuôn dữ liệu và hai phép tra nằm ở `src/variants/lib/tree`, KHÔNG khai
3
+ * ở đây.
4
+ *
5
+ * Cùng lý do `lib/mask` chuyển lên đó: bản React nhận đúng hình dạng nút này,
6
+ * và một `TreeNode` khai hai lần là hai lần để chúng trôi khỏi nhau. Re-export
7
+ * để 20 chỗ đang `import type { TreeNode } from '../tree'` không phải sửa.
8
+ */
9
+ export {
10
+ branchIds,
11
+ branchNodes,
12
+ findNode,
13
+ findPath,
14
+ flattenTree,
15
+ isBranch,
16
+ type TreeNode,
17
+ type TreeRow,
18
+ } from '../../variants/lib/tree'
19
+ export { default as Tree } from './Tree.vue'
20
+
21
+ export { default as TreeItem } from './TreeItem.vue'
@@ -0,0 +1,141 @@
1
+ <script setup lang="ts">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import type { TreeNode } from '../tree'
4
+ import { computed, ref } from 'vue'
5
+ import { useI18n } from '../../composables/useI18n'
6
+ import { focusRing, invalidRing } from '../../lib/focus'
7
+ import { ChevronDown } from '../../lib/icons'
8
+ import { cn } from '../../lib/utils'
9
+ import { pickerTriggerVariants } from '../../variants/components/picker-trigger'
10
+ import { Popover, PopoverContent, PopoverTrigger } from '../popover'
11
+ import { Tree, TreeItem } from '../tree'
12
+
13
+ /**
14
+ * Gốc của component là `Popover`, mà `PopoverRoot` không vẽ ra DOM nào — nên
15
+ * thuộc tính rơi tự do KHÔNG có chỗ đáp và biến mất lặng lẽ. `id` là cái mất
16
+ * đau nhất: `<Label for>` trỏ vào một id không tồn tại thì nhãn vẫn hiện ra
17
+ * bình thường và chẳng làm gì cả.
18
+ *
19
+ * Chuyển tay xuống chính cái nút, cùng cách `Input.vue` làm.
20
+ */
21
+ defineOptions({ inheritAttrs: false })
22
+
23
+ const props = withDefaults(
24
+ defineProps<ClassProps & {
25
+ items: TreeNode[]
26
+ multiple?: boolean
27
+ placeholder?: string
28
+ disabled?: boolean
29
+ invalid?: boolean
30
+ size?: 'sm' | 'default'
31
+ /** Chọn nút cha thì chọn luôn cả nhánh. Chuyển thẳng xuống `Tree`. */
32
+ propagateSelect?: boolean
33
+ /** Chiều cao tối đa của bảng trước khi nó cuộn. */
34
+ maxHeight?: string
35
+ }>(),
36
+ {
37
+ multiple: false,
38
+ disabled: false,
39
+ invalid: false,
40
+ size: 'default',
41
+ propagateSelect: false,
42
+ maxHeight: '260px',
43
+ },
44
+ )
45
+
46
+ const model = defineModel<TreeNode | TreeNode[] | undefined>()
47
+ const expanded = defineModel<string[]>('expanded', { default: () => [] })
48
+
49
+ const { t } = useI18n()
50
+
51
+ const open = ref(false)
52
+
53
+ const placeholder = computed(() => props.placeholder ?? t('ui.treeSelect.placeholder'))
54
+
55
+ const selected = computed<TreeNode[]>(() => {
56
+ if (model.value === undefined)
57
+ return []
58
+ return Array.isArray(model.value) ? model.value : [model.value]
59
+ })
60
+
61
+ const label = computed(() =>
62
+ selected.value.length === 0
63
+ ? placeholder.value
64
+ : selected.value.map(node => node.label).join(', '),
65
+ )
66
+
67
+ /**
68
+ * Chế độ một lựa chọn thì chọn xong là đóng; chế độ nhiều thì KHÔNG.
69
+ *
70
+ * Đóng sau mỗi lần tick trong chế độ nhiều nghĩa là mỗi mục phải mở lại bảng
71
+ * một lần — và người ta bật chế độ nhiều chính vì sắp chọn nhiều thứ.
72
+ */
73
+ function onUpdate(value: TreeNode | TreeNode[] | undefined): void {
74
+ model.value = value
75
+ if (!props.multiple && value !== undefined)
76
+ open.value = false
77
+ }
78
+ </script>
79
+
80
+ <template>
81
+ <Popover v-model:open="open">
82
+ <PopoverTrigger as-child>
83
+ <!--
84
+ Nút mang `role="combobox"`, đúng khuôn "combobox chỉ để chọn" của APG.
85
+
86
+ `aria-haspopup` để nguyên `dialog` chứ KHÔNG đổi thành `tree`: reka
87
+ hard-code `role="dialog"` cho `PopoverContentImpl` và không thuộc tính
88
+ nào đè được, nên cái cây thật sự nằm TRONG một dialog. Khai `tree` ở nút
89
+ là tự mâu thuẫn với chính DOM của mình.
90
+ -->
91
+ <button
92
+ v-bind="$attrs"
93
+ type="button"
94
+ role="combobox"
95
+ aria-haspopup="dialog"
96
+ :aria-expanded="open"
97
+ :disabled="props.disabled"
98
+ :aria-invalid="props.invalid || undefined"
99
+ :data-invalid="props.invalid || undefined"
100
+ :data-placeholder="selected.length === 0 ? '' : undefined"
101
+ :class="cn(pickerTriggerVariants({ size: props.size }), focusRing, invalidRing, props.class)"
102
+ >
103
+ <span class="truncate text-left">{{ label }}</span>
104
+ <ChevronDown class="size-4 shrink-0 text-muted-foreground" aria-hidden="true" />
105
+ </button>
106
+ </PopoverTrigger>
107
+
108
+ <!--
109
+ `w-(--reka-popover-trigger-width)` bám đúng bề rộng của nút, giống cách
110
+ Select làm: một bảng rộng hơn hay hẹp hơn ô của nó đọc ra như đang thuộc
111
+ về thứ khác.
112
+ -->
113
+ <PopoverContent
114
+ class="w-(--reka-popover-trigger-width) p-1"
115
+ align="start"
116
+ :style="{ maxHeight: props.maxHeight }"
117
+ >
118
+ <div class="overflow-y-auto" :style="{ maxHeight: `calc(${props.maxHeight} - 0.5rem)` }">
119
+ <Tree
120
+ v-slot="{ items: rows }"
121
+ v-model:expanded="expanded"
122
+ :model-value="model"
123
+ :items="props.items"
124
+ :multiple="props.multiple"
125
+ :propagate-select="props.propagateSelect"
126
+ @update:model-value="onUpdate"
127
+ >
128
+ <TreeItem
129
+ v-for="row in rows"
130
+ :key="row._id"
131
+ v-bind="row.bind"
132
+ :disabled="row.value.disabled"
133
+ :has-children="!!row.value.children?.length"
134
+ >
135
+ {{ row.value.label }}
136
+ </TreeItem>
137
+ </Tree>
138
+ </div>
139
+ </PopoverContent>
140
+ </Popover>
141
+ </template>
@@ -0,0 +1 @@
1
+ export { default as TreeSelect } from './TreeSelect.vue'
@@ -0,0 +1,302 @@
1
+ <script setup lang="ts" generic="TData extends RowData">
2
+ import type { ExpandedState, RowData, Updater } from '@tanstack/vue-table'
3
+ import type { ClassProps } from '../../lib/types'
4
+ import type { DataTableColumnMeta } from '../data-table/features'
5
+ import type { TreeTableColumn } from './features'
6
+ import { FlexRender, useTable } from '@tanstack/vue-table'
7
+ import { computed, ref, toRef } from 'vue'
8
+ import { ChevronRight } from '../../lib/icons'
9
+ import { cn } from '../../lib/utils'
10
+ import Table from '../table/Table.vue'
11
+ import TableBody from '../table/TableBody.vue'
12
+ import TableCell from '../table/TableCell.vue'
13
+ import TableEmpty from '../table/TableEmpty.vue'
14
+ import TableHead from '../table/TableHead.vue'
15
+ import TableHeader from '../table/TableHeader.vue'
16
+ import TableRow from '../table/TableRow.vue'
17
+ import { treeTableFeatures } from './features'
18
+
19
+ const props = withDefaults(
20
+ defineProps<ClassProps & {
21
+ columns: TreeTableColumn<TData>[]
22
+ data: TData[]
23
+ /**
24
+ * Lấy nút con. Mặc định đọc trường `children`.
25
+ *
26
+ * Khai riêng thay vì ép dữ liệu phải có đúng tên đó — cây từ API hay gọi nó
27
+ * là `items`, `nodes`, `subRows`, và đổi tên cả một cây chỉ để hợp một
28
+ * component là cái giá không đáng.
29
+ */
30
+ getSubRows?: (row: TData) => TData[] | undefined
31
+ getRowId?: (row: TData, index: number) => string
32
+ /** Cột mang mũi tên và phần thụt lề. Mặc định là cột đầu tiên. */
33
+ expandColumn?: string
34
+ loading?: boolean
35
+ emptyMessage?: string
36
+ stickyHeader?: boolean
37
+ maxHeight?: string
38
+ }>(),
39
+ { loading: false, stickyHeader: true },
40
+ )
41
+
42
+ const expanded = defineModel<ExpandedState>('expanded', { default: () => ({}) })
43
+
44
+ function defaultSubRows(row: TData): TData[] | undefined {
45
+ const children = (row as { children?: unknown }).children
46
+ return Array.isArray(children) ? (children as TData[]) : undefined
47
+ }
48
+
49
+ const tableState = computed(() => ({ expanded: expanded.value }))
50
+
51
+ function onExpandedChange(updater: Updater<ExpandedState>): void {
52
+ expanded.value = typeof updater === 'function'
53
+ ? (updater as (old: ExpandedState) => ExpandedState)(expanded.value)
54
+ : updater
55
+ }
56
+
57
+ const table = useTable({
58
+ features: treeTableFeatures,
59
+ columns: toRef(props, 'columns'),
60
+ data: toRef(props, 'data'),
61
+ state: tableState,
62
+ getRowId: props.getRowId,
63
+ getSubRows: props.getSubRows ?? defaultSubRows,
64
+ onExpandedChange,
65
+ })
66
+
67
+ /** Dòng đang THẤY được, đã làm phẳng theo trạng thái mở. */
68
+ const rows = computed(() => table.getRowModel().rows)
69
+
70
+ const expanderId = computed(() =>
71
+ props.expandColumn ?? table.getAllLeafColumns()[0]?.id,
72
+ )
73
+
74
+ /**
75
+ * Chỉ ép bề rộng cho cột MÀ NGƯỜI VIẾT khai `size`.
76
+ *
77
+ * Không đọc `column.columnDef.size`: `columnSizingFeature` nhét sẵn `size: 150`
78
+ * vào mọi cột, nên đọc từ đó thì cột nào cũng cứng và không còn cột nào co giãn.
79
+ * Cùng cái bẫy đã ghi cho DataTable.
80
+ */
81
+ const declared = computed(() => {
82
+ const widths = new Map<string, number>()
83
+ const aligns = new Map<string, NonNullable<DataTableColumnMeta['align']>>()
84
+
85
+ for (const column of props.columns) {
86
+ const id = column.id ?? ('accessorKey' in column ? String(column.accessorKey) : undefined)
87
+ if (id === undefined)
88
+ continue
89
+ if (typeof column.size === 'number')
90
+ widths.set(id, column.size)
91
+ if (column.meta?.align !== undefined)
92
+ aligns.set(id, column.meta.align)
93
+ }
94
+
95
+ return { widths, aligns }
96
+ })
97
+
98
+ function widthStyle(columnId: string): { width: string } | undefined {
99
+ const width = declared.value.widths.get(columnId)
100
+ return width === undefined ? undefined : { width: `${width}px` }
101
+ }
102
+
103
+ /** Cột số căn phải VÀ tabular figures — xem chú thích ở `DataTableColumnMeta`. */
104
+ function alignClass(columnId: string): string | undefined {
105
+ const align = declared.value.aligns.get(columnId)
106
+ if (align === 'number')
107
+ return 'text-right tabular-nums'
108
+ if (align === 'right')
109
+ return 'text-right'
110
+ if (align === 'center')
111
+ return 'text-center'
112
+ return undefined
113
+ }
114
+
115
+ /**
116
+ * Roving tabindex chạy trên DÒNG, không chạy trên ô.
117
+ *
118
+ * APG cho phép cả hai với `treegrid`; ở đây chọn dòng vì thứ người dùng đang
119
+ * duyệt là một cây, còn các cột chỉ là thuộc tính của nút. Điều hướng tới từng
120
+ * ô sẽ là một lời hứa khác hẳn — và nếu khai `treegrid` rồi chỉ làm một nửa thì
121
+ * lời hứa đó là lời hứa sai.
122
+ */
123
+ const focusedId = ref<string>()
124
+
125
+ const activeId = computed(() => {
126
+ const list = rows.value
127
+ if (list.length === 0)
128
+ return undefined
129
+ return list.some(row => row.id === focusedId.value) ? focusedId.value : list[0].id
130
+ })
131
+
132
+ /**
133
+ * Vùng chứa lấy từ CHÍNH sự kiện, không lấy từ một template ref.
134
+ *
135
+ * `ref` đặt trên `<TableBody>` trả về instance component chứ không phải
136
+ * element, nên `querySelector` trên đó ném lỗi — mà `vue-tsc` không bắt được
137
+ * vì ref được khai kiểu `HTMLElement`. Còn `currentTarget` của `keydown` thì
138
+ * đúng là cái `<tbody>` thật.
139
+ */
140
+ function focusRow(container: HTMLElement, id: string | undefined): void {
141
+ if (id === undefined)
142
+ return
143
+ focusedId.value = id
144
+ container.querySelector<HTMLElement>(`[data-row="${CSS.escape(id)}"]`)?.focus()
145
+ }
146
+
147
+ function onKeydown(event: KeyboardEvent): void {
148
+ const container = event.currentTarget as HTMLElement
149
+ const list = rows.value
150
+ const at = list.findIndex(row => row.id === activeId.value)
151
+ if (at < 0)
152
+ return
153
+ const row = list[at]
154
+
155
+ switch (event.key) {
156
+ case 'ArrowDown':
157
+ event.preventDefault()
158
+ focusRow(container, list[Math.min(at + 1, list.length - 1)]?.id)
159
+ break
160
+
161
+ case 'ArrowUp':
162
+ event.preventDefault()
163
+ focusRow(container, list[Math.max(at - 1, 0)]?.id)
164
+ break
165
+
166
+ case 'ArrowRight':
167
+ event.preventDefault()
168
+ // Đóng thì mở ra; đã mở rồi thì đi vào con đầu tiên. Hai nhịp cho hai
169
+ // việc khác nhau, đúng như cây thư mục.
170
+ if (row.getCanExpand() && !row.getIsExpanded())
171
+ row.toggleExpanded(true)
172
+ else if (row.getIsExpanded())
173
+ focusRow(container, list[at + 1]?.id)
174
+ break
175
+
176
+ case 'ArrowLeft':
177
+ event.preventDefault()
178
+ if (row.getIsExpanded()) {
179
+ row.toggleExpanded(false)
180
+ }
181
+ else {
182
+ // Lên nút cha: dòng gần nhất phía trên có độ sâu nhỏ hơn.
183
+ for (let i = at - 1; i >= 0; i -= 1) {
184
+ if (list[i].depth < row.depth) {
185
+ focusRow(container, list[i].id)
186
+ break
187
+ }
188
+ }
189
+ }
190
+ break
191
+
192
+ case 'Home':
193
+ event.preventDefault()
194
+ focusRow(container, list[0]?.id)
195
+ break
196
+
197
+ case 'End':
198
+ event.preventDefault()
199
+ focusRow(container, list[list.length - 1]?.id)
200
+ break
201
+
202
+ case 'Enter':
203
+ case ' ':
204
+ if (row.getCanExpand()) {
205
+ event.preventDefault()
206
+ row.toggleExpanded()
207
+ }
208
+ break
209
+ }
210
+ }
211
+ </script>
212
+
213
+ <template>
214
+ <!--
215
+ `role="treegrid"` đặt lên chính `<table>`.
216
+
217
+ Không phải đặt thêm role cho `<tr>` và `<td>`: HTML-AAM nói `<td>` ánh xạ
218
+ thành `gridcell` khi bảng tổ tiên là `grid` hoặc `treegrid`, và thành `cell`
219
+ khi là `table`. Đổi đúng một thuộc tính ở gốc là cả bảng đổi theo.
220
+ -->
221
+ <Table
222
+ role="treegrid"
223
+ :aria-busy="props.loading || undefined"
224
+ :class="cn(props.class)"
225
+ :container-class="props.maxHeight ? 'overflow-auto' : undefined"
226
+ :style="props.maxHeight ? { maxHeight: props.maxHeight } : undefined"
227
+ >
228
+ <TableHeader>
229
+ <TableRow v-for="group in table.getHeaderGroups()" :key="group.id">
230
+ <TableHead
231
+ v-for="header in group.headers"
232
+ :key="header.id"
233
+ :style="widthStyle(header.column.id)"
234
+ :class="cn(props.stickyHeader && 'sticky top-0 z-(--z-raised) bg-background', alignClass(header.column.id))"
235
+ >
236
+ <FlexRender
237
+ v-if="!header.isPlaceholder"
238
+ :render="header.column.columnDef.header"
239
+ :props="header.getContext()"
240
+ />
241
+ </TableHead>
242
+ </TableRow>
243
+ </TableHeader>
244
+
245
+ <TableBody @keydown="onKeydown">
246
+ <!-- `TableEmpty` không có slot: nội dung con bị bỏ im lặng, phải đi qua prop `message`. -->
247
+ <TableEmpty v-if="rows.length === 0" :colspan="props.columns.length" :message="props.emptyMessage" />
248
+
249
+ <TableRow
250
+ v-for="row in rows"
251
+ v-else
252
+ :key="row.id"
253
+ :data-row="row.id"
254
+ :aria-level="row.depth + 1"
255
+ :aria-expanded="row.getCanExpand() ? row.getIsExpanded() : undefined"
256
+ :tabindex="row.id === activeId ? 0 : -1"
257
+ class="outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
258
+ @focus="focusedId = row.id"
259
+ >
260
+ <TableCell
261
+ v-for="cell in row.getAllCells()"
262
+ :key="cell.id"
263
+ :class="alignClass(cell.column.id)"
264
+ >
265
+ <div
266
+ v-if="cell.column.id === expanderId"
267
+ class="flex items-center gap-1"
268
+ :style="{ paddingLeft: `${row.depth * 18}px` }"
269
+ >
270
+ <!--
271
+ Mũi tên là `tabindex="-1"`: chuỗi Tab đi qua DÒNG, không qua từng
272
+ nút gập. Một cây trăm nút mà mỗi nút là một điểm dừng Tab thì bàn
273
+ phím không đi qua nổi.
274
+ -->
275
+ <button
276
+ v-if="row.getCanExpand()"
277
+ type="button"
278
+ tabindex="-1"
279
+ aria-hidden="true"
280
+ class="grid size-4 shrink-0 place-items-center rounded text-muted-foreground hover:text-foreground"
281
+ @click.stop="row.toggleExpanded()"
282
+ >
283
+ <ChevronRight
284
+ class="size-3.5 transition-transform duration-(--duration-fast) motion-reduce:transition-none"
285
+ :class="row.getIsExpanded() && 'rotate-90'"
286
+ />
287
+ </button>
288
+ <span v-else class="size-4 shrink-0" />
289
+
290
+ <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
291
+ </div>
292
+
293
+ <FlexRender
294
+ v-else
295
+ :render="cell.column.columnDef.cell"
296
+ :props="cell.getContext()"
297
+ />
298
+ </TableCell>
299
+ </TableRow>
300
+ </TableBody>
301
+ </Table>
302
+ </template>
@@ -0,0 +1,49 @@
1
+ import type { CellData, ColumnDef, RowData } from '@tanstack/vue-table'
2
+ import type { DataTableColumnMeta } from '../data-table/features'
3
+ import {
4
+ columnSizingFeature,
5
+ createExpandedRowModel,
6
+ createSortedRowModel,
7
+ rowExpandingFeature,
8
+ rowSelectionFeature,
9
+ rowSortingFeature,
10
+ sortFn_alphanumeric,
11
+ sortFn_basic,
12
+ sortFn_datetime,
13
+ sortFn_text,
14
+ tableFeatures,
15
+ } from '@tanstack/vue-table'
16
+
17
+ /**
18
+ * Bộ feature riêng của TreeTable — KHÔNG dùng lại bộ của DataTable.
19
+ *
20
+ * v9 không bật sẵn feature nào, và bộ của DataTable cố tình không đăng ký
21
+ * expanding để bundle của nó khỏi kéo theo. Ngược lại ở đây không có phân trang:
22
+ * cắt một cái cây theo trang thì một nút cha nằm cuối trang 1 còn con nó ở đầu
23
+ * trang 2, và cái thụt lề bên trái thành ra vô nghĩa.
24
+ *
25
+ * `columnMeta` dùng chung `DataTableColumnMeta` — cùng một chuyện căn chữ, khai
26
+ * hai lần là hai chỗ để chúng trôi khỏi nhau.
27
+ */
28
+ export const treeTableFeatures = tableFeatures({
29
+ columnSizingFeature,
30
+ rowExpandingFeature,
31
+ expandedRowModel: createExpandedRowModel(),
32
+ rowSortingFeature,
33
+ sortedRowModel: createSortedRowModel(),
34
+ sortFns: {
35
+ alphanumeric: sortFn_alphanumeric,
36
+ text: sortFn_text,
37
+ datetime: sortFn_datetime,
38
+ basic: sortFn_basic,
39
+ },
40
+ rowSelectionFeature,
41
+
42
+ columnMeta: {} as DataTableColumnMeta,
43
+ })
44
+
45
+ export type TreeTableFeatures = typeof treeTableFeatures
46
+
47
+ /** Column def đã gắn sẵn feature set — dùng ở app thay cho `ColumnDef` thô. */
48
+ export type TreeTableColumn<TData extends RowData, TValue extends CellData = CellData>
49
+ = ColumnDef<TreeTableFeatures, TData, TValue>
@@ -0,0 +1,3 @@
1
+ export type { TreeTableColumn, TreeTableFeatures } from './features'
2
+ export { treeTableFeatures } from './features'
3
+ export { default as TreeTable } from './TreeTable.vue'