@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,472 @@
1
+ <script setup lang="ts">
2
+ import type { ClassProps } from '../../lib/types'
3
+ import type { VideoPlayerControl, VideoPlayerPlacement, VideoPlayerVolumeLayout } from './index'
4
+ import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
5
+ import { useI18n } from '../../composables/useI18n'
6
+ import {
7
+ Gauge,
8
+ Maximize,
9
+ Minimize,
10
+ Pause,
11
+ Play,
12
+ RotateCcw,
13
+ RotateCw,
14
+ Volume1,
15
+ Volume2,
16
+ VolumeX,
17
+ } from '../../lib/icons'
18
+ import { cn } from '../../lib/utils'
19
+ import { clampMediaTime, formatMediaTime, mediaVolumeLevel } from '../../variants/lib/media'
20
+ import { Button } from '../button'
21
+ import {
22
+ DropdownMenu,
23
+ DropdownMenuContent,
24
+ DropdownMenuLabel,
25
+ DropdownMenuRadioGroup,
26
+ DropdownMenuRadioItem,
27
+ DropdownMenuSeparator,
28
+ DropdownMenuTrigger,
29
+ } from '../dropdown-menu'
30
+ import { Slider } from '../slider'
31
+
32
+ const props = withDefaults(
33
+ defineProps<ClassProps & {
34
+ src: string
35
+ /**
36
+ * Ảnh bìa. Có bìa thì player mở ra với một nút chạy lớn giữa khung và
37
+ * THANH ĐIỀU KHIỂN ẨN cho tới lần chạy đầu — trước đó nó không có gì để nói.
38
+ */
39
+ poster?: string
40
+ /** Hiện trên thanh điều khiển, cắt bớt khi hẹp. */
41
+ title?: string
42
+ autoplay?: boolean
43
+ loop?: boolean
44
+ muted?: boolean
45
+ preload?: 'none' | 'metadata' | 'auto'
46
+ /** Mục nào hiện trên thanh. Mặc định là đủ cả bảy. */
47
+ controls?: VideoPlayerControl[]
48
+ placement?: VideoPlayerPlacement
49
+ volumeLayout?: VideoPlayerVolumeLayout
50
+ skipSeconds?: number
51
+ rates?: number[]
52
+ }>(),
53
+ {
54
+ autoplay: false,
55
+ loop: false,
56
+ muted: false,
57
+ preload: 'metadata',
58
+ controls: () => ['play', 'skip', 'seek', 'time', 'rate', 'volume', 'fullscreen'],
59
+ placement: 'overlay',
60
+ volumeLayout: 'inline',
61
+ skipSeconds: 10,
62
+ rates: () => [0.5, 0.75, 1, 1.25, 1.5, 2],
63
+ },
64
+ )
65
+
66
+ const emit = defineEmits<{
67
+ play: []
68
+ pause: []
69
+ ended: []
70
+ timeupdate: [seconds: number]
71
+ }>()
72
+
73
+ const { t } = useI18n()
74
+
75
+ /**
76
+ * Xin toàn màn hình cho CÁI VỎ, không cho thẻ `<video>`.
77
+ *
78
+ * Đưa chính thẻ video vào toàn màn hình thì trình duyệt thay bằng bộ điều khiển
79
+ * mặc định của nó và thanh của mình biến mất — đúng thứ component này đang dựng.
80
+ */
81
+ const shell = ref<HTMLElement>()
82
+ const video = ref<HTMLVideoElement>()
83
+
84
+ const playing = ref(false)
85
+ /**
86
+ * `started` KHÁC `playing`: nó chỉ bật một lần và không tắt lại.
87
+ *
88
+ * Dùng `playing` để ẩn tấm bìa thì bấm tạm dừng là bìa quay lại đè lên đúng
89
+ * khung hình người ta vừa dừng để nhìn.
90
+ */
91
+ const started = ref(false)
92
+ const currentTime = ref(0)
93
+ const duration = ref(0)
94
+ const volume = ref(1)
95
+ const muted = ref(props.muted)
96
+ const rate = ref('1')
97
+ const fullscreen = ref(false)
98
+
99
+ function has(control: VideoPlayerControl): boolean {
100
+ return props.controls.includes(control)
101
+ }
102
+
103
+ /** Bìa chỉ che khi chưa từng chạy; sau đó không bao giờ quay lại. */
104
+ const showPoster = computed(() => props.poster !== undefined && !started.value)
105
+ /** Có bìa thì thanh im lặng cho tới lần chạy đầu. */
106
+ const showBar = computed(() => props.controls.length > 0 && !showPoster.value)
107
+
108
+ const volumeIcon = computed(() => {
109
+ const level = mediaVolumeLevel(volume.value, muted.value)
110
+ if (level === 'off')
111
+ return VolumeX
112
+
113
+ return level === 'low' ? Volume1 : Volume2
114
+ })
115
+
116
+ /**
117
+ * Tắt thanh tua thì hai mốc thời gian gộp làm một ô `0:12 / 1:00`; bật thì
118
+ * chúng tách ra hai đầu thanh, đúng bố cục quen thuộc của một trình phát.
119
+ */
120
+ const splitTime = computed(() => has('seek'))
121
+
122
+ function toggle(): void {
123
+ const el = video.value
124
+ if (!el)
125
+ return
126
+
127
+ if (el.paused)
128
+ void el.play().catch(() => {})
129
+ else
130
+ el.pause()
131
+ }
132
+
133
+ function skip(seconds: number): void {
134
+ const el = video.value
135
+ if (!el)
136
+ return
137
+
138
+ el.currentTime = clampMediaTime(el.currentTime + seconds, el.duration)
139
+ }
140
+
141
+ function seek(value: number[] | undefined): void {
142
+ const el = video.value
143
+ if (!el || value?.[0] === undefined)
144
+ return
145
+
146
+ el.currentTime = clampMediaTime(value[0], el.duration)
147
+ }
148
+
149
+ /** Kéo âm lượng lên thì BỎ tắt tiếng luôn — không ai kéo thanh đó để vẫn im. */
150
+ function changeVolume(value: number[] | undefined): void {
151
+ const el = video.value
152
+ if (!el || value?.[0] === undefined)
153
+ return
154
+
155
+ el.volume = value[0]
156
+ el.muted = value[0] === 0
157
+ }
158
+
159
+ function toggleMute(): void {
160
+ const el = video.value
161
+ if (el)
162
+ el.muted = !el.muted
163
+ }
164
+
165
+ /**
166
+ * Nhận `unknown` chứ không phải `string`: radio group của Reka phát ra
167
+ * `AcceptableValue`, trong đó có cả `null`.
168
+ */
169
+ function changeRate(value: unknown): void {
170
+ if (typeof value !== 'string')
171
+ return
172
+
173
+ rate.value = value
174
+ if (video.value)
175
+ video.value.playbackRate = Number(value)
176
+ }
177
+
178
+ /**
179
+ * Nuốt lỗi là cố ý: `requestFullscreen()` bị từ chối khi tài liệu không có
180
+ * quyền — trong iframe thiếu `allowfullscreen` chẳng hạn — và một lỗi chưa bắt
181
+ * thì console đỏ lên trong khi người dùng chẳng có gì để làm.
182
+ */
183
+ function toggleFullscreen(): void {
184
+ if (document.fullscreenElement)
185
+ void document.exitFullscreen()
186
+ else
187
+ void shell.value?.requestFullscreen().catch(() => {})
188
+ }
189
+
190
+ function syncFullscreen(): void {
191
+ fullscreen.value = document.fullscreenElement === shell.value
192
+ }
193
+
194
+ function onTimeUpdate(event: Event): void {
195
+ currentTime.value = (event.target as HTMLVideoElement).currentTime
196
+ emit('timeupdate', currentTime.value)
197
+ }
198
+
199
+ function onVolumeChange(event: Event): void {
200
+ const el = event.target as HTMLVideoElement
201
+ volume.value = el.volume
202
+ muted.value = el.muted
203
+ }
204
+
205
+ function onPlay(): void {
206
+ started.value = true
207
+ playing.value = true
208
+ emit('play')
209
+ }
210
+
211
+ function onPause(): void {
212
+ playing.value = false
213
+ emit('pause')
214
+ }
215
+
216
+ /**
217
+ * Tự chạy bằng `play()` chứ KHÔNG bằng thuộc tính `autoplay`.
218
+ *
219
+ * Thuộc tính đó không hỏi được `prefers-reduced-motion`, mà một vòng lặp tự
220
+ * chạy đúng là thứ tuỳ chọn ấy sinh ra để tắt. `play()` còn trả về Promise nên
221
+ * bắt được lúc trình duyệt từ chối — im lặng ở đây là đúng, người dùng vẫn còn
222
+ * khung hình đầu để nhìn.
223
+ *
224
+ * Gọi lúc mount là ĐỦ, dù lúc đó `readyState` còn 0 và `currentSrc` còn rỗng.
225
+ * Có lúc tưởng không đủ và đã thêm một lượt gọi lại ở `canplay`; đo kỹ thì đó
226
+ * là lỗi PHÉP ĐO chứ không phải của component — tab đang ẩn
227
+ * (`document.visibilityState === 'hidden'`) thì Chrome cho `play` và `playing`
228
+ * bắn ra rồi tự phát `pause` sau 2ms, không qua JS nào và Promise vẫn resolve.
229
+ * Đưa tab ra trước là chạy đúng. Đã gỡ lượt gọi thừa đó.
230
+ */
231
+ function tryAutoplay(): void {
232
+ if (!props.autoplay || started.value)
233
+ return
234
+
235
+ if (window.matchMedia('(prefers-reduced-motion: reduce)').matches)
236
+ return
237
+
238
+ void video.value?.play().catch(() => {})
239
+ }
240
+
241
+ onMounted(() => {
242
+ document.addEventListener('fullscreenchange', syncFullscreen)
243
+ tryAutoplay()
244
+ })
245
+
246
+ onBeforeUnmount(() => document.removeEventListener('fullscreenchange', syncFullscreen))
247
+ </script>
248
+
249
+ <template>
250
+ <div
251
+ ref="shell"
252
+ :class="cn('relative overflow-hidden rounded-xl border border-border bg-card', props.class)"
253
+ >
254
+ <div class="relative aspect-video w-full bg-muted">
255
+ <video
256
+ ref="video"
257
+ :src="props.src"
258
+ :loop="props.loop"
259
+ :muted="props.muted"
260
+ :preload="props.preload"
261
+ class="h-full w-full object-cover"
262
+ playsinline
263
+ @click="toggle"
264
+ @play="onPlay"
265
+ @pause="onPause"
266
+ @ended="emit('ended')"
267
+ @timeupdate="onTimeUpdate"
268
+ @loadedmetadata="duration = ($event.target as HTMLVideoElement).duration"
269
+ @volumechange="onVolumeChange"
270
+ />
271
+
272
+ <!--
273
+ `<video>` phải TỰ ĐÓNG. Để nó mở thì mọi thứ viết sau trở thành con của
274
+ nó, mà con của `<video>` là nội dung DỰ PHÒNG — chỉ hiện khi trình duyệt
275
+ không phát được. Tức tấm bìa và slot sẽ không bao giờ vẽ ra, và không có
276
+ lỗi nào báo. Đã dính một lần; chính lần eslint --fix thụt lại dòng mới
277
+ làm nó lộ ra.
278
+ -->
279
+
280
+ <!-- Nội dung tự do đặt trên khung hình: tiêu đề hero, nhãn, huy hiệu… -->
281
+ <slot name="overlay" />
282
+
283
+ <!--
284
+ Bìa vẽ bằng `<img>` chứ không bằng thuộc tính `poster`. Thuộc tính đó
285
+ không tự tắt được bằng state, và filter đặt lên `<video>` để làm dịu bìa
286
+ ở chế độ tối sẽ dính cả khung hình lúc đang chạy.
287
+ -->
288
+ <div v-if="showPoster" class="absolute inset-0">
289
+ <img :src="props.poster" alt="" class="h-full w-full object-cover">
290
+
291
+ <div class="absolute inset-0 grid place-items-center">
292
+ <Button
293
+ size="icon-lg"
294
+ class="size-16 rounded-full [&_svg]:size-6"
295
+ :aria-label="t('ui.videoPlayer.play')"
296
+ @click="toggle"
297
+ >
298
+ <Play class="translate-x-0.5 fill-current" />
299
+ </Button>
300
+ </div>
301
+ </div>
302
+ </div>
303
+
304
+ <!--
305
+ MỘT thanh duy nhất cho cả hai kiểu đặt, đổi bằng class chứ không nhân đôi
306
+ markup: nó là con của vỏ `relative`, nên `absolute` thì nổi lên khung hình
307
+ còn tĩnh thì tự cộng thêm chiều cao bên dưới.
308
+
309
+ Nền `bg-background/85` chứ không `bg-black/60`: đó là mặt nền của theme
310
+ nên nó đi theo bảng màu, thay vì là mảng duy nhất đứng im khi đổi theme.
311
+ -->
312
+ <div
313
+ v-if="showBar"
314
+ :class="cn(
315
+ 'flex items-center gap-2',
316
+ props.placement === 'overlay'
317
+ ? 'absolute inset-x-2 bottom-2 rounded-lg border border-border bg-background/85 px-3 py-2 backdrop-blur-sm'
318
+ : 'border-t border-border px-3 py-2.5',
319
+ )"
320
+ >
321
+ <Button
322
+ v-if="has('skip')"
323
+ size="icon-sm"
324
+ variant="ghost"
325
+ :aria-label="t('ui.videoPlayer.skipBack', { seconds: props.skipSeconds })"
326
+ @click="skip(-props.skipSeconds)"
327
+ >
328
+ <RotateCcw />
329
+ </Button>
330
+
331
+ <Button
332
+ v-if="has('play')"
333
+ size="icon-sm"
334
+ variant="ghost"
335
+ :aria-label="playing ? t('ui.videoPlayer.pause') : t('ui.videoPlayer.play')"
336
+ @click="toggle"
337
+ >
338
+ <Pause v-if="playing" />
339
+ <Play v-else />
340
+ </Button>
341
+
342
+ <Button
343
+ v-if="has('skip')"
344
+ size="icon-sm"
345
+ variant="ghost"
346
+ :aria-label="t('ui.videoPlayer.skipForward', { seconds: props.skipSeconds })"
347
+ @click="skip(props.skipSeconds)"
348
+ >
349
+ <RotateCw />
350
+ </Button>
351
+
352
+ <p v-if="props.title" class="min-w-0 flex-1 truncate text-sm text-muted-foreground">
353
+ {{ props.title }}
354
+ </p>
355
+
356
+ <span
357
+ v-if="has('time')"
358
+ class="shrink-0 font-mono text-xs tabular-nums text-muted-foreground"
359
+ >
360
+ {{ formatMediaTime(currentTime) }}<template v-if="!splitTime"> / {{ formatMediaTime(duration) }}</template>
361
+ </span>
362
+
363
+ <Slider
364
+ v-if="has('seek')"
365
+ size="sm"
366
+ class="flex-1"
367
+ :model-value="[currentTime]"
368
+ :max="duration || 100"
369
+ :step="0.1"
370
+ :aria-label="t('ui.videoPlayer.seek')"
371
+ @update:model-value="seek"
372
+ />
373
+
374
+ <span
375
+ v-if="has('time') && splitTime"
376
+ class="shrink-0 font-mono text-xs tabular-nums text-muted-foreground"
377
+ >
378
+ {{ formatMediaTime(duration) }}
379
+ </span>
380
+
381
+ <DropdownMenu v-if="has('rate')">
382
+ <DropdownMenuTrigger as-child>
383
+ <Button
384
+ size="sm"
385
+ variant="ghost"
386
+ class="shrink-0 tabular-nums"
387
+ :aria-label="t('ui.videoPlayer.playbackSpeed')"
388
+ >
389
+ <Gauge />
390
+ {{ rate }}&times;
391
+ </Button>
392
+ </DropdownMenuTrigger>
393
+
394
+ <DropdownMenuContent align="end" class="w-36">
395
+ <DropdownMenuLabel>{{ t('ui.videoPlayer.playbackSpeed') }}</DropdownMenuLabel>
396
+ <DropdownMenuSeparator />
397
+ <DropdownMenuRadioGroup :model-value="rate" @update:model-value="changeRate">
398
+ <DropdownMenuRadioItem v-for="value in props.rates" :key="value" :value="String(value)">
399
+ {{ value }}&times;
400
+ </DropdownMenuRadioItem>
401
+ </DropdownMenuRadioGroup>
402
+ </DropdownMenuContent>
403
+ </DropdownMenu>
404
+
405
+ <div v-if="has('volume')" class="group/volume relative flex shrink-0 items-center gap-2">
406
+ <Button
407
+ size="icon-sm"
408
+ variant="ghost"
409
+ :aria-label="muted ? t('ui.videoPlayer.unmute') : t('ui.videoPlayer.mute')"
410
+ @click="toggleMute"
411
+ >
412
+ <component :is="volumeIcon" />
413
+ </Button>
414
+
415
+ <Slider
416
+ v-if="props.volumeLayout === 'inline'"
417
+ size="sm"
418
+ class="w-20"
419
+ :model-value="[muted ? 0 : volume]"
420
+ :max="1"
421
+ :step="0.01"
422
+ :aria-label="t('ui.videoPlayer.volume')"
423
+ @update:model-value="changeVolume"
424
+ />
425
+
426
+ <!--
427
+ Bảng dọc ẩn bằng `opacity-0` + `pointer-events-none`, KHÔNG `invisible`.
428
+ Đã đo: `visibility: hidden` loại phần tử khỏi thứ tự Tab y hệt
429
+ `display: none`, nên núm trượt không nhận được tiêu điểm và
430
+ `group-focus-within` không bao giờ xảy ra — vế bàn phím tự vô hiệu hoá
431
+ chính nó.
432
+
433
+ Khoảng hở 8px nằm ở `pb-2` của LỚP NGOÀI chứ không ở `mb-2`: hộp định
434
+ vị phải chạm vào nút, nếu không con trỏ đi qua khe sẽ rời cả nhóm và
435
+ bảng chớp tắt giữa đường.
436
+ -->
437
+ <div
438
+ v-else
439
+ class="pointer-events-none absolute bottom-full left-1/2 -translate-x-1/2 pb-2 opacity-0 transition-opacity duration-(--duration-base) group-focus-within/volume:pointer-events-auto group-focus-within/volume:opacity-100 group-hover/volume:pointer-events-auto group-hover/volume:opacity-100"
440
+ >
441
+ <div class="flex flex-col items-center gap-2 rounded-lg border border-border bg-popover p-3 shadow-md">
442
+ <Slider
443
+ size="sm"
444
+ orientation="vertical"
445
+ class="data-[orientation=vertical]:h-24"
446
+ :model-value="[muted ? 0 : volume]"
447
+ :max="1"
448
+ :step="0.01"
449
+ :aria-label="t('ui.videoPlayer.volume')"
450
+ @update:model-value="changeVolume"
451
+ />
452
+ <span class="font-mono text-xs tabular-nums text-muted-foreground">
453
+ {{ Math.round((muted ? 0 : volume) * 100) }}
454
+ </span>
455
+ </div>
456
+ </div>
457
+ </div>
458
+
459
+ <Button
460
+ v-if="has('fullscreen')"
461
+ size="icon-sm"
462
+ variant="ghost"
463
+ class="shrink-0"
464
+ :aria-label="fullscreen ? t('ui.videoPlayer.exitFullscreen') : t('ui.videoPlayer.enterFullscreen')"
465
+ @click="toggleFullscreen"
466
+ >
467
+ <Minimize v-if="fullscreen" />
468
+ <Maximize v-else />
469
+ </Button>
470
+ </div>
471
+ </div>
472
+ </template>
@@ -0,0 +1,20 @@
1
+ export { clampMediaTime, formatMediaTime, mediaVolumeLevel } from '../../variants/lib/media'
2
+
3
+ /**
4
+ * Một mục trên thanh điều khiển.
5
+ *
6
+ * Bật/tắt bằng CÁCH CÓ MẶT trong mảng `controls`, không phải bằng bảy prop
7
+ * boolean riêng: một mảng đọc ra ngay là player này có gì, còn bảy boolean thì
8
+ * phải đọc cả bảy mới biết. Thứ tự trong mảng không quyết định thứ tự hiển thị
9
+ * — bố cục thanh là cố định để tám cách cấu hình vẫn ra một sản phẩm nhìn giống
10
+ * nhau, đó là điểm của việc gom về một component.
11
+ */
12
+ export type VideoPlayerControl = 'play' | 'skip' | 'seek' | 'time' | 'rate' | 'volume' | 'fullscreen'
13
+
14
+ /** Thanh điều khiển nổi trên khung hình, hay nằm dưới nó. */
15
+ export type VideoPlayerPlacement = 'overlay' | 'below'
16
+
17
+ /** Thanh âm lượng nằm ngay cạnh nút loa, hay ẩn trong một bảng dọc. */
18
+ export type VideoPlayerVolumeLayout = 'inline' | 'popover'
19
+
20
+ export { default as VideoPlayer } from './VideoPlayer.vue'
@@ -0,0 +1,15 @@
1
+ export * from './useBase'
2
+ export * from './useChartPalette'
3
+ export * from './useColorMode'
4
+ export * from './useConfirm'
5
+ export * from './useDelayedFlag'
6
+ export * from './useI18n'
7
+ export * from './useIsMobile'
8
+ export * from './useMenuAccent'
9
+ export * from './useMenuAppearance'
10
+ export * from './useMenuColor'
11
+ export * from './usePalette'
12
+ export * from './useStyle'
13
+ export * from './useSurface'
14
+ export * from './useTableQuery'
15
+ export * from './useThemeController'
@@ -0,0 +1,32 @@
1
+ import type { BaseId, BaseOption } from '@vestow/theme'
2
+ import type { ComputedRef } from 'vue'
3
+ import { bases, themeController } from '@vestow/theme'
4
+ import { computed } from 'vue'
5
+ import { useThemeState } from './useThemeController'
6
+
7
+ export type { BaseId, BaseOption }
8
+ // Danh sách họ trung tính là DỮ LIỆU, ở `@vestow/theme`.
9
+ export { bases }
10
+
11
+ export interface UseBaseReturn {
12
+ base: ComputedRef<BaseId>
13
+ bases: readonly BaseOption[]
14
+ setBase: (value: BaseId) => void
15
+ }
16
+
17
+ /**
18
+ * Họ màu của nhóm trung tính. Vỏ bọc Vue quanh `themeController`, cùng khuôn
19
+ * với `usePalette` / `useStyle` / `useChartPalette`.
20
+ *
21
+ * Trục này chỉ đổi `--tint-hue` và `--tint-scale` — xem đầu `bases.css` để biết
22
+ * vì sao hai token đó là đủ cho cả nhóm trung tính.
23
+ */
24
+ export function useBase(): UseBaseReturn {
25
+ const state = useThemeState()
26
+
27
+ return {
28
+ base: computed(() => state.value.base),
29
+ bases,
30
+ setBase: themeController.setBase,
31
+ }
32
+ }
@@ -0,0 +1,33 @@
1
+ import type { ChartId, ChartOption } from '@vestow/theme'
2
+ import type { ComputedRef } from 'vue'
3
+ import { chartPalettes, themeController } from '@vestow/theme'
4
+ import { computed } from 'vue'
5
+ import { useThemeState } from './useThemeController'
6
+
7
+ export type { ChartId, ChartOption }
8
+ // Danh sách bộ màu biểu đồ là DỮ LIỆU, ở `@vestow/theme`. Re-export để chỗ
9
+ // gọi không phải nhớ nó nằm ở package nào.
10
+ export { chartPalettes }
11
+
12
+ export interface UseChartPaletteReturn {
13
+ chart: ComputedRef<ChartId>
14
+ chartPalettes: readonly ChartOption[]
15
+ setChart: (value: ChartId) => void
16
+ }
17
+
18
+ /**
19
+ * Bộ màu cho năm chuỗi dữ liệu của biểu đồ. Vỏ bọc Vue quanh `themeController`
20
+ * của `@vestow/theme`, cùng khuôn với `usePalette` / `useStyle`.
21
+ *
22
+ * Giá trị mặc định `theme` nghĩa là ĐI THEO bảng màu — controller gỡ hẳn
23
+ * `data-chart` khi ở giá trị đó, nên `palettes.css` lại là bên quyết định.
24
+ */
25
+ export function useChartPalette(): UseChartPaletteReturn {
26
+ const state = useThemeState()
27
+
28
+ return {
29
+ chart: computed(() => state.value.chart),
30
+ chartPalettes,
31
+ setChart: themeController.setChart,
32
+ }
33
+ }
@@ -0,0 +1,29 @@
1
+ import type { ColorMode } from '@vestow/theme'
2
+ import type { ComputedRef } from 'vue'
3
+ import { themeController } from '@vestow/theme'
4
+ import { computed } from 'vue'
5
+ import { useThemeState } from './useThemeController'
6
+
7
+ export type { ColorMode }
8
+
9
+ export interface UseColorModeReturn {
10
+ mode: ComputedRef<ColorMode>
11
+ isDark: ComputedRef<boolean>
12
+ setMode: (value: ColorMode) => void
13
+ toggle: () => void
14
+ }
15
+
16
+ /**
17
+ * Chế độ sáng/tối. Vỏ bọc Vue quanh `themeController` của `@vestow/theme` —
18
+ * xem `useThemeController.ts` để biết vì sao logic không nằm ở đây.
19
+ */
20
+ export function useColorMode(): UseColorModeReturn {
21
+ const state = useThemeState()
22
+
23
+ return {
24
+ mode: computed(() => state.value.colorMode),
25
+ isDark: computed(() => state.value.isDark),
26
+ setMode: themeController.setColorMode,
27
+ toggle: themeController.toggleColorMode,
28
+ }
29
+ }
@@ -0,0 +1,81 @@
1
+ import type { Ref, ShallowRef } from 'vue'
2
+ import type { ButtonVariants } from '../components/button'
3
+ import { ref, shallowRef } from 'vue'
4
+
5
+ export interface ConfirmOptions {
6
+ title: string
7
+ description?: string
8
+ /**
9
+ * Nhãn nút xác nhận — **bắt buộc**, và phải là ĐỘNG TỪ nói rõ việc sắp xảy
10
+ * ra: `'Xoá 12 tệp'`, không phải `'Xác nhận'`.
11
+ *
12
+ * Trước đây trường này có mặc định `'Xác nhận'`. Bỏ đi vì mặc định luôn là
13
+ * thứ được dùng nhiều nhất, mà `'Xác nhận'` không nói người dùng đang đồng ý
14
+ * với cái gì — đúng kiểu nhãn mà hộp thoại phá huỷ không được dùng. Bắt khai
15
+ * tường minh thì người viết buộc phải nghĩ ra câu đó một lần.
16
+ *
17
+ * `cancelLabel` thì vẫn có mặc định: nó là đường AN TOÀN, bấm nhầm không mất
18
+ * gì, nên `'Huỷ'` đủ rõ.
19
+ */
20
+ confirmLabel: string
21
+ cancelLabel?: string
22
+ /** destructive cho hành động xoá / thu hồi quyền */
23
+ variant?: ButtonVariants['variant']
24
+ }
25
+
26
+ interface ConfirmState extends ConfirmOptions {
27
+ resolve: (value: boolean) => void
28
+ }
29
+
30
+ // State ở module scope: chỉ có 1 ConfirmDialog trong toàn app.
31
+ const open = ref(false)
32
+ const state = shallowRef<ConfirmState | null>(null)
33
+
34
+ /**
35
+ * Hỏi xác nhận bằng Promise thay vì callback:
36
+ *
37
+ * if (await confirm({ title: 'Xoá người dùng?', variant: 'destructive' }))
38
+ * await api.deleteUser(id)
39
+ *
40
+ * Yêu cầu: đặt <ConfirmDialog /> một lần trong layout gốc.
41
+ */
42
+ export interface UseConfirmReturn {
43
+ confirm: (options: ConfirmOptions) => Promise<boolean>
44
+ }
45
+
46
+ export function useConfirm(): UseConfirmReturn {
47
+ function confirm(options: ConfirmOptions): Promise<boolean> {
48
+ // Nếu đang có 1 confirm mở, từ chối cái cũ để không treo Promise.
49
+ state.value?.resolve(false)
50
+
51
+ return new Promise<boolean>((resolve) => {
52
+ state.value = { ...options, resolve }
53
+ open.value = true
54
+ })
55
+ }
56
+
57
+ return { confirm }
58
+ }
59
+
60
+ /** Chỉ ConfirmDialog dùng — không phải API công khai. */
61
+ export interface ConfirmInternals {
62
+ open: Ref<boolean>
63
+ state: ShallowRef<ConfirmState | null>
64
+ settle: (result: boolean) => void
65
+ onOpenChange: (value: boolean) => void
66
+ }
67
+
68
+ export function useConfirmInternals(): ConfirmInternals {
69
+ function settle(result: boolean): void {
70
+ state.value?.resolve(result)
71
+ state.value = null
72
+ open.value = false
73
+ }
74
+
75
+ function onOpenChange(value: boolean): void {
76
+ if (!value)
77
+ settle(false)
78
+ }
79
+
80
+ return { open, state, settle, onOpenChange }
81
+ }