@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,389 @@
1
+ <script setup lang="ts" generic="T extends Record<string, unknown>">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import type { ChartBaseProps, ChartLabelKey } from './types'
4
+ import { GroupedBar, StackedBar } from '@unovis/ts'
5
+ import { VisAxis, VisGroupedBar, VisStackedBar, VisXYContainer, VisXYLabels } from '@unovis/vue'
6
+ import { computed, ref } from 'vue'
7
+ import { cn } from '../../lib/utils'
8
+ import ChartContainer from './ChartContainer.vue'
9
+ import ChartCrosshair from './ChartCrosshair.vue'
10
+ import ChartLegend from './ChartLegend.vue'
11
+ import { useChartSeries } from './useChartSeries'
12
+ import { xyLabelHtml } from './xy-label'
13
+
14
+ const props = withDefaults(defineProps<ClassProps & ChartBaseProps<T> & {
15
+ /** Xếp chồng thay vì đặt cạnh nhau. @default false */
16
+ stacked?: boolean
17
+ /** Bo góc trên của cột, tính bằng pixel. @default 4 */
18
+ radius?: number
19
+ /** Chặn bề rộng tối đa của một nhóm cột. */
20
+ barMaxWidth?: number
21
+ /** Cột nằm ngang: hạng mục chạy theo trục tung. @default false */
22
+ horizontal?: boolean
23
+ /**
24
+ * Tô mỗi CỘT một màu theo thứ tự hạng mục, thay vì mỗi CHUỖI một màu.
25
+ * Chỉ có nghĩa khi vẽ đúng một chuỗi — khoá tra màu khi đó là giá trị của
26
+ * `index`, giống cách PieChart lấy màu.
27
+ * @default false
28
+ */
29
+ colorByIndex?: boolean
30
+ /**
31
+ * Chỉ số cột được đánh dấu là đang chọn.
32
+ *
33
+ * Đánh dấu bằng **viền đứt nét** màu của chính cột đó, cộng nền còn 80% —
34
+ * đúng cách shadcn làm (`chart-bar-active`). Các cột khác giữ NGUYÊN.
35
+ *
36
+ * Bản trước làm ngược: để cột được chọn nguyên vẹn rồi pha loãng mọi cột khác
37
+ * xuống 28%. Nhìn ra là "năm cột bị mờ" chứ không ra "một cột được chọn", và
38
+ * ở biểu đồ nhiều cột thì phần lớn dữ liệu tự dưng khó đọc.
39
+ */
40
+ activeIndex?: number
41
+ /**
42
+ * In nhãn ở đầu cột — trên đỉnh khi cột đứng, ngoài mũi cột khi cột nằm ngang.
43
+ * @default false
44
+ */
45
+ showLabels?: boolean
46
+ /** Nhãn đó in giá trị hay tên hạng mục. @default 'value' */
47
+ labelKey?: ChartLabelKey
48
+ /**
49
+ * Thêm nhãn TÊN hạng mục nằm trong lòng cột, sát gốc.
50
+ *
51
+ * Chỉ có nghĩa với `horizontal`: đó là khuôn "bar list" của shadcn — tên nằm
52
+ * trong cột, số nằm ngoài mũi cột, và cả hai trục đều ẩn. Cột đứng thì trong
53
+ * lòng cột không đủ bề ngang cho một cái tên.
54
+ * @default false
55
+ */
56
+ showIndexLabels?: boolean
57
+ /** Bấm vào legend để ẩn/hiện chuỗi. @default false */
58
+ interactiveLegend?: boolean
59
+ /**
60
+ * Thời lượng animation vào trang, mili giây. Mặc định **0**: 68 biểu đồ cùng
61
+ * mount một lúc, animation từ 0 làm cả trang trông như cột bị nén dí xuống
62
+ * đáy trong ~0,7 giây đầu — đã đo, và đã khiến chính tôi lẫn người dùng tưởng
63
+ * là lỗi hình học. Đặt 400–600 nếu muốn hiệu ứng ở màn dashboard chỉ có vài
64
+ * biểu đồ.
65
+ * @default 0
66
+ */
67
+ duration?: number
68
+ }>(), {
69
+ duration: 0,
70
+ stacked: false,
71
+ radius: 4,
72
+ horizontal: false,
73
+ colorByIndex: false,
74
+ showLabels: false,
75
+ labelKey: 'value',
76
+ showIndexLabels: false,
77
+ showXAxis: true,
78
+ showYAxis: true,
79
+ showTooltip: true,
80
+ showLegend: false,
81
+ showGridLine: true,
82
+ showCursor: true,
83
+ interactiveLegend: false,
84
+ margin: () => ({ top: 8, right: 8, bottom: 0, left: 0 }),
85
+ })
86
+
87
+ const hidden = ref<string[]>([])
88
+
89
+ // Bar KHÔNG dùng phần xếp chồng tính sẵn của `useChartSeries`: `VisStackedBar`
90
+ // tự chồng khi nhận mảng accessor `y`, mà cột thì không cần gradient riêng cho
91
+ // từng lớp nên cũng không có lý do gì phải tách ra.
92
+ const { categories, plotData, xTicks, isMounted } = useChartSeries(props)
93
+
94
+ /** Chuỗi đang hiện — chuỗi bị legend tắt bị bỏ hẳn ra để cột co lại đúng chỗ. */
95
+ const visible = computed(() => categories.value.filter(key => !hidden.value.includes(key)))
96
+
97
+ const yAccessors = computed(() => visible.value.map(
98
+ key => (d: Record<string, unknown>): number => Number(d[key]) || 0,
99
+ ))
100
+
101
+ const colors = computed(() => visible.value.map(key => `var(--color-${key})`))
102
+
103
+ function colorOf(key: string): string {
104
+ return `var(--color-${key})`
105
+ }
106
+
107
+ function labelOf(key: string): string {
108
+ return props.config[key]?.label ?? key
109
+ }
110
+
111
+ /**
112
+ * Mặc định Unovis gọi accessor màu với chỉ số LỚP (chuỗi thứ mấy).
113
+ * `colorByIndex` đổi nghĩa sang chỉ số HÀNG: mỗi cột một màu, dùng cho biểu đồ
114
+ * một chuỗi mà từng hạng mục cần màu riêng.
115
+ */
116
+ function barColor(d: Record<string, unknown>, i: number): string {
117
+ if (props.colorByIndex)
118
+ return `var(--color-${String(d?.[props.index] ?? '')})`
119
+ return colors.value[i] ?? 'transparent'
120
+ }
121
+
122
+ /**
123
+ * Gắn móc `data-active-bar` lên cột đang chọn để CSS bắt được.
124
+ *
125
+ * `attributes` của Unovis chỉ gọi `selection.attr()`, mà thuộc tính trình bày
126
+ * thì yếu hơn mọi rule CSS — nên viền phải khai bằng CSS (xem `index.css`), ở
127
+ * đây chỉ đánh dấu.
128
+ *
129
+ * Bars của Unovis được bind vào chính HÀNG dữ liệu, nên đọc `__i` là ra chỉ số
130
+ * hàng. Tham số thứ hai là chỉ số CHUỖI trong nhóm, không dùng được.
131
+ *
132
+ * Trả về chuỗi rỗng thay vì `null` cho cột không được chọn: selector khoá theo
133
+ * `="true"` nên thuộc tính rỗng vô hại, mà kiểu của `StringAccessor` thì không
134
+ * nhận `null`.
135
+ */
136
+ const barAttributes = computed(() => {
137
+ const selector = props.stacked ? StackedBar.selectors.bar : GroupedBar.selectors.bar
138
+ return {
139
+ [selector]: {
140
+ 'data-active-bar': (d: Record<string, unknown>): string =>
141
+ props.activeIndex !== undefined && Number(d.__i) === props.activeIndex ? 'true' : '',
142
+ },
143
+ }
144
+ })
145
+
146
+ /**
147
+ * Vị trí của một hàng trên trục hạng mục.
148
+ *
149
+ * Cột NGANG phải ĐẢO: thang trục tung của Unovis chạy từ dưới lên, nên hàng đầu
150
+ * tiên rơi xuống đáy và bảng đọc ngược hẳn so với dữ liệu — shadcn (và mọi bảng
151
+ * ngang khác) xếp hàng đầu ở TRÊN CÙNG. Đảo bằng chỉ số chứ không đảo domain:
152
+ * đảo domain thì bề dày cột tính ra âm.
153
+ *
154
+ * Mọi chỗ đọc vị trí hàng đều phải đi qua đây — cột, nhãn, và cả nhãn trục.
155
+ */
156
+ function rowPos(i: number): number {
157
+ return props.horizontal ? props.data.length - 1 - i : i
158
+ }
159
+
160
+ /** Chuỗi được gắn nhãn. Chỉ chuỗi ĐẦU — chồng nhãn lên cột nhóm là không đọc được. */
161
+ const labelled = computed(() => visible.value[0] ?? '')
162
+
163
+ /**
164
+ * Màu viền của cột đang chọn — chính màu của cột đó, đúng cách shadcn lấy
165
+ * `props.payload.fill`. Phải tính ở đây rồi truyền xuống qua biến CSS, vì CSS
166
+ * không có cách nào đọc `fill` của một element để dùng lại cho `stroke`.
167
+ */
168
+ const activeBarColor = computed(() => {
169
+ if (props.activeIndex === undefined)
170
+ return undefined
171
+
172
+ const row = props.data[props.activeIndex]
173
+ if (row === undefined)
174
+ return undefined
175
+
176
+ const key = props.colorByIndex ? String(row[props.index] ?? '') : labelled.value
177
+ return key ? `var(--color-${key})` : undefined
178
+ })
179
+
180
+ /** Tên hạng mục của một hàng, tra qua config trước. */
181
+ function indexTextOf(d: Record<string, unknown>): string {
182
+ const raw = String(d[props.index] ?? '')
183
+ return props.config[raw]?.label ?? raw
184
+ }
185
+
186
+ function valueTextOf(d: Record<string, unknown>): string {
187
+ const key = labelled.value
188
+ const raw = Number(d[key]) || 0
189
+ return props.valueFormatter ? props.valueFormatter(raw, key) : String(raw)
190
+ }
191
+
192
+ /**
193
+ * Vị trí neo nhãn trên trục trị số.
194
+ *
195
+ * Kẹp về phía vạch KHÔNG khi giá trị âm. Recharts đặt `position="top"` ở mép
196
+ * TRÊN của hình chữ nhật, mà cột âm chạy từ vạch 0 xuống dưới nên mép trên của
197
+ * nó chính là vạch 0 — nhãn nằm ngay trên vạch, không rơi xuống đáy cột.
198
+ *
199
+ * Không đụng gì tới cột dương: `max(v, 0)` bằng đúng `v`.
200
+ */
201
+ function labelAnchor(d: Record<string, unknown>): number {
202
+ return Math.max(Number(d[labelled.value]) || 0, 0)
203
+ }
204
+
205
+ /**
206
+ * Nhãn ở đầu cột. Lệch theo PIXEL, nên phải đi qua `xyLabelHtml` — xem ghi chú
207
+ * ở `xy-label.ts` về việc `VisXYLabels` không có tuỳ chọn lệch nào.
208
+ *
209
+ * Cột ngang thì nhãn ra ngoài mũi cột, cột đứng thì lên trên đỉnh — 8px và
210
+ * 12px, đúng hai con số shadcn dùng cho `position="right"` và `position="top"`.
211
+ *
212
+ * Nhưng `offset` của Recharts đo MÉP CHỮ tới mép cột, còn `dy` ở đây đo TÂM
213
+ * chữ, nên chiều dọc phải cộng thêm nửa chiều cao chữ. Chiều ngang thì không:
214
+ * căn trái nên mép trái chữ rơi đúng vào `dx`.
215
+ */
216
+ const LABEL_GAP_Y = 12 + 12 / 2
217
+ function labelAt(d: Record<string, unknown>): string {
218
+ if (!labelled.value)
219
+ return ''
220
+ const text = props.labelKey === 'name' ? indexTextOf(d) : valueTextOf(d)
221
+ return props.horizontal
222
+ ? xyLabelHtml(text, { dx: 8, anchor: 'start' })
223
+ : xyLabelHtml(text, { dy: -LABEL_GAP_Y })
224
+ }
225
+
226
+ /** Nhãn tên nằm trong lòng cột, cách gốc 8px. */
227
+ function indexLabelAt(d: Record<string, unknown>): string {
228
+ return xyLabelHtml(indexTextOf(d), { dx: 8, anchor: 'start' })
229
+ }
230
+
231
+ /**
232
+ * Chữ nằm ĐÈ lên màu cột nên phải lấy màu chữ dẫn xuất của chính chuỗi đó, chứ
233
+ * không phải `--foreground`. shadcn khai cứng `var(--background)`; ở đây
234
+ * `--color-<key>-foreground` tự chọn đen hay trắng theo độ sáng của màu nền —
235
+ * cần thiết vì thang màu chuỗi cố ý trải rộng độ sáng.
236
+ */
237
+ const indexLabelColor = computed(() => `var(--color-${labelled.value}-foreground)`)
238
+
239
+ /**
240
+ * Kẻ lưới KHÔNG đi kèm việc có hiện trục hay không.
241
+ *
242
+ * Unovis chỉ vẽ lưới qua `VisAxis`, nên bỏ trục là mất luôn lưới — trong khi
243
+ * `CartesianGrid` của Recharts là component độc lập, và gần như mọi biểu đồ
244
+ * trong gallery shadcn đều có lưới mà KHÔNG có trục tung. Cách xử lý: vẫn dựng
245
+ * trục đó nhưng cho nhãn rỗng. Bleed của Unovis tính theo bề rộng nhãn thật nên
246
+ * nhãn rỗng gần như không chiếm chỗ.
247
+ *
248
+ * Trục mang lưới: cột đứng thì trục tung, cột ngang thì trục hoành.
249
+ */
250
+ const showX = computed(() => props.showXAxis || (props.showGridLine && props.horizontal))
251
+ const showY = computed(() => props.showYAxis || (props.showGridLine && !props.horizontal))
252
+
253
+ function formatX(tick: number | Date, i: number, ticks: number[] | Date[]): string {
254
+ // `tick` là VỊ TRÍ trên trục, phải đổi ngược về chỉ số hàng trước khi tra.
255
+ const row = rowPos(Number(tick))
256
+ if (props.xFormatter)
257
+ return props.xFormatter(row, i, ticks)
258
+ return String(props.data[row]?.[props.index] ?? '')
259
+ }
260
+
261
+ /** Nhãn rỗng cho trục chỉ tồn tại để mang kẻ lưới. */
262
+ function blankTick(): string {
263
+ return ''
264
+ }
265
+
266
+ function toggle(key: string): void {
267
+ hidden.value = hidden.value.includes(key)
268
+ ? hidden.value.filter(k => k !== key)
269
+ : [...hidden.value, key]
270
+ }
271
+ </script>
272
+
273
+ <template>
274
+ <ChartContainer
275
+ :config="props.config"
276
+ :show-cursor="props.showCursor"
277
+ :class="cn(props.class)"
278
+ :style="activeBarColor ? { '--chart-active-bar': activeBarColor } : undefined"
279
+ >
280
+ <VisXYContainer
281
+ :data="plotData"
282
+ :margin="props.margin"
283
+ :duration="props.duration"
284
+ :style="{ height: isMounted ? '100%' : 'auto' }"
285
+ >
286
+ <VisStackedBar
287
+ v-if="props.stacked"
288
+ :x="(_d: Record<string, unknown>, i: number) => rowPos(i)"
289
+ :y="yAccessors"
290
+ :color="barColor"
291
+ :rounded-corners="props.radius"
292
+ :bar-max-width="props.barMaxWidth"
293
+ :bar-padding="0.2"
294
+ :orientation="props.horizontal ? 'horizontal' : 'vertical'"
295
+ :attributes="barAttributes"
296
+ />
297
+ <VisGroupedBar
298
+ v-else
299
+ :x="(_d: Record<string, unknown>, i: number) => rowPos(i)"
300
+ :y="yAccessors"
301
+ :color="barColor"
302
+ :rounded-corners="props.radius"
303
+ :group-max-width="props.barMaxWidth"
304
+ :group-padding="0.2"
305
+ :orientation="props.horizontal ? 'horizontal' : 'vertical'"
306
+ :attributes="barAttributes"
307
+ />
308
+
309
+ <!--
310
+ Cột ngang thì HAI TRỤC ĐỔI VAI, nên accessor của nhãn cũng phải đổi theo:
311
+ trị số chạy trên trục hoành, chỉ số hàng trên trục tung. Giữ nguyên cách
312
+ khai sẽ ra nhãn nằm rải rác giữa khung, không dính vào cột nào.
313
+ -->
314
+ <VisXYLabels
315
+ v-if="props.showLabels"
316
+ :x="props.horizontal
317
+ ? labelAnchor
318
+ : (d: Record<string, unknown>) => rowPos(Number(d.__i))"
319
+ :y="props.horizontal
320
+ ? (d: Record<string, unknown>) => rowPos(Number(d.__i))
321
+ : labelAnchor"
322
+ :label="labelAt"
323
+ :label-font-size="12"
324
+ :clustering="false"
325
+ background-color="none"
326
+ color="var(--foreground)"
327
+ />
328
+
329
+ <!-- Neo ở giá trị 0, tức mép gốc của cột — không phải mép trái khung vẽ. -->
330
+ <VisXYLabels
331
+ v-if="props.showIndexLabels && props.horizontal"
332
+ :x="() => 0"
333
+ :y="(d: Record<string, unknown>) => rowPos(Number(d.__i))"
334
+ :label="indexLabelAt"
335
+ :label-font-size="12"
336
+ :clustering="false"
337
+ background-color="none"
338
+ :color="indexLabelColor"
339
+ />
340
+
341
+ <ChartCrosshair
342
+ v-if="props.showTooltip"
343
+ :index="props.index"
344
+ :categories="categories as Extract<keyof T, string>[]"
345
+ :hidden="hidden"
346
+ :color-of="colorOf"
347
+ :label-of="labelOf"
348
+ :value-formatter="props.valueFormatter"
349
+ :options="props.tooltip"
350
+ :default-index="props.defaultIndex"
351
+ :show-cursor="props.showCursor"
352
+ />
353
+
354
+ <!--
355
+ Nằm ngang thì HAI TRỤC ĐỔI VAI: hạng mục chạy theo trục tung, còn trị số
356
+ theo trục hoành. Giữ nguyên cách khai sẽ ra nhãn tháng nằm dưới trong khi
357
+ cột lại kéo ngang — nhìn là biết sai ngay.
358
+ -->
359
+ <VisAxis
360
+ v-if="showX"
361
+ type="x"
362
+ :tick-values="props.horizontal ? undefined : xTicks"
363
+ :tick-format="props.showXAxis ? (props.horizontal ? props.yFormatter : formatX) : blankTick"
364
+ :grid-line="props.horizontal ? props.showGridLine : false"
365
+ :tick-line="false"
366
+ :domain-line="false"
367
+ />
368
+ <VisAxis
369
+ v-if="showY"
370
+ type="y"
371
+ :tick-values="props.horizontal ? xTicks : undefined"
372
+ :tick-format="props.showYAxis ? (props.horizontal ? formatX : props.yFormatter) : blankTick"
373
+ :grid-line="props.horizontal ? false : props.showGridLine"
374
+ :tick-line="false"
375
+ :domain-line="false"
376
+ />
377
+
378
+ <slot />
379
+ </VisXYContainer>
380
+
381
+ <ChartLegend
382
+ v-if="props.showLegend"
383
+ :categories="categories"
384
+ :hidden="hidden"
385
+ :interactive="props.interactiveLegend"
386
+ @toggle="toggle"
387
+ />
388
+ </ChartContainer>
389
+ </template>
@@ -0,0 +1,127 @@
1
+ <script setup lang="ts">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import type { ChartConfig, ChartContext } from './types'
4
+ import { computed, provide, ref } from 'vue'
5
+ import { cn } from '../../lib/utils'
6
+ import { CHART_CONTEXT_KEY } from './types'
7
+
8
+ const props = withDefaults(defineProps<ClassProps & {
9
+ config: ChartConfig
10
+ /** Vạch dóng dọc khi rê chuột. @default true */
11
+ showCursor?: boolean
12
+ }>(), {
13
+ showCursor: true,
14
+ })
15
+
16
+ const keys = computed(() => Object.keys(props.config))
17
+
18
+ /**
19
+ * `var(--color-<key>)` chứ không phải màu đã resolve — xem ghi chú ở `ChartContext`.
20
+ * Unovis vốn thiết kế theo hướng CSS variable (mặc định của nó là
21
+ * `var(--vis-colorN)`), nên chuỗi này đi thẳng vào thuộc tính SVG được.
22
+ */
23
+ function colorOf(key: string): string {
24
+ return `var(--color-${key})`
25
+ }
26
+
27
+ function labelOf(key: string): string {
28
+ return props.config[key]?.label ?? key
29
+ }
30
+
31
+ /**
32
+ * Biến CSS đặt ngay trên thẻ bọc, hai nhóm:
33
+ *
34
+ * 1. `--color-<key>` cho từng chuỗi — đúng cách shadcn làm, nhờ vậy khai màu một
35
+ * chỗ rồi dùng lại ở đường vẽ, chấm tooltip và ô legend.
36
+ * 2. `--vis-*` để nối diện mạo của Unovis vào token của repo.
37
+ *
38
+ * Cơ chế dark theme SẴN CÓ của Unovis cố tình không dùng: nó bắt theo
39
+ * `html[data-theme="dark"]` / `html.theme-dark`, mà repo này dùng `html.dark` —
40
+ * không selector nào khớp. Khai thẳng token ở đây thì chart đi theo cả năm bảng
41
+ * màu, hai chế độ sáng/tối và cả `data-surface="glass"` mà không cần một dòng JS.
42
+ */
43
+ const styleVars = computed<Record<string, string>>(() => {
44
+ const vars: Record<string, string> = {
45
+ '--vis-font-family': 'var(--font-sans)',
46
+ '--vis-axis-font-family': 'var(--font-sans)',
47
+
48
+ // Trục: chữ mờ một nấc so với nội dung, kẻ nền dùng đúng màu viền chung.
49
+ '--vis-axis-tick-label-color': 'var(--muted-foreground)',
50
+ '--vis-axis-label-color': 'var(--muted-foreground)',
51
+ '--vis-axis-tick-label-font-size': '12px',
52
+ '--vis-axis-grid-color': 'var(--border)',
53
+ '--vis-axis-domain-color': 'var(--border)',
54
+ '--vis-axis-tick-color': 'var(--border)',
55
+ '--vis-axis-grid-line-width': '1',
56
+
57
+ // Vạch dóng khi rê chuột. Tắt bằng ĐỘ ĐỤC chứ không bằng màu: Unovis tô vạch
58
+ // qua một class emotion đọc `--vis-crosshair-line-stroke-opacity`, nên đây là
59
+ // chỗ duy nhất giấu được nó mà không phải đụng vào DOM sau khi vẽ.
60
+ '--vis-crosshair-line-stroke-color': 'var(--border)',
61
+ '--vis-crosshair-line-stroke-width': '1',
62
+ '--vis-crosshair-line-stroke-opacity': props.showCursor ? '1' : '0',
63
+ '--vis-crosshair-circle-stroke-color': 'var(--background)',
64
+
65
+ // Hộp tooltip của Unovis bị làm cho TÀNG HÌNH: nền, viền, bóng và padding
66
+ // đều do markup của mình vẽ bằng utility Tailwind (xem `tooltip.ts`), nhờ
67
+ // vậy tooltip dùng đúng token như Popover chứ không phải một hộp riêng.
68
+ '--vis-tooltip-background-color': 'transparent',
69
+ '--vis-tooltip-border-color': 'transparent',
70
+ '--vis-tooltip-box-shadow': 'none',
71
+ '--vis-tooltip-backdrop-filter': 'none',
72
+ '--vis-tooltip-padding': '0',
73
+ '--vis-tooltip-border-radius': '0',
74
+ }
75
+
76
+ // Lưới màu dự phòng của Unovis (`--vis-color0`…`--vis-color5`) cho trường hợp
77
+ // một component con quên truyền `color` — đỡ ra hình trong suốt.
78
+ for (let i = 0; i < 6; i += 1) {
79
+ vars[`--vis-color${i}`] = `var(--chart-${(i % 5) + 1})`
80
+ }
81
+
82
+ keys.value.forEach((key, i) => {
83
+ const color = props.config[key]?.color ?? `var(--chart-${(i % 5) + 1})`
84
+ vars[`--color-${key}`] = color
85
+ // Màu chữ đặt TRỰC TIẾP trên màu chuỗi đó (nhãn trong cột, trong lát, trên
86
+ // vòng radial). Không thể dùng chung một màu chữ cho cả bộ vì thang màu cố ý
87
+ // trải rộng độ sáng — cùng cơ chế và cùng ngưỡng với `--chart-N-foreground`
88
+ // trong tokens.css; xem ghi chú ở đó về vì sao hệ số phải là 1000.
89
+ vars[`--color-${key}-foreground`] = `oklch(from ${color} clamp(0, (0.58 - l) * 1000, 1) 0 h)`
90
+ })
91
+
92
+ return vars
93
+ })
94
+
95
+ /**
96
+ * Con trỏ có đang ở trong khung không. Theo dõi ở ĐÂY chứ không ở từng biểu đồ
97
+ * vì đây là thẻ bọc duy nhất dùng chung, và `ChartCrosshair` thì không có phần
98
+ * tử DOM nào của riêng nó để gắn sự kiện.
99
+ */
100
+ const hovered = ref(false)
101
+
102
+ provide<ChartContext>(CHART_CONTEXT_KEY, {
103
+ get config() {
104
+ return props.config
105
+ },
106
+ colorOf,
107
+ labelOf,
108
+ hovered,
109
+ })
110
+ </script>
111
+
112
+ <template>
113
+ <!--
114
+ Chiều cao đặt ở thẻ bọc này. Unovis tự tiêm stylesheet KHÔNG nằm trong
115
+ cascade layer, nên nó thắng utility của Tailwind bất kể specificity — đúng
116
+ cái bẫy mà `vue-echarts` từng gây ra. Cứ để container giữ kích thước và
117
+ Unovis đo theo nó thì không đụng độ.
118
+ -->
119
+ <div
120
+ :class="cn('ui-chart flex h-64 w-full min-w-0 flex-col text-xs', props.class)"
121
+ :style="styleVars"
122
+ @pointerenter="hovered = true"
123
+ @pointerleave="hovered = false"
124
+ >
125
+ <slot />
126
+ </div>
127
+ </template>
@@ -0,0 +1,92 @@
1
+ <script setup lang="ts" generic="T extends Record<string, unknown>">
2
+ import type { ChartTooltipOptions, ChartTooltipRow, ChartValueFormatter } from './types'
3
+ import { VisCrosshair, VisTooltip } from '@unovis/vue'
4
+ import { computed, inject } from 'vue'
5
+ import { formatChartValue, renderTooltip } from './tooltip'
6
+ import { CHART_CONTEXT_KEY } from './types'
7
+
8
+ const props = defineProps<{
9
+ /** Khoá làm nhãn trục hoành — cũng là tiêu đề của tooltip. */
10
+ index: Extract<keyof T, string>
11
+ /** Các chuỗi cần liệt kê, theo đúng thứ tự vẽ. */
12
+ categories: Extract<keyof T, string>[]
13
+ /** Chuỗi đang bị legend tắt — vẫn vẽ mờ nhưng không hiện trong tooltip. */
14
+ hidden?: string[]
15
+ colorOf: (key: string) => string
16
+ labelOf: (key: string) => string
17
+ valueFormatter?: ChartValueFormatter
18
+ options?: ChartTooltipOptions
19
+ /** Ghim tooltip mở sẵn ở điểm thứ mấy khi con trỏ chưa vào khung. */
20
+ defaultIndex?: number
21
+ /** Vạch dóng và chấm bám. Vạch do ChartContainer giấu, chấm thì ở đây. @default true */
22
+ showCursor?: boolean
23
+ }>()
24
+
25
+ const chart = inject(CHART_CONTEXT_KEY, undefined)
26
+
27
+ /**
28
+ * `forceShowAt` của Unovis nhận toạ độ trong KHÔNG GIAN DỮ LIỆU, mà trục hoành
29
+ * của bộ này chạy theo chỉ số hàng — nên truyền thẳng `defaultIndex` là đúng.
30
+ *
31
+ * Phải NHẢ ra khi con trỏ vào khung, không thì tooltip đứng im: `_render` của
32
+ * Crosshair đọc `forceShowAt` TRƯỚC vị trí chuột, đặt sẵn là rê chuột không
33
+ * nhúc nhích. Bỏ chuột ra thì nó quay lại điểm ghim — đúng cách Recharts xử lý
34
+ * `defaultIndex`.
35
+ */
36
+ const forceShowAt = computed(() => (chart?.hovered.value ? undefined : props.defaultIndex))
37
+
38
+ function template(d: T): string {
39
+ const format = props.valueFormatter
40
+ const rows: ChartTooltipRow[] = props.categories
41
+ .filter(key => !props.hidden?.includes(key))
42
+ .map((key) => {
43
+ const raw = Number(d[key])
44
+ return {
45
+ key,
46
+ label: props.labelOf(key),
47
+ color: props.colorOf(key),
48
+ value: format ? format(raw, key) : formatChartValue(raw),
49
+ raw,
50
+ }
51
+ })
52
+
53
+ // `labelKey` trỏ vào một khoá trong `config` chứ không phải một cột dữ liệu:
54
+ // tiêu đề khi đó là một nhãn CỐ ĐỊNH cho cả biểu đồ ("Activities"), không đổi
55
+ // theo điểm đang rê. Vẫn cho `labelFormatter` chạy tiếp bên trong `renderTooltip`.
56
+ const title = props.options?.labelKey
57
+ ? props.labelOf(props.options.labelKey)
58
+ : String(d[props.index] ?? '')
59
+
60
+ return renderTooltip(title, rows, props.options ?? {})
61
+ }
62
+
63
+ /** Màu của chấm tròn bám trên từng đường, khớp thứ tự với `categories`. */
64
+ function color(_d: unknown, i: number): string {
65
+ return props.colorOf(props.categories[i] ?? '')
66
+ }
67
+ </script>
68
+
69
+ <template>
70
+ <VisTooltip :horizontal-shift="20" :vertical-shift="20" />
71
+ <!--
72
+ HAI prop dưới đây phải viết camelCase, KHÔNG được gạch nối. `VisCrosshair`
73
+ của @unovis/vue 1.6.7 là component DUY NHẤT không khai `props` (mọi wrapper
74
+ khác đều có), nên Vue đẩy tất cả xuống `attrs` và giữ nguyên tên gạch nối;
75
+ `useForwardProps` thì camel-hoá từ danh sách props — vốn rỗng — nên config
76
+ nhận được `forceShowAt: undefined` cạnh `'force-show-at': 1`. Không lỗi,
77
+ không cảnh báo, prop chỉ đơn giản là không có tác dụng.
78
+
79
+ `visibilityThreshold: 0` khi có điểm ghim: mặc định Unovis đòi khung lộ ra ít
80
+ nhất 35% viewport, mà nó KHÔNG theo dõi cuộn trang — ô nằm dưới màn hình lúc
81
+ mount sẽ không bao giờ vẽ tooltip, kể cả sau khi cuộn tới.
82
+ -->
83
+ <!-- eslint-disable vue/attribute-hyphenation -->
84
+ <VisCrosshair
85
+ :template="template"
86
+ :color="color"
87
+ :forceShowAt="forceShowAt"
88
+ :circleRadius="props.showCursor === false ? 0 : undefined"
89
+ :visibilityThreshold="props.defaultIndex === undefined ? undefined : 0"
90
+ />
91
+ <!-- eslint-enable vue/attribute-hyphenation -->
92
+ </template>
@@ -0,0 +1,63 @@
1
+ <script setup lang="ts">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import { computed, inject } from 'vue'
4
+ import { cn } from '../../lib/utils'
5
+ import { CHART_CONTEXT_KEY } from './types'
6
+
7
+ /**
8
+ * Legend viết bằng HTML thường, KHÔNG dùng `VisBulletLegend`.
9
+ *
10
+ * shadcn-vue để Unovis render legend rồi sau khi mount mới `querySelector` và
11
+ * nhét class của Button vào từng mục. Cách đó phụ thuộc vào class nội bộ của
12
+ * thư viện và chạy lệch một nhịp sau khi vẽ. Tự render thì markup nằm trong tay
13
+ * mình: đúng bố cục shadcn, có chỗ cho icon, và bấm để ẩn/hiện chuỗi.
14
+ */
15
+ const props = defineProps<ClassProps & {
16
+ /** Các chuỗi hiện trong legend, theo thứ tự vẽ. */
17
+ categories: string[]
18
+ /** Chuỗi đang bị tắt. */
19
+ hidden?: string[]
20
+ /** Cho bấm vào mục để ẩn/hiện chuỗi. @default false */
21
+ interactive?: boolean
22
+ }>()
23
+
24
+ const emit = defineEmits<{
25
+ toggle: [key: string]
26
+ }>()
27
+
28
+ const chart = inject(CHART_CONTEXT_KEY)
29
+
30
+ const items = computed(() => props.categories.map(key => ({
31
+ key,
32
+ label: chart?.labelOf(key) ?? key,
33
+ color: chart?.colorOf(key) ?? 'transparent',
34
+ icon: chart?.config[key]?.icon,
35
+ inactive: props.hidden?.includes(key) ?? false,
36
+ })))
37
+ </script>
38
+
39
+ <template>
40
+ <div :class="cn('flex flex-wrap items-center justify-center gap-4 pt-3', props.class)">
41
+ <component
42
+ :is="props.interactive ? 'button' : 'div'"
43
+ v-for="item in items"
44
+ :key="item.key"
45
+ :type="props.interactive ? 'button' : undefined"
46
+ :aria-pressed="props.interactive ? !item.inactive : undefined"
47
+ :class="cn(
48
+ 'flex items-center gap-1.5 text-xs text-muted-foreground transition-opacity',
49
+ props.interactive && 'cursor-pointer hover:opacity-80',
50
+ item.inactive && 'opacity-40',
51
+ )"
52
+ @click="props.interactive && emit('toggle', item.key)"
53
+ >
54
+ <component :is="item.icon" v-if="item.icon" class="size-3 shrink-0" />
55
+ <span
56
+ v-else
57
+ class="size-2 shrink-0 rounded-[2px]"
58
+ :style="{ background: item.color }"
59
+ />
60
+ {{ item.label }}
61
+ </component>
62
+ </div>
63
+ </template>