@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,45 @@
1
+ import type { VariantProps } from 'class-variance-authority'
2
+ import { cva } from 'class-variance-authority'
3
+
4
+ /**
5
+ * Dòng thời gian: nhật ký hoạt động, lịch sử đơn hàng, các bước đã chạy.
6
+ *
7
+ * P cho đặt nội dung ở **cả hai** phía đường kẻ (`align="alternate"`). Ở đây bỏ
8
+ * hẳn kiểu đó: nội dung so le hai bên đọc theo hình zíc zắc, mà nhật ký quản trị
9
+ * thì người ta quét dọc bằng mắt. Chỉ giữ một cột nội dung.
10
+ */
11
+ export const timelineVariants = cva('flex w-full flex-col', {
12
+ variants: {
13
+ size: {
14
+ sm: 'gap-0',
15
+ default: 'gap-0',
16
+ },
17
+ },
18
+ defaultVariants: { size: 'default' },
19
+ })
20
+
21
+ /** Chấm mốc. Màu mang NGHĨA nên đi theo bộ variant chung của repo. */
22
+ export const timelineMarkerVariants = cva(
23
+ 'grid shrink-0 place-items-center rounded-full border-2 bg-background [&_svg]:size-3',
24
+ {
25
+ variants: {
26
+ variant: {
27
+ default: 'border-border text-muted-foreground',
28
+ primary: 'border-primary bg-primary text-primary-foreground',
29
+ success: 'border-success bg-success text-success-foreground',
30
+ warning: 'border-warning bg-warning text-warning-foreground',
31
+ destructive: 'border-destructive bg-destructive text-destructive-foreground',
32
+ info: 'border-info bg-info text-info-foreground',
33
+ },
34
+ size: {
35
+ sm: 'size-4',
36
+ default: 'size-6',
37
+ },
38
+ },
39
+ defaultVariants: { variant: 'default', size: 'default' },
40
+ },
41
+ )
42
+
43
+ export type TimelineVariants = VariantProps<typeof timelineVariants>
44
+
45
+ export type TimelineMarkerVariants = VariantProps<typeof timelineMarkerVariants>
@@ -0,0 +1,58 @@
1
+ import type { VariantProps } from 'class-variance-authority'
2
+ import { cva } from 'class-variance-authority'
3
+ import { focusRing } from '../lib/focus'
4
+
5
+ /**
6
+ * Nút giữ trạng thái bật/tắt. Thang cỡ lấy **đúng** của Button (24 / 28 / 32 /
7
+ * 36px, kèm bốn cỡ vuông) chứ không khai riêng: toggle với button hay đứng cùng
8
+ * một thanh công cụ, lệch chiều cao là thấy ngay.
9
+ *
10
+ * Hai `variant`:
11
+ * - `ghost` — không viền, chỉ đổi nền khi bật. Dùng trong thanh công cụ dày đặc.
12
+ * - `outline` — có viền, đọc ra như một nút thật. Dùng khi nó đứng một mình.
13
+ *
14
+ * Trạng thái bật đọc từ `data-state=on` của reka, không phải từ prop.
15
+ */
16
+ export const toggleVariants = cva(
17
+ [
18
+ 'ui-button inline-flex items-center justify-center gap-1.5 whitespace-nowrap rounded-lg',
19
+ 'text-sm font-medium',
20
+ 'transition-[color,background-color,border-color,opacity,transform] duration-(--ui-control-duration)',
21
+ 'active:scale-[0.97] active:duration-(--ui-control-duration-press) motion-reduce:active:scale-100',
22
+ focusRing,
23
+ 'disabled:pointer-events-none disabled:opacity-50',
24
+ '[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
25
+ ],
26
+ {
27
+ variants: {
28
+ variant: {
29
+ ghost: [
30
+ 'bg-transparent hover:bg-accent hover:text-accent-foreground',
31
+ 'data-[state=on]:bg-accent data-[state=on]:text-accent-foreground',
32
+ ],
33
+ outline: [
34
+ 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
35
+ 'data-[state=on]:bg-accent data-[state=on]:text-accent-foreground',
36
+ ],
37
+ },
38
+
39
+ size: {
40
+ 'xs': 'h-(--ui-control-height-xs) gap-1 rounded-md px-(--ui-control-padding-x-xs) text-xs [&_svg]:size-3',
41
+ 'sm': 'h-(--ui-control-height-sm) gap-1 rounded-md px-(--ui-control-padding-x) text-[0.8rem]',
42
+ 'default': 'h-(--ui-control-height-md) px-(--ui-control-padding-x)',
43
+ 'lg': 'h-(--ui-control-height-lg) px-(--ui-control-padding-x)',
44
+ 'icon-xs': 'size-(--ui-control-height-xs) rounded-md [&_svg]:size-3',
45
+ 'icon-sm': 'size-(--ui-control-height-sm) rounded-md',
46
+ 'icon': 'size-(--ui-control-height-md)',
47
+ 'icon-lg': 'size-(--ui-control-height-lg)',
48
+ },
49
+ },
50
+
51
+ defaultVariants: {
52
+ variant: 'ghost',
53
+ size: 'default',
54
+ },
55
+ },
56
+ )
57
+
58
+ export type ToggleVariants = VariantProps<typeof toggleVariants>
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Barrel cho bên nào muốn lấy cả bộ một lần.
3
+ *
4
+ * Bên trong repo thì nhập theo subpath (`src/variants/components/button`): bộ dò
5
+ * token của trang docs đi theo import để biết component nào đọc bảng nào, và
6
+ * một file barrel sẽ làm cả 18 bảng dính vào nhau — trang Select mượn
7
+ * `badgeVariants` cho một cái chip sẽ kéo luôn năm màu trạng thái vào bảng.
8
+ */
9
+
10
+ export * from './components/accordion'
11
+ export * from './components/alert'
12
+ export * from './components/badge'
13
+ export * from './components/button'
14
+ export * from './components/button-group'
15
+ export * from './components/checkbox'
16
+ export * from './components/code-block'
17
+ export * from './components/dialog'
18
+ export * from './components/empty'
19
+ export * from './components/field'
20
+ export * from './components/float-label'
21
+ export * from './components/input-group'
22
+ export * from './components/input-password'
23
+ export * from './components/kbd'
24
+ export * from './components/picker-trigger'
25
+ export * from './components/radio-group'
26
+ export * from './components/sheet'
27
+ export * from './components/sidebar'
28
+ export * from './components/slider'
29
+ export * from './components/timeline'
30
+ export * from './components/toggle'
31
+ export * from './lib/focus'
32
+ export * from './lib/highlight'
33
+ export * from './lib/menu-styles'
34
+ export * from './lib/platform'
35
+ export * from './lib/utils'
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Hình học cực, dùng chung cho RadialChart và RadarChart.
3
+ *
4
+ * Hai loại này Unovis KHÔNG có (`nested-donut` là sunburst phân cấp, không phải
5
+ * vòng đồng tâm; radar thì không có gì cả), nên vẽ tay bằng SVG. Đổi lại được
6
+ * một thứ: mọi màu là `var(--color-…)` đặt thẳng vào thuộc tính SVG, nên vẫn đi
7
+ * theo token y hệt phần còn lại — không phải uốn theo API của thư viện.
8
+ *
9
+ * Quy ước góc: 0 ở đỉnh (12 giờ), chạy THEO chiều kim đồng hồ, đơn vị độ.
10
+ * Chọn vậy vì cả radial lẫn radar đều đọc từ đỉnh xuống, còn `Math.cos/sin`
11
+ * mặc định lấy 0 ở hướng 3 giờ và chạy ngược kim đồng hồ.
12
+ */
13
+
14
+ export interface Point {
15
+ x: number
16
+ y: number
17
+ }
18
+
19
+ /** Đổi (góc, bán kính) sang toạ độ Descartes quanh tâm. */
20
+ export function polarToCartesian(cx: number, cy: number, radius: number, angleDeg: number): Point {
21
+ const rad = ((angleDeg - 90) * Math.PI) / 180
22
+ return { x: cx + radius * Math.cos(rad), y: cy + radius * Math.sin(rad) }
23
+ }
24
+
25
+ /**
26
+ * Path của một cung VÀNH (có bề dày), tức hình quạt bị khoét ruột.
27
+ *
28
+ * Cung tròn 360° phải tách làm HAI cung 180°: cờ `large-arc-flag` của SVG chỉ
29
+ * phân biệt được cung lớn hay nhỏ, nên một cung đúng 360° có điểm đầu trùng
30
+ * điểm cuối và trình duyệt vẽ ra… không gì cả.
31
+ */
32
+ export function annularArcPath(
33
+ cx: number,
34
+ cy: number,
35
+ outerRadius: number,
36
+ innerRadius: number,
37
+ startAngle: number,
38
+ endAngle: number,
39
+ ): string {
40
+ const sweep = Math.abs(endAngle - startAngle)
41
+ if (sweep < 0.01)
42
+ return ''
43
+
44
+ if (sweep >= 359.99) {
45
+ const mid = startAngle + 180
46
+ return `${annularArcPath(cx, cy, outerRadius, innerRadius, startAngle, mid)} ${annularArcPath(cx, cy, outerRadius, innerRadius, mid, startAngle + 359.98)}`
47
+ }
48
+
49
+ const largeArc = sweep > 180 ? 1 : 0
50
+ const o1 = polarToCartesian(cx, cy, outerRadius, startAngle)
51
+ const o2 = polarToCartesian(cx, cy, outerRadius, endAngle)
52
+ const i2 = polarToCartesian(cx, cy, innerRadius, endAngle)
53
+ const i1 = polarToCartesian(cx, cy, innerRadius, startAngle)
54
+
55
+ return [
56
+ `M ${o1.x} ${o1.y}`,
57
+ `A ${outerRadius} ${outerRadius} 0 ${largeArc} 1 ${o2.x} ${o2.y}`,
58
+ `L ${i2.x} ${i2.y}`,
59
+ `A ${innerRadius} ${innerRadius} 0 ${largeArc} 0 ${i1.x} ${i1.y}`,
60
+ 'Z',
61
+ ].join(' ')
62
+ }
63
+
64
+ /** Các đỉnh của một đa giác đều `sides` cạnh, đỉnh đầu ở 12 giờ. */
65
+ export function polygonPoints(cx: number, cy: number, radius: number, sides: number): Point[] {
66
+ return Array.from({ length: sides }, (_, i) => polarToCartesian(cx, cy, radius, (360 / sides) * i))
67
+ }
68
+
69
+ /** Chuỗi `points` cho `<polygon>`. */
70
+ export function toPointsAttr(points: Point[]): string {
71
+ return points.map(p => `${p.x.toFixed(2)},${p.y.toFixed(2)}`).join(' ')
72
+ }
73
+
74
+ /**
75
+ * Khung bao của một cung vành, tính đúng chứ không lấy cả hình vuông.
76
+ *
77
+ * Cần nó vì một cung nửa vòng chỉ chiếm nửa khung: để nguyên khung vuông thì
78
+ * biểu đồ bán nguyệt bị dạt lên trên và bỏ trống nửa dưới. Điểm cực trị của
79
+ * cung tròn chỉ rơi vào hai đầu mút HOẶC các mốc 0/90/180/270 nằm trong khoảng,
80
+ * nên chỉ cần thử chừng ấy điểm.
81
+ */
82
+ export function arcBounds(
83
+ cx: number,
84
+ cy: number,
85
+ outerRadius: number,
86
+ innerRadius: number,
87
+ startAngle: number,
88
+ endAngle: number,
89
+ includeCenter = false,
90
+ ): { x: number, y: number, width: number, height: number } {
91
+ const lo = Math.min(startAngle, endAngle)
92
+ const hi = Math.max(startAngle, endAngle)
93
+
94
+ const angles = [lo, hi]
95
+ for (let a = Math.ceil(lo / 90) * 90; a <= hi; a += 90)
96
+ angles.push(a)
97
+
98
+ const points: Point[] = []
99
+ for (const a of angles) {
100
+ points.push(polarToCartesian(cx, cy, outerRadius, a))
101
+ points.push(polarToCartesian(cx, cy, innerRadius, a))
102
+ }
103
+ if (includeCenter)
104
+ points.push({ x: cx, y: cy })
105
+
106
+ const xs = points.map(p => p.x)
107
+ const ys = points.map(p => p.y)
108
+ const minX = Math.min(...xs)
109
+ const minY = Math.min(...ys)
110
+ return { x: minX, y: minY, width: Math.max(...xs) - minX, height: Math.max(...ys) - minY }
111
+ }
112
+
113
+ /**
114
+ * Path của một cung MỞ (chỉ đường, không khép kín) chạy trên bán kính `radius`.
115
+ *
116
+ * Dùng cho radial bar: thay vì tô một hình quạt vành, ta VẼ NÉT dày bằng đúng
117
+ * bề dày vành dọc theo đường tâm của nó. Nhờ vậy `stroke-linecap="round"` cho ra
118
+ * hai đầu cung bo tròn miễn phí — thứ mà tô hình quạt phải dựng path phức tạp
119
+ * mới có (Recharts gọi là `cornerRadius`).
120
+ *
121
+ * Cung 360° vẫn phải tách đôi, cùng lý do như `annularArcPath`.
122
+ */
123
+ export function arcPath(cx: number, cy: number, radius: number, startAngle: number, endAngle: number): string {
124
+ const sweep = Math.abs(endAngle - startAngle)
125
+ if (sweep < 0.01)
126
+ return ''
127
+
128
+ if (sweep >= 359.99) {
129
+ const mid = startAngle + 180
130
+ const a = polarToCartesian(cx, cy, radius, startAngle)
131
+ const b = polarToCartesian(cx, cy, radius, mid)
132
+ return `M ${a.x} ${a.y} A ${radius} ${radius} 0 0 1 ${b.x} ${b.y} A ${radius} ${radius} 0 0 1 ${a.x} ${a.y}`
133
+ }
134
+
135
+ const largeArc = sweep > 180 ? 1 : 0
136
+ const from = polarToCartesian(cx, cy, radius, startAngle)
137
+ const to = polarToCartesian(cx, cy, radius, endAngle)
138
+ return `M ${from.x} ${from.y} A ${radius} ${radius} 0 ${largeArc} 1 ${to.x} ${to.y}`
139
+ }
@@ -0,0 +1,101 @@
1
+ import type { ChartTooltipOptions, ChartTooltipRow } from './types'
2
+
3
+ /**
4
+ * Unovis nhận `template` trả về CHUỖI HTML, không nhận component của framework
5
+ * nào — nên hàm này dùng chung được cho cả hai cây, và ở đây là chỗ của nó.
6
+ *
7
+ * shadcn-vue giải bài này bằng `createApp(ChartTooltip).mount(div)` cho từng
8
+ * điểm dữ liệu rồi cache `innerHTML` vào một WeakMap. Cách đó có hai chỗ hỏng:
9
+ * mount hẳn một app mỗi lần rê chuột là quá nặng, và HTML đã cache thì KHÔNG
10
+ * đổi theo theme nữa — đổi sang chế độ tối xong tooltip vẫn giữ màu cũ.
11
+ *
12
+ * Ở đây chỉ ghép chuỗi. Vì nó là DOM thật trong trang nên utility Tailwind và
13
+ * token vẫn áp bình thường, và màu lấy qua `var(--color-<key>)` nên đổi theme
14
+ * hay đổi bảng màu là tooltip đổi theo ngay, không có gì để mà cũ.
15
+ */
16
+
17
+ /** Chặn nhãn do người dùng truyền vào phá cấu trúc HTML. */
18
+ export function escapeHtml(value: string): string {
19
+ return value
20
+ .replace(/&/g, '&amp;')
21
+ .replace(/</g, '&lt;')
22
+ .replace(/>/g, '&gt;')
23
+ .replace(/"/g, '&quot;')
24
+ .replace(/'/g, '&#39;')
25
+ }
26
+
27
+ /**
28
+ * Ba kiểu chỉ báo của shadcn. `dashed` cố ý KHÔNG có nền — nó là hai nét gạch
29
+ * nên phải để trong suốt, nếu tô nền thì nhìn hệt `line` bị dày lên.
30
+ */
31
+ const INDICATOR_CLASS: Record<NonNullable<ChartTooltipOptions['indicator']>, string> = {
32
+ dot: 'size-2.5 rounded-[2px]',
33
+ line: 'h-3 w-1 rounded-[2px]',
34
+ dashed: 'h-3 w-0 border-[1.5px] border-dashed',
35
+ }
36
+
37
+ function indicatorHtml(color: string, options: ChartTooltipOptions): string {
38
+ if (options.hideIndicator)
39
+ return ''
40
+ const kind = options.indicator ?? 'dot'
41
+ const style = kind === 'dashed'
42
+ ? `border-color:${color}`
43
+ : `background:${color}`
44
+ return `<div class="shrink-0 ${INDICATOR_CLASS[kind]}" style="${style}"></div>`
45
+ }
46
+
47
+ /**
48
+ * Ghép hộp tooltip theo đúng bố cục của shadcn: dòng tiêu đề đậm, dưới là từng
49
+ * chuỗi với chỉ báo màu, tên mờ bên trái và số bên phải.
50
+ *
51
+ * Số dùng `tabular-nums` + `font-mono` để cột giá trị không nhảy khi rê chuột
52
+ * qua các điểm có độ dài chữ số khác nhau.
53
+ */
54
+ export function renderTooltip(
55
+ title: string,
56
+ rows: ChartTooltipRow[],
57
+ options: ChartTooltipOptions = {},
58
+ ): string {
59
+ const heading = options.labelFormatter ? options.labelFormatter(title) : title
60
+ const head = heading && !options.hideLabel
61
+ ? `<div class="font-medium text-popover-foreground">${escapeHtml(heading)}</div>`
62
+ : ''
63
+
64
+ const body = rows.map(row => `
65
+ <div class="flex w-full items-center gap-2">
66
+ ${indicatorHtml(row.color, options)}
67
+ <div class="flex flex-1 items-center justify-between gap-4 leading-none">
68
+ <span class="text-muted-foreground">${escapeHtml(row.label)}</span>
69
+ <span class="font-mono font-medium tabular-nums text-foreground">${escapeHtml(row.value)}</span>
70
+ </div>
71
+ </div>`).join('')
72
+
73
+ // Dòng tổng ngăn bằng một đường kẻ, và KHÔNG có chỉ báo màu: nó không phải
74
+ // một chuỗi, gắn ô màu vào là đọc nhầm thành chuỗi thứ n+1.
75
+ const totalRow = options.showTotal && rows.length > 1
76
+ ? `<div class="mt-1.5 flex items-center justify-between gap-4 border-t border-border/50 pt-1.5">
77
+ <span class="text-muted-foreground">${escapeHtml(options.totalLabel ?? 'Total')}</span>
78
+ <span class="font-mono font-medium tabular-nums text-foreground">${escapeHtml(sumOf(rows))}</span>
79
+ </div>`
80
+ : ''
81
+
82
+ return `<div class="grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-popover px-2.5 py-1.5 text-xs shadow-xl">
83
+ ${head}
84
+ <div class="grid gap-1.5">${body}</div>
85
+ ${totalRow}
86
+ </div>`
87
+ }
88
+
89
+ /**
90
+ * Tổng của các dòng. Cộng lại từ CHUỖI đã định dạng thì sai, nên mỗi dòng giữ
91
+ * thêm `raw` để cộng; thiếu `raw` thì bỏ qua dòng đó.
92
+ */
93
+ function sumOf(rows: ChartTooltipRow[]): string {
94
+ const total = rows.reduce((sum, r) => sum + (r.raw ?? 0), 0)
95
+ return formatChartValue(total)
96
+ }
97
+
98
+ /** Định dạng số mặc định: phân nhóm hàng nghìn theo locale của trình duyệt. */
99
+ export function formatChartValue(value: number): string {
100
+ return Number.isFinite(value) ? value.toLocaleString() : '—'
101
+ }
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Kiểu dùng chung cho cả bộ biểu đồ — KHÔNG dính framework nào.
3
+ *
4
+ * Nằm ở đây vì nó mô tả DỮ LIỆU và các tuỳ chọn hiển thị, mà cả hai cây đọc
5
+ * đúng một hình dạng: `ChartConfig` của shadcn, cùng prop của Recharts mà bộ
6
+ * này soi theo. Hai bản là hai cơ hội để một bên thêm prop rồi bên kia không
7
+ * biết.
8
+ *
9
+ * Phần CÓ dính framework — icon của legend, và context mà `ChartContainer`
10
+ * chuyền xuống — nằm ở `types.ts` của từng cây.
11
+ */
12
+
13
+ /**
14
+ * Mô tả một chuỗi số liệu: nhãn, màu, icon.
15
+ *
16
+ * Đây là `ChartConfig` của shadcn, giữ nguyên hình dạng để người đã quen bên đó
17
+ * không phải học lại. Khoá của object chính là khoá trong dữ liệu, nên chỉ khai
18
+ * một lần là legend, tooltip và màu vẽ đều dùng chung.
19
+ */
20
+ export interface ChartSeriesConfig {
21
+ /** Nhãn người dùng nhìn thấy trong legend và tooltip. */
22
+ label: string
23
+ /**
24
+ * Màu của chuỗi. Bỏ trống thì lấy `--chart-1`…`--chart-5` theo đúng thứ tự
25
+ * khai báo trong config, quay vòng khi có hơn năm chuỗi.
26
+ */
27
+ color?: string
28
+ /**
29
+ * Icon đứng trước nhãn trong legend.
30
+ *
31
+ * Kiểu để `unknown` ở tầng dùng chung: bên Vue nó là một `Component`, bên
32
+ * React là một `ComponentType` — mỗi cây thu hẹp lại trong `ChartConfig` của
33
+ * chính nó. Đây là trường DUY NHẤT của bộ type này dính tới framework.
34
+ */
35
+ icon?: unknown
36
+ }
37
+
38
+ /** Khoá của object là khoá trong dữ liệu. */
39
+ export type ChartConfig = Record<string, ChartSeriesConfig>
40
+
41
+ /**
42
+ * Lề trong của khung vẽ.
43
+ *
44
+ * Khai lại ở đây chứ không import `Spacing` của `@unovis/ts`: `src/variants`
45
+ * không được phụ thuộc một thư viện biểu đồ — nó là tầng không biết framework
46
+ * VÀ không biết thư viện vẽ. Hình dạng trùng khít nên vẫn truyền thẳng xuống
47
+ * Unovis được nhờ structural typing.
48
+ */
49
+ export interface ChartSpacing {
50
+ top?: number
51
+ right?: number
52
+ bottom?: number
53
+ left?: number
54
+ }
55
+
56
+ /** Hàm định dạng nhãn trục, nhận đúng chữ ký `tickFormat` của Unovis. */
57
+ export type ChartTickFormatter = (tick: number | Date, i: number, ticks: number[] | Date[]) => string
58
+
59
+ /** Định dạng giá trị hiện trong tooltip và legend. */
60
+ export type ChartValueFormatter = (value: number, key: string) => string
61
+
62
+ /**
63
+ * Kiểu nối điểm. Rút gọn từ `CurveType` của Unovis xuống bốn lựa chọn mà gallery
64
+ * của shadcn thật sự dùng — bày cả mười tám kiểu chỉ tổ làm người đọc phân vân.
65
+ *
66
+ * `natural` là kiểu shadcn dùng nhiều nhất (8 ô area, 6 ô line); `monotone` chỉ
67
+ * ở `line-multiple` và `line-interactive`. Hai kiểu này nhìn gần giống nhau —
68
+ * khác ở chỗ `natural` (spline tự nhiên) được phép vọt qua giá trị điểm dữ liệu,
69
+ * còn `monotone` thì không bao giờ.
70
+ */
71
+ export type ChartCurve = 'monotone' | 'natural' | 'linear' | 'step'
72
+
73
+ /**
74
+ * Nhãn in trên cột / lát / điểm lấy nội dung ở đâu: con SỐ, hay TÊN hạng mục.
75
+ *
76
+ * `'name'` tra qua `config` trước — nhờ vậy dữ liệu ghi `chrome` mà nhãn hiện
77
+ * ra `Chrome`, đúng cách shadcn dùng `formatter` của `LabelList`. Không có
78
+ * trong config thì in nguyên giá trị.
79
+ */
80
+ export type ChartLabelKey = 'value' | 'name'
81
+
82
+ /** Prop dùng chung của mọi biểu đồ trục XY trong bộ này. */
83
+ export interface ChartBaseProps<
84
+ T extends Record<string, unknown>,
85
+ TConfig extends Record<string, ChartSeriesConfig> = ChartConfig,
86
+ > {
87
+ /** Dữ liệu nguồn, mỗi phần tử là một điểm trên trục hoành. */
88
+ data: T[]
89
+ /** Khoá dùng làm nhãn trục hoành, ví dụ `'month'`. */
90
+ index: Extract<keyof T, string>
91
+ /**
92
+ * Các chuỗi cần vẽ. Bỏ trống thì vẽ đúng những khoá có trong `config`, theo
93
+ * thứ tự khai báo — nên thường không phải truyền.
94
+ */
95
+ categories?: Extract<keyof T, string>[]
96
+ /** Nhãn, màu và icon của từng chuỗi. */
97
+ config: TConfig
98
+ /** Lề trong của khung vẽ. */
99
+ margin?: ChartSpacing
100
+ /** Định dạng nhãn trục hoành. */
101
+ xFormatter?: ChartTickFormatter
102
+ /** Định dạng nhãn trục tung. */
103
+ yFormatter?: ChartTickFormatter
104
+ /** Định dạng giá trị trong tooltip. Mặc định là `toLocaleString('vi-VN')`. */
105
+ valueFormatter?: ChartValueFormatter
106
+ /** Tuỳ chọn hiển thị của tooltip: kiểu chỉ báo, ẩn nhãn, ẩn chỉ báo. */
107
+ tooltip?: ChartTooltipOptions
108
+ /** @default true */
109
+ showXAxis?: boolean
110
+ /** @default true */
111
+ showYAxis?: boolean
112
+ /** @default true */
113
+ showTooltip?: boolean
114
+ /** @default false — shadcn để legend tắt sẵn, bật khi có từ hai chuỗi trở lên. */
115
+ showLegend?: boolean
116
+ /** Đường kẻ ngang nền. @default true */
117
+ showGridLine?: boolean
118
+ /**
119
+ * Vạch dóng dọc và chấm bám trên chuỗi khi rê chuột.
120
+ *
121
+ * shadcn tắt nó ở 58/65 ô (`cursor={false}`) — nhưng đó là quy ước TRÌNH BÀY của
122
+ * trang gallery, không phải mặc định hợp lý cho một component: mặc định của
123
+ * Recharts cũng là bật, họ phải khai tắt từng ô. Giữ `true`, ai cần thì tắt.
124
+ * @default true
125
+ */
126
+ showCursor?: boolean
127
+ /**
128
+ * Ghim tooltip mở sẵn ở điểm dữ liệu thứ mấy, không cần rê chuột.
129
+ *
130
+ * Dành cho trang tài liệu: cả chín biến thể ở trang Tooltips của shadcn đều
131
+ * khai `defaultIndex={1}`, vì tooltip là THỨ ĐANG ĐƯỢC TRƯNG BÀY — bắt người
132
+ * đọc rê chuột lên từng ô mới thấy thì xem như không trưng bày gì.
133
+ *
134
+ * Rê chuột lên vẫn đè được; bỏ chuột ra thì nó quay về điểm đã ghim.
135
+ */
136
+ defaultIndex?: number
137
+ }
138
+
139
+ /** Tuỳ chọn hiển thị của tooltip, theo đúng bộ prop của shadcn. */
140
+ export interface ChartTooltipOptions {
141
+ /** Kiểu chỉ báo màu đứng trước tên chuỗi. @default 'dot' */
142
+ indicator?: 'dot' | 'line' | 'dashed'
143
+ /** Bỏ dòng tiêu đề (nhãn trục hoành). */
144
+ hideLabel?: boolean
145
+ /** Bỏ hẳn ô/vạch màu, chỉ còn tên và số. */
146
+ hideIndicator?: boolean
147
+ /**
148
+ * Lấy dòng tiêu đề từ `config[labelKey].label` thay vì từ nhãn trục hoành.
149
+ *
150
+ * Khoá này KHÔNG phải một chuỗi số liệu — nó chỉ có `label`. Vì `categories`
151
+ * mặc định là mọi khoá của `config`, biểu đồ dùng nó phải khai `categories`
152
+ * tường minh, nếu không sẽ mọc thêm một chuỗi rỗng.
153
+ */
154
+ labelKey?: string
155
+ /** Đổi cách viết dòng tiêu đề, ví dụ đổi 'January' thành 'Tháng 1'. */
156
+ labelFormatter?: (label: string) => string
157
+ /** Thêm một dòng tổng ở cuối. Chỉ có nghĩa khi tooltip liệt kê nhiều chuỗi. */
158
+ showTotal?: boolean
159
+ /** Nhãn của dòng tổng. @default 'Total' */
160
+ totalLabel?: string
161
+ }
162
+
163
+ /** Một dòng trong tooltip, đã resolve xong nhãn và màu. */
164
+ export interface ChartTooltipRow {
165
+ key: string
166
+ label: string
167
+ color: string
168
+ /** Đã định dạng, dùng để hiển thị. */
169
+ value: string
170
+ /** Số thô, dùng để cộng dòng tổng. */
171
+ raw?: number
172
+ }
@@ -0,0 +1,42 @@
1
+ import { escapeHtml } from './tooltip'
2
+
3
+ /**
4
+ * Đẩy nhãn của `VisXYLabels` LỆCH khỏi điểm dữ liệu, bằng `<tspan>`.
5
+ *
6
+ * `VisXYLabels` KHÔNG có tuỳ chọn lệch theo pixel: `x`/`y` là toạ độ trong
7
+ * không gian dữ liệu, và chữ luôn nằm ĐÚNG TÂM điểm đó
8
+ * (`text-anchor: middle; dominant-baseline: central`, đặt bằng inline style nên
9
+ * CSS thường không đè được). Nhưng shadcn thì đặt nhãn cách cột 12px phía trên,
10
+ * hoặc 8px ngoài đầu cột ngang — tức phải lệch theo PIXEL, thứ không quy được
11
+ * về đơn vị dữ liệu nếu không biết thang đo.
12
+ *
13
+ * Lối ra nằm ở chỗ Unovis gán nội dung nhãn bằng `text.html(labelText)`, tức
14
+ * chuỗi trả về được nhét thẳng vào `<text>` dưới dạng markup. Ba điều khiến
15
+ * cách này chạy đúng, cả ba đều là SVG chuẩn chứ không phải mẹo:
16
+ *
17
+ * 1. `<tspan y="-12">` dời cả chữ lên 12px — đã ĐO: tâm chữ lệch đúng 12px, và
18
+ * `dominant-baseline: central` của thẻ cha vẫn được kế thừa.
19
+ * 2. `text-anchor` khai TRÊN CHÍNH tspan thắng giá trị kế thừa từ inline style
20
+ * của `<text>` — thuộc tính khai trực tiếp luôn hơn kế thừa.
21
+ * 3. Nhưng `text-anchor` chỉ có hiệu lực ở ĐẦU một text chunk, mà chunk mới chỉ
22
+ * mở ra khi có toạ độ TUYỆT ĐỐI. Vì vậy luôn phát cả `x` lẫn `y`, kể cả khi
23
+ * bằng 0 — thiếu `x` là anchor bị bỏ qua và chữ vẫn nằm giữa.
24
+ *
25
+ * Kèm một hệ quả phải nhớ: `getLabelRenderProps` ước lượng bề rộng nền theo
26
+ * `labelText.length`, mà chuỗi giờ có cả markup nên con số đó vô nghĩa. Không
27
+ * sao vì nền để `none`, NHƯNG nó làm phép gom cụm (`clustering`) tính sai —
28
+ * luôn đặt `:clustering="false"` ở mọi chỗ dùng hàm này.
29
+ */
30
+ export interface XYLabelOffset {
31
+ /** Lệch ngang, pixel. Dương là sang phải. */
32
+ dx?: number
33
+ /** Lệch dọc, pixel. Âm là lên trên. */
34
+ dy?: number
35
+ /** Căn chữ so với điểm neo sau khi đã lệch. @default 'middle' */
36
+ anchor?: 'start' | 'middle' | 'end'
37
+ }
38
+
39
+ export function xyLabelHtml(text: string, offset: XYLabelOffset = {}): string {
40
+ const { dx = 0, dy = 0, anchor = 'middle' } = offset
41
+ return `<tspan x="${dx}" y="${dy}" text-anchor="${anchor}">${escapeHtml(text)}</tspan>`
42
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Vòng focus dùng chung cho control NHẬP LIỆU.
3
+ *
4
+ * Tách ra một chỗ vì trước đây mười ba file cùng chép một chuỗi class, và
5
+ * chúng đã trôi khỏi nhau: có chỗ `ring-offset-1`, có chỗ `ring-offset-2`, có
6
+ * chỗ quên hẳn. Sửa một kiểu focus mà phải mở mười ba file thì kiểu gì cũng sót.
7
+ *
8
+ * Hình dạng (xem `--focus-ring` trong `tokens.css` để biết vì sao hai lớp):
9
+ *
10
+ * | trạng thái | viền | bóng |
11
+ * |---|---|---|
12
+ * | thường | `--input` | `shadow-xs` |
13
+ * | focus | `--ring` | hai lớp, sắc `--ring` |
14
+ * | không hợp lệ | `--destructive` | **không** — chỉ đổi viền |
15
+ * | không hợp lệ + focus | `--destructive` | hai lớp, sắc `--destructive` |
16
+ *
17
+ * Dòng cuối bảng là chỗ dễ làm sai nhất. Người dùng chỉ yêu cầu "invalid thì
18
+ * đổi viền thôi", nhưng đó là nói về trạng thái **tĩnh**; bỏ luôn cả quầng lúc
19
+ * đang focus thì field sai lại thành field khó thấy con trỏ đang ở đâu nhất.
20
+ *
21
+ * Vì sao `data-[invalid]:focus-visible:*` chắc chắn thắng `focus-visible:*`:
22
+ * biến thể ghép sinh ra selector có thêm một attribute selector, tức
23
+ * specificity (0,3,0) so với (0,2,0). Nó thắng nhờ **specificity**, không phải
24
+ * nhờ thứ tự trong file — nên không phụ thuộc vào cách Tailwind sắp xếp.
25
+ */
26
+
27
+ /** Bỏ outline mặc định của trình duyệt. Luôn đi kèm một trong hai bộ dưới. */
28
+ const noOutline = 'focus-visible:outline-none'
29
+
30
+ /** Control mà chính nó nhận focus: input, textarea, trigger của select… */
31
+ export const focusRing = [
32
+ noOutline,
33
+ 'focus-visible:border-ring focus-visible:shadow-focus',
34
+ ].join(' ')
35
+
36
+ /**
37
+ * Vỏ bọc quanh control thật: InputGroup, InputNumber, vùng thả của FileUpload.
38
+ * Focus nằm ở `<input>` bên trong nên vỏ phải bắt bằng `focus-within`, còn ô
39
+ * nhập bên trong thì bị lột sạch viền và vòng focus của nó.
40
+ */
41
+ export const focusRingWithin = [
42
+ 'focus-within:outline-none',
43
+ 'focus-within:border-ring focus-within:shadow-focus',
44
+ ].join(' ')
45
+
46
+ /** Trạng thái không hợp lệ, đọc từ `data-invalid` trên chính element. */
47
+ export const invalidRing = [
48
+ 'data-[invalid]:border-destructive',
49
+ 'data-[invalid]:focus-visible:border-destructive data-[invalid]:focus-visible:shadow-focus-invalid',
50
+ ].join(' ')
51
+
52
+ /** Bản `focus-within` của `invalidRing`, cho vỏ bọc. */
53
+ export const invalidRingWithin = [
54
+ 'data-[invalid]:border-destructive',
55
+ 'data-[invalid]:focus-within:border-destructive data-[invalid]:focus-within:shadow-focus-invalid',
56
+ ].join(' ')
57
+
58
+ /**
59
+ * Cho control không đọc được `data-invalid` từ DOM mà phải nhận qua prop —
60
+ * chủ yếu là các wrapper của reka, nơi thuộc tính không xuống tới element
61
+ * đang vẽ viền.
62
+ */
63
+ export const invalidRingStatic = [
64
+ 'border-destructive',
65
+ 'focus-visible:border-destructive focus-visible:shadow-focus-invalid',
66
+ ].join(' ')