@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,32 @@
1
+ <script setup lang="ts">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import type { InputGroupAddonVariants } from './index'
4
+ import { cn } from '../../lib/utils'
5
+ import { injectInputGroupContext, inputGroupAddonVariants } from './index'
6
+
7
+ const props = withDefaults(
8
+ defineProps<ClassProps & {
9
+ variant?: InputGroupAddonVariants['variant']
10
+ /** Đứng trước hay sau ô nhập — quyết định đường kẻ vẽ ở cạnh nào. */
11
+ align?: InputGroupAddonVariants['align']
12
+ }>(),
13
+ { variant: 'text', align: 'start' },
14
+ )
15
+
16
+ const group = injectInputGroupContext(null)
17
+ </script>
18
+
19
+ <template>
20
+ <div
21
+ :class="cn(
22
+ inputGroupAddonVariants({
23
+ variant: props.variant,
24
+ align: props.align,
25
+ size: group?.size.value,
26
+ }),
27
+ props.class,
28
+ )"
29
+ >
30
+ <slot />
31
+ </div>
32
+ </template>
@@ -0,0 +1,33 @@
1
+ <script setup lang="ts">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import { computed } from 'vue'
4
+ import { cn } from '../../lib/utils'
5
+ import { injectInputGroupContext } from './index'
6
+
7
+ const props = defineProps<ClassProps & { disabled?: boolean }>()
8
+
9
+ const model = defineModel<string | number | null>()
10
+
11
+ const group = injectInputGroupContext(null)
12
+
13
+ const padding = computed(() => (group?.size.value === 'sm' ? 'px-2.5' : 'px-3'))
14
+ </script>
15
+
16
+ <template>
17
+ <!--
18
+ Lột sạch viền, nền, bóng và vòng focus: nhóm bên ngoài đang giữ cả bốn thứ
19
+ đó. Giữ lại trên ô nhập là ra vòng focus lồng trong vòng focus.
20
+ -->
21
+ <input
22
+ v-model="model"
23
+ :disabled="props.disabled"
24
+ :class="cn(
25
+ 'min-w-0 flex-1 border-0 bg-transparent text-inherit shadow-none outline-none',
26
+ 'placeholder:text-muted-foreground',
27
+ 'focus:outline-none focus-visible:outline-none focus-visible:ring-0',
28
+ 'disabled:cursor-not-allowed',
29
+ padding,
30
+ props.class,
31
+ )"
32
+ >
33
+ </template>
@@ -0,0 +1,17 @@
1
+ import type { ComputedRef } from 'vue'
2
+ import type { InputGroupVariants } from '../../variants/components/input-group'
3
+ import { createContext } from 'reka-ui'
4
+
5
+ export { inputGroupAddonVariants, type InputGroupAddonVariants, inputGroupVariants, type InputGroupVariants } from '../../variants/components/input-group'
6
+ export { default as InputGroup } from './InputGroup.vue'
7
+ export { default as InputGroupAddon } from './InputGroupAddon.vue'
8
+
9
+ /** Cỡ khai một lần ở nhóm, ô nhập và phần phụ đọc lại. */
10
+ export interface InputGroupContext {
11
+ size: ComputedRef<InputGroupVariants['size']>
12
+ }
13
+
14
+ export const [injectInputGroupContext, provideInputGroupContext]
15
+ = createContext<InputGroupContext>('InputGroup')
16
+
17
+ export { default as InputGroupInput } from './InputGroupInput.vue'
@@ -0,0 +1,82 @@
1
+ <script setup lang="ts">
2
+ import type { NumberFieldRootEmits, NumberFieldRootProps } from 'reka-ui'
3
+ import type { ClassProps } from '../../lib/types'
4
+ import { NumberFieldDecrement, NumberFieldIncrement, NumberFieldInput, NumberFieldRoot, useForwardPropsEmits } from 'reka-ui'
5
+ import { computed } from 'vue'
6
+ import { focusRingWithin, invalidRingWithin } from '../../lib/focus'
7
+ import { Minus, Plus } from '../../lib/icons'
8
+ import { cn } from '../../lib/utils'
9
+
10
+ const props = withDefaults(
11
+ defineProps<NumberFieldRootProps & ClassProps & {
12
+ size?: 'sm' | 'default'
13
+ invalid?: boolean
14
+ /** Ẩn hai nút cộng trừ, chỉ còn ô nhập có định dạng số. */
15
+ hideStepper?: boolean
16
+ }>(),
17
+ {
18
+ size: 'default',
19
+ invalid: false,
20
+ hideStepper: false,
21
+ /**
22
+ * Mặc định `vi-VN`: dấu phân cách nghìn và dấu thập phân của tiếng Việt
23
+ * ngược với tiếng Anh (1.234,5 chứ không phải 1,234.5). Để trình duyệt tự
24
+ * đoán theo ngôn ngữ hệ thống thì cùng một màn hình có thể hiện hai kiểu.
25
+ */
26
+ locale: 'vi-VN',
27
+ },
28
+ )
29
+
30
+ const emits = defineEmits<NumberFieldRootEmits>()
31
+
32
+ const delegated = computed(() => {
33
+ const { class: _class, size: _size, invalid: _invalid, hideStepper: _hideStepper, ...rest } = props
34
+ return rest
35
+ })
36
+
37
+ const forwarded = useForwardPropsEmits(delegated, emits)
38
+
39
+ const height = computed(() => (props.size === 'sm' ? 'h-(--ui-field-height-sm)' : 'h-(--ui-field-height-md)'))
40
+
41
+ const stepper = cn(
42
+ 'grid w-8 shrink-0 place-items-center text-muted-foreground transition-colors',
43
+ 'hover:bg-accent hover:text-accent-foreground',
44
+ 'disabled:pointer-events-none disabled:opacity-40',
45
+ '[&_svg]:size-3.5',
46
+ )
47
+ </script>
48
+
49
+ <template>
50
+ <NumberFieldRoot
51
+ v-bind="forwarded"
52
+ :data-invalid="props.invalid || undefined"
53
+ :class="cn(
54
+ 'flex w-full items-stretch overflow-hidden rounded-md border border-input bg-background shadow-xs transition-colors',
55
+ focusRingWithin,
56
+ 'has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50',
57
+ invalidRingWithin,
58
+ height,
59
+ props.class,
60
+ )"
61
+ >
62
+ <!-- Nút giảm nằm bên TRÁI, tăng bên PHẢI: cùng thứ tự với trục số, nên
63
+ không phải nghĩ xem nút nào làm gì. -->
64
+ <NumberFieldDecrement :class="cn(stepper, 'border-r border-input', props.hideStepper && 'hidden')">
65
+ <Minus />
66
+ </NumberFieldDecrement>
67
+
68
+ <!-- Căn giữa khi có hai nút hai bên, căn trái khi không: số lơ lửng giữa một
69
+ ô trống trông như bị đặt nhầm chỗ. -->
70
+ <NumberFieldInput
71
+ :class="cn(
72
+ 'min-w-0 flex-1 border-0 bg-transparent px-2.5 text-sm tabular-nums outline-none',
73
+ 'placeholder:text-muted-foreground disabled:cursor-not-allowed',
74
+ props.hideStepper ? 'text-left' : 'text-center',
75
+ )"
76
+ />
77
+
78
+ <NumberFieldIncrement :class="cn(stepper, 'border-l border-input', props.hideStepper && 'hidden')">
79
+ <Plus />
80
+ </NumberFieldIncrement>
81
+ </NumberFieldRoot>
82
+ </template>
@@ -0,0 +1 @@
1
+ export { default as InputNumber } from './InputNumber.vue'
@@ -0,0 +1,186 @@
1
+ <script setup lang="ts">
2
+ import type { PinInputRootEmits, PinInputRootProps } from 'reka-ui'
3
+ import type { ClassProps } from '../../lib/types'
4
+ import { PinInputInput, PinInputRoot, useForwardPropsEmits } from 'reka-ui'
5
+ import { computed, nextTick } from 'vue'
6
+ import { focusRing, invalidRingStatic } from '../../lib/focus'
7
+ import { cn } from '../../lib/utils'
8
+
9
+ const props = withDefaults(
10
+ defineProps<Omit<PinInputRootProps, 'modelValue'> & ClassProps & {
11
+ /** Số ô. */
12
+ length?: number
13
+ /** Chèn dấu ngăn sau mỗi `groupSize` ô. Đặt 0 để không ngăn. */
14
+ groupSize?: number
15
+ invalid?: boolean
16
+ }>(),
17
+ {
18
+ length: 6,
19
+ groupSize: 0,
20
+ /**
21
+ * `otp` bật gợi ý mã một lần của trình duyệt: iOS và Chrome sẽ mời dán mã
22
+ * vừa nhận qua SMS. Đây là lý do chính để dùng component này thay vì sáu ô
23
+ * `<input>` viết tay.
24
+ */
25
+ otp: true,
26
+ type: 'text',
27
+ invalid: false,
28
+ },
29
+ )
30
+
31
+ const emits = defineEmits<PinInputRootEmits>()
32
+
33
+ const model = defineModel<string[]>({ default: () => [] })
34
+
35
+ const delegated = computed(() => {
36
+ const { class: _class, length: _length, groupSize: _groupSize, invalid: _invalid, ...rest } = props
37
+ return rest
38
+ })
39
+
40
+ const forwarded = useForwardPropsEmits(delegated, emits)
41
+
42
+ const cells = computed(() => Array.from({ length: props.length }, (_, i) => i))
43
+
44
+ /** Sau ô thứ `groupSize`, `2*groupSize`… thì chèn dấu ngăn — trừ ô cuối cùng. */
45
+ function hasSeparatorAfter(index: number): boolean {
46
+ if (!props.groupSize)
47
+ return false
48
+ return (index + 1) % props.groupSize === 0 && index !== props.length - 1
49
+ }
50
+
51
+ /* ------------------------------------------------------------------------- *
52
+ * Dãy phải LUÔN DÀY: không có ô trống nằm giữa hai ô có giá trị.
53
+ *
54
+ * reka đá con trỏ về ô trống đầu tiên ở MỌI lần focus (`handleFocus` của
55
+ * `PinInputInput`), kể cả khi ô được bấm vào đang có chữ số. Để nguyên hành vi
56
+ * xoá của reka — xoá tại chỗ, chừa lại một lỗ — thì các ô sau lỗ đó không còn
57
+ * cách nào chạm tới: chuột, mũi tên và Tab đều bị đá ngược. Xem BUG-001.
58
+ *
59
+ * Không tắt được cái đá đó (`otp` gánh luôn `autocomplete="one-time-code"`),
60
+ * nên thay vào đó ta không bao giờ tạo ra lỗ: xoá thì dồn trái, gõ chèn thì dồn
61
+ * phải — coi sáu ô như một chuỗi ký tự, đúng cách `input-otp` của shadcn làm.
62
+ * ------------------------------------------------------------------------- */
63
+
64
+ /** Dãy hiện tại, đủ `length` phần tử, ô thiếu thành chuỗi rỗng. */
65
+ function currentValues(): string[] {
66
+ return Array.from({ length: props.length }, (_, i) => model.value[i] ?? '')
67
+ }
68
+
69
+ /** Ghi lại model, cắt đuôi rỗng cho khớp cách reka tự chuẩn hoá. */
70
+ function commit(values: string[]): void {
71
+ const next = values.slice(0, props.length)
72
+ while (next.length > 0 && next[next.length - 1] === '')
73
+ next.pop()
74
+ model.value = next
75
+ }
76
+
77
+ function cellIndexOf(target: EventTarget | null): number {
78
+ const raw = (target as HTMLElement | null)?.dataset?.index
79
+ return raw === undefined ? -1 : Number(raw)
80
+ }
81
+
82
+ function focusCell(root: HTMLElement, index: number): void {
83
+ nextTick(() => {
84
+ root.querySelector<HTMLInputElement>(`input[data-index="${index}"]`)?.focus()
85
+ })
86
+ }
87
+
88
+ /**
89
+ * Backspace / Delete: bỏ hẳn ô đó khỏi dãy rồi dồn phần đuôi lên một bước.
90
+ * Chặn ở pha capture nên handler của reka trên chính ô đó không chạy nữa.
91
+ */
92
+ function handleKeydownCapture(event: KeyboardEvent): void {
93
+ if (event.key !== 'Backspace' && event.key !== 'Delete')
94
+ return
95
+
96
+ const root = event.currentTarget as HTMLElement
97
+ const index = cellIndexOf(event.target)
98
+ if (index < 0)
99
+ return
100
+
101
+ const values = currentValues()
102
+ let focusIndex = index
103
+
104
+ if (values[index] !== '') {
105
+ values.splice(index, 1)
106
+ }
107
+ else if (event.key === 'Backspace' && index > 0) {
108
+ // Dãy luôn dày nên ô trống chỉ nằm ở đuôi: lùi lại một ô và xoá ô đó.
109
+ values.splice(index - 1, 1)
110
+ focusIndex = index - 1
111
+ }
112
+ else {
113
+ // Delete trên ô trống, hoặc Backspace ở ô đầu: không có gì để xoá.
114
+ return
115
+ }
116
+
117
+ event.preventDefault()
118
+ event.stopPropagation()
119
+ commit(values)
120
+
121
+ if (focusIndex !== index)
122
+ focusCell(root, focusIndex)
123
+ }
124
+
125
+ /**
126
+ * Gõ vào ô đang có chữ số khi dãy CHƯA đầy: chèn vào giữa và dồn phần đuôi
127
+ * sang phải, thay vì đè mất chữ số đang đứng ở đó.
128
+ *
129
+ * Dãy đã đầy thì để nguyên hành vi của reka (gõ đè lên đúng ô đó) — không còn
130
+ * chỗ mà dồn, và đè lên một ô là cách sửa một chữ số sai nhanh nhất.
131
+ */
132
+ function handleBeforeInputCapture(event: InputEvent): void {
133
+ if (event.inputType !== 'insertText')
134
+ return
135
+
136
+ const char = event.data
137
+ if (!char || char.length !== 1)
138
+ return
139
+
140
+ const root = event.currentTarget as HTMLElement
141
+ const index = cellIndexOf(event.target)
142
+ if (index < 0)
143
+ return
144
+
145
+ const values = currentValues()
146
+ // Ô trống thì không có gì phải dồn; dãy đầy thì không còn chỗ để dồn.
147
+ if (values[index] === '' || values.every(value => value !== ''))
148
+ return
149
+
150
+ event.preventDefault()
151
+ event.stopPropagation()
152
+ values.splice(index, 0, char)
153
+ commit(values)
154
+ focusCell(root, Math.min(index + 1, props.length - 1))
155
+ }
156
+ </script>
157
+
158
+ <template>
159
+ <PinInputRoot
160
+ v-model="model"
161
+ v-bind="forwarded"
162
+ :class="cn('flex items-center gap-2', props.class)"
163
+ @keydown.capture="handleKeydownCapture"
164
+ @beforeinput.capture="handleBeforeInputCapture"
165
+ >
166
+ <template v-for="index in cells" :key="index">
167
+ <PinInputInput
168
+ :index="index"
169
+ :data-index="index"
170
+ :class="cn(
171
+ 'size-10 rounded-md border border-input bg-background text-center text-base font-medium shadow-xs transition-colors',
172
+ // z-(--z-raised) để quầng focus nổi lên trên ô kế bên, không bị nó cắt mất.
173
+ 'focus-visible:z-(--z-raised)',
174
+ focusRing,
175
+ 'disabled:cursor-not-allowed disabled:opacity-50',
176
+ props.invalid && invalidRingStatic,
177
+ )"
178
+ />
179
+ <span
180
+ v-if="hasSeparatorAfter(index)"
181
+ class="text-muted-foreground"
182
+ aria-hidden="true"
183
+ >–</span>
184
+ </template>
185
+ </PinInputRoot>
186
+ </template>
@@ -0,0 +1 @@
1
+ export { default as InputOtp } from './InputOtp.vue'
@@ -0,0 +1,116 @@
1
+ <script setup lang="ts">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import { computed, ref } from 'vue'
4
+ import { useI18n } from '../../composables/useI18n'
5
+ import { Eye, EyeOff } from '../../lib/icons'
6
+ import { cn } from '../../lib/utils'
7
+ import Button from '../button/Button.vue'
8
+ import Input from '../input/Input.vue'
9
+ import { scoreOf, STRENGTH_LEVELS } from './index'
10
+
11
+ /**
12
+ * Gốc là một `<div>`, nên thuộc tính rơi tự do phải chuyển tay xuống `Input` —
13
+ * cùng lý do đã ghi ở `Input.vue`: `id` dính lên div thì `<label for>` mất đích.
14
+ */
15
+ defineOptions({ inheritAttrs: false })
16
+
17
+ const props = withDefaults(
18
+ defineProps<ClassProps & {
19
+ /** Nút con mắt. Tắt đi khi ô nằm trong một luồng không cho phép lộ giá trị. */
20
+ toggle?: boolean
21
+ /** Thanh đo độ mạnh. Chỉ bật cho ô ĐẶT mật khẩu, không bật cho ô đăng nhập. */
22
+ strength?: boolean
23
+ invalid?: boolean
24
+ size?: 'sm' | 'default'
25
+ /**
26
+ * Nhãn trợ năng cho nút con mắt ở hai trạng thái. Bỏ trống thì lấy chuỗi
27
+ * của thư viện — đừng đặt default ở đây, chuỗi trong `withDefaults` tính
28
+ * một lần lúc dựng component nên nó đóng băng ở một ngôn ngữ.
29
+ */
30
+ showLabel?: string
31
+ hideLabel?: string
32
+ }>(),
33
+ { toggle: true, strength: false, size: 'default' },
34
+ )
35
+
36
+ const model = defineModel<string>({ default: '' })
37
+
38
+ const { t } = useI18n()
39
+
40
+ const revealed = ref(false)
41
+
42
+ const toggleLabel = computed(() =>
43
+ revealed.value
44
+ ? props.hideLabel ?? t('ui.inputPassword.hide')
45
+ : props.showLabel ?? t('ui.inputPassword.show'),
46
+ )
47
+
48
+ const score = computed(() => (props.strength ? scoreOf(model.value) : 0))
49
+ const level = computed(() => STRENGTH_LEVELS[score.value])
50
+
51
+ /** Chừa chỗ cho nút, nếu không ký tự cuối chui xuống dưới nó. */
52
+ const inputClass = computed(() =>
53
+ props.toggle ? (props.size === 'sm' ? 'pr-8' : 'pr-9') : undefined,
54
+ )
55
+ </script>
56
+
57
+ <template>
58
+ <div :class="cn('w-full', props.class)">
59
+ <div class="relative">
60
+ <Input
61
+ v-bind="$attrs"
62
+ v-model="model"
63
+ :type="revealed ? 'text' : 'password'"
64
+ :size="props.size"
65
+ :invalid="props.invalid"
66
+ :class="inputClass"
67
+ />
68
+
69
+ <!--
70
+ `type="button"` là bắt buộc, không phải cho gọn.
71
+
72
+ Nút trần trong một `<form>` mặc định là `submit`, nên bấm vào con mắt
73
+ để đọc lại thứ mình vừa gõ sẽ GỬI luôn cái form. Không lỗi, không cảnh
74
+ báo — chỉ là biểu mẫu tự nộp.
75
+ -->
76
+ <Button
77
+ v-if="props.toggle"
78
+ type="button"
79
+ variant="ghost"
80
+ :size="props.size === 'sm' ? 'icon-xs' : 'icon-sm'"
81
+ :aria-label="toggleLabel"
82
+ :class="cn(
83
+ 'absolute top-1/2 -translate-y-1/2 text-muted-foreground',
84
+ props.size === 'sm' ? 'right-1' : 'right-1',
85
+ )"
86
+ @click="revealed = !revealed"
87
+ >
88
+ <EyeOff v-if="revealed" aria-hidden="true" />
89
+ <Eye v-else aria-hidden="true" />
90
+ </Button>
91
+ </div>
92
+
93
+ <!--
94
+ Thanh đo nằm trong vùng live, nhưng đọc ra là CHỮ chứ không phải con số.
95
+ Bốn ô màu thì trình đọc màn hình không thấy gì; và vì nhãn chỉ đổi khi
96
+ qua một nấc nên nó không lải nhải theo từng phím gõ.
97
+ -->
98
+ <div v-if="props.strength" class="mt-2">
99
+ <div class="flex gap-1" aria-hidden="true">
100
+ <span
101
+ v-for="step in STRENGTH_LEVELS.length - 1"
102
+ :key="step"
103
+ :class="cn(
104
+ 'h-1 flex-1 rounded-full transition-colors',
105
+ step <= score ? level.bar : 'bg-muted',
106
+ )"
107
+ />
108
+ </div>
109
+ <p role="status" aria-live="polite" class="mt-1.5 text-xs text-muted-foreground">
110
+ <template v-if="score > 0">
111
+ {{ t('ui.inputPassword.strength') }}: <span :class="level.text">{{ t(level.key) }}</span>
112
+ </template>
113
+ </p>
114
+ </div>
115
+ </div>
116
+ </template>
@@ -0,0 +1,5 @@
1
+ // Thang đo lên `src/variants` vì `STRENGTH_LEVELS` là một bảng class, và
2
+ // bản React đọc cùng bảng đó. Re-export để chỗ gọi không phải sửa import.
3
+ export { scoreOf, STRENGTH_LEVELS, type StrengthLevel } from '../../variants/components/input-password'
4
+
5
+ export { default as InputPassword } from './InputPassword.vue'
@@ -0,0 +1,38 @@
1
+ <script setup lang="ts">
2
+ import type { TagsInputRootEmits, TagsInputRootProps } from 'reka-ui'
3
+ import type { ClassProps } from '../../lib/types'
4
+ import { TagsInputRoot, useForwardPropsEmits } from 'reka-ui'
5
+ import { computed } from 'vue'
6
+ import { focusRingWithin, invalidRingWithin } from '../../lib/focus'
7
+ import { cn } from '../../lib/utils'
8
+
9
+ const props = defineProps<TagsInputRootProps & ClassProps>()
10
+ const emits = defineEmits<TagsInputRootEmits>()
11
+
12
+ const delegated = computed(() => {
13
+ const { class: _class, ...rest } = props
14
+ return rest
15
+ })
16
+
17
+ const forwarded = useForwardPropsEmits(delegated, emits)
18
+ </script>
19
+
20
+ <template>
21
+ <!--
22
+ Vỏ bọc bắt focus bằng `focus-within`, đúng khuôn của InputGroup và
23
+ InputNumber: ô nhập thật nằm bên trong nên chính nó bị lột sạch viền và vòng
24
+ focus, còn cái vỏ mới vẽ ra chúng. Để cả hai thì ra vòng focus lồng vòng focus.
25
+ -->
26
+ <TagsInputRoot
27
+ v-bind="forwarded"
28
+ :class="cn(
29
+ 'flex w-full flex-wrap items-center gap-1.5 rounded-md border border-input bg-background px-2 py-1.5 shadow-xs transition-colors',
30
+ focusRingWithin,
31
+ invalidRingWithin,
32
+ 'has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50',
33
+ props.class,
34
+ )"
35
+ >
36
+ <slot />
37
+ </TagsInputRoot>
38
+ </template>
@@ -0,0 +1,39 @@
1
+ <script setup lang="ts">
2
+ import type { TagsInputClearProps } from 'reka-ui'
3
+ import type { ClassProps } from '../../lib/types'
4
+ import { TagsInputClear } from 'reka-ui'
5
+ import { computed } from 'vue'
6
+ import { useI18n } from '../../composables/useI18n'
7
+ import { X } from '../../lib/icons'
8
+ import { cn } from '../../lib/utils'
9
+
10
+ const props = defineProps<TagsInputClearProps & ClassProps & {
11
+ /** Nhãn trợ năng. Bỏ trống thì lấy chuỗi mặc định của thư viện. */
12
+ label?: string
13
+ }>()
14
+
15
+ const { t } = useI18n()
16
+
17
+ const label = computed(() => props.label ?? t('ui.inputTags.clear'))
18
+
19
+ const delegated = computed(() => {
20
+ const { class: _class, label: _label, ...rest } = props
21
+ return rest
22
+ })
23
+ </script>
24
+
25
+ <template>
26
+ <TagsInputClear
27
+ v-bind="delegated"
28
+ :aria-label="label"
29
+ :class="cn(
30
+ 'grid size-5 shrink-0 place-items-center rounded-sm text-muted-foreground transition-colors',
31
+ 'hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
32
+ props.class,
33
+ )"
34
+ >
35
+ <slot>
36
+ <X class="size-3.5" />
37
+ </slot>
38
+ </TagsInputClear>
39
+ </template>
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ import type { TagsInputInputProps } from 'reka-ui'
3
+ import type { ClassProps } from '../../lib/types'
4
+ import { TagsInputInput } from 'reka-ui'
5
+ import { computed } from 'vue'
6
+ import { cn } from '../../lib/utils'
7
+
8
+ const props = defineProps<TagsInputInputProps & ClassProps>()
9
+
10
+ const delegated = computed(() => {
11
+ const { class: _class, ...rest } = props
12
+ return rest
13
+ })
14
+ </script>
15
+
16
+ <template>
17
+ <!-- Ô nhập bị lột sạch viền và vòng focus: cái vỏ đã vẽ chúng rồi.
18
+ `min-w-24` để nó không co về 0 khi hàng chip đã đầy. -->
19
+ <TagsInputInput
20
+ v-bind="delegated"
21
+ :class="cn(
22
+ 'h-6 min-w-24 flex-1 border-0 bg-transparent text-sm outline-none placeholder:text-muted-foreground',
23
+ 'disabled:cursor-not-allowed',
24
+ props.class,
25
+ )"
26
+ />
27
+ </template>
@@ -0,0 +1,40 @@
1
+ <script setup lang="ts">
2
+ import type { TagsInputItemProps } from 'reka-ui'
3
+ import type { ClassProps } from '../../lib/types'
4
+ import { TagsInputItem } from 'reka-ui'
5
+ import { computed } from 'vue'
6
+ import { cn } from '../../lib/utils'
7
+ import { badgeVariants } from '../badge'
8
+ import { chipVariants } from '../chip'
9
+
10
+ const props = defineProps<TagsInputItemProps & ClassProps>()
11
+
12
+ const delegated = computed(() => {
13
+ const { class: _class, ...rest } = props
14
+ return rest
15
+ })
16
+ </script>
17
+
18
+ <template>
19
+ <!--
20
+ Dáng lấy thẳng từ `Chip` — cùng hai bộ variant, không chép lại chuỗi class.
21
+ Không bọc chính `Chip` vào đây vì nút gỡ của nó và `TagsInputItemDelete` của
22
+ reka sẽ tranh nhau làm một việc, mà bản của reka mới là bản biết cách xoá
23
+ khỏi model và trả focus về ô nhập.
24
+
25
+ `data-state=active` là dòng mà bàn phím đang chọn (mũi tên trái/phải) —
26
+ trạng thái reka có còn Chip thì không.
27
+ -->
28
+ <TagsInputItem
29
+ v-bind="delegated"
30
+ :class="cn(
31
+ badgeVariants({ variant: 'secondary' }),
32
+ chipVariants({ size: 'sm', removable: true }),
33
+ 'data-[state=active]:ring-2 data-[state=active]:ring-ring',
34
+ 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
35
+ props.class,
36
+ )"
37
+ >
38
+ <slot />
39
+ </TagsInputItem>
40
+ </template>
@@ -0,0 +1,32 @@
1
+ <script setup lang="ts">
2
+ import type { TagsInputItemDeleteProps } from 'reka-ui'
3
+ import type { ClassProps } from '../../lib/types'
4
+ import { TagsInputItemDelete } from 'reka-ui'
5
+ import { computed } from 'vue'
6
+ import { X } from '../../lib/icons'
7
+ import { cn } from '../../lib/utils'
8
+
9
+ const props = defineProps<TagsInputItemDeleteProps & ClassProps>()
10
+
11
+ const delegated = computed(() => {
12
+ const { class: _class, ...rest } = props
13
+ return rest
14
+ })
15
+ </script>
16
+
17
+ <template>
18
+ <!-- `currentColor` để nút hợp với mọi variant của chip, kể cả nền đặc lẫn
19
+ nền soft — cùng cách `Chip` xử lý. -->
20
+ <TagsInputItemDelete
21
+ v-bind="delegated"
22
+ :class="cn(
23
+ 'grid size-4 shrink-0 place-items-center rounded-full text-current opacity-60 transition-opacity',
24
+ 'hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current',
25
+ props.class,
26
+ )"
27
+ >
28
+ <slot>
29
+ <X class="size-3" />
30
+ </slot>
31
+ </TagsInputItemDelete>
32
+ </template>