@brumaombra/ui-vintage 0.9.1 → 0.9.3

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 (344) hide show
  1. package/COMPONENTS.md +102 -2
  2. package/module.mjs +43 -43
  3. package/package.json +175 -175
  4. package/src/components/background-grid/BackgroundGrid.vue +4 -4
  5. package/src/components/blog/AllPostsSection.vue +33 -33
  6. package/src/components/blog/BlogCategoriesPage.vue +46 -0
  7. package/src/components/blog/BlogContentRenderer.vue +111 -111
  8. package/src/components/blog/BlogFAQSection.vue +19 -19
  9. package/src/components/blog/BlogHeaderSection.vue +80 -80
  10. package/src/components/blog/BlogIndexPage.vue +118 -0
  11. package/src/components/blog/BlogInfoSection.vue +47 -47
  12. package/src/components/blog/BlogPageBreadcrumbs.vue +43 -0
  13. package/src/components/blog/BlogPostHeader.vue +135 -135
  14. package/src/components/blog/BlogPostPage.vue +116 -0
  15. package/src/components/blog/BlogPostsPage.vue +74 -0
  16. package/src/components/blog/BlogProgressBar.vue +33 -33
  17. package/src/components/blog/BlogSectionTitle.vue +22 -22
  18. package/src/components/blog/BlogTagsPage.vue +46 -0
  19. package/src/components/blog/BlogTagsSection.vue +25 -25
  20. package/src/components/blog/BlogViewAllLink.vue +18 -0
  21. package/src/components/blog/CategoriesList.vue +14 -14
  22. package/src/components/blog/CategoriesSection.vue +11 -11
  23. package/src/components/blog/CategoryCard.vue +25 -25
  24. package/src/components/blog/HeaderCarousel.vue +97 -97
  25. package/src/components/blog/PostCard.vue +33 -33
  26. package/src/components/blog/PostsList.vue +15 -15
  27. package/src/components/blog/SocialShareSidebar.vue +84 -84
  28. package/src/components/blog/TableOfContents.vue +307 -307
  29. package/src/components/blog/blogPageUtils.ts +98 -0
  30. package/src/components/blog/index.ts +12 -1
  31. package/src/components/busy-indicator/BusyIndicator.vue +7 -7
  32. package/src/components/busy-indicator/busy-indicator-state.ts +56 -56
  33. package/src/components/card-grid/CardGrid.vue +34 -34
  34. package/src/components/chip/Chip.vue +15 -15
  35. package/src/components/confirm-dialog/ConfirmDialog.vue +50 -50
  36. package/src/components/confirm-dialog/confirm-dialog-state.ts +131 -131
  37. package/src/components/content/BlogList.vue +30 -30
  38. package/src/components/content/BlogTable.vue +36 -36
  39. package/src/components/content/CodeBlock.vue +136 -136
  40. package/src/components/content/Flow.vue +60 -60
  41. package/src/components/content/ProseHr.vue +15 -15
  42. package/src/components/content/ProsePre.vue +12 -12
  43. package/src/components/content/Terminal.vue +59 -59
  44. package/src/components/cookie-consent/CookieConsent.vue +36 -36
  45. package/src/components/cookie-consent/useCookieConsent.ts +37 -37
  46. package/src/components/dashboard-shell/DashboardShell.vue +171 -171
  47. package/src/components/data-list/DataList.vue +6 -6
  48. package/src/components/data-list/DataListItem.vue +4 -4
  49. package/src/components/data-list/DataListLabel.vue +4 -4
  50. package/src/components/data-list/DataListText.vue +4 -4
  51. package/src/components/data-list/DataListValue.vue +4 -4
  52. package/src/components/empty-state-card/EmptyStateCard.vue +40 -40
  53. package/src/components/error-page/ErrorPage.vue +72 -72
  54. package/src/components/info-card/InfoCard.vue +31 -31
  55. package/src/components/landing/landing-content/LandingContent.vue +4 -4
  56. package/src/components/landing/landing-footer/LandingFooter.vue +128 -128
  57. package/src/components/landing/landing-navbar/LandingNavbar.vue +50 -50
  58. package/src/components/landing/landing-shell/LandingShell.vue +36 -36
  59. package/src/components/language-flag/LanguageFlag.vue +25 -25
  60. package/src/components/language-flag/flags/ChineseFlag.vue +8 -8
  61. package/src/components/language-flag/flags/EnglishFlag.vue +7 -7
  62. package/src/components/language-flag/flags/FrenchFlag.vue +5 -5
  63. package/src/components/language-flag/flags/GermanFlag.vue +5 -5
  64. package/src/components/language-flag/flags/ItalianFlag.vue +7 -7
  65. package/src/components/language-flag/flags/JapaneseFlag.vue +4 -4
  66. package/src/components/language-flag/flags/PortugueseFlag.vue +57 -57
  67. package/src/components/language-flag/flags/RussianFlag.vue +5 -5
  68. package/src/components/language-flag/flags/SpanishFlag.vue +4 -4
  69. package/src/components/language-selector/LanguageSelector.vue +76 -76
  70. package/src/components/load-more-button/LoadMoreButton.vue +17 -17
  71. package/src/components/loading-state-card/LoadingStateCard.vue +28 -28
  72. package/src/components/message-dialog/MessageDialog.vue +59 -59
  73. package/src/components/message-dialog/message-dialog-state.ts +105 -105
  74. package/src/components/message-toast/MessageToast.vue +149 -149
  75. package/src/components/message-toast/message-toast-state.ts +146 -146
  76. package/src/components/page-header/PageHeader.vue +34 -34
  77. package/src/components/progress-component/ProgressComponent.vue +33 -33
  78. package/src/components/single-value-card/SingleValueCard.vue +57 -57
  79. package/src/components/slider-form-component/SliderFormComponent.vue +36 -36
  80. package/src/components/stat-strip/StatStrip.vue +22 -22
  81. package/src/components/switch-form-component/SwitchFormComponent.vue +20 -20
  82. package/src/components/text-link/TextLink.vue +14 -14
  83. package/src/components/theme-selector/ThemeSelector.vue +61 -61
  84. package/src/components/theme-selector/theme-transition.ts +63 -63
  85. package/src/components/ui/accordion/Accordion.vue +38 -38
  86. package/src/components/ui/alert/Alert.vue +5 -5
  87. package/src/components/ui/alert/AlertDescription.vue +4 -4
  88. package/src/components/ui/alert/AlertTitle.vue +4 -4
  89. package/src/components/ui/alert/index.ts +2 -2
  90. package/src/components/ui/alert-dialog/AlertDialog.vue +3 -3
  91. package/src/components/ui/alert-dialog/AlertDialogAction.vue +3 -3
  92. package/src/components/ui/alert-dialog/AlertDialogCancel.vue +3 -3
  93. package/src/components/ui/alert-dialog/AlertDialogContent.vue +7 -7
  94. package/src/components/ui/alert-dialog/AlertDialogDescription.vue +3 -3
  95. package/src/components/ui/alert-dialog/AlertDialogFooter.vue +4 -4
  96. package/src/components/ui/alert-dialog/AlertDialogHeader.vue +4 -4
  97. package/src/components/ui/alert-dialog/AlertDialogTitle.vue +3 -3
  98. package/src/components/ui/alert-dialog/AlertDialogTrigger.vue +3 -3
  99. package/src/components/ui/animated-number/AnimatedNumber.vue +49 -48
  100. package/src/components/ui/avatar/Avatar.vue +28 -28
  101. package/src/components/ui/avatar/AvatarFallback.vue +6 -6
  102. package/src/components/ui/avatar/AvatarGroup.vue +40 -40
  103. package/src/components/ui/avatar/AvatarImage.vue +3 -3
  104. package/src/components/ui/avatar/index.ts +17 -17
  105. package/src/components/ui/badge/Badge.vue +20 -20
  106. package/src/components/ui/breadcrumb/Breadcrumb.vue +4 -4
  107. package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +9 -9
  108. package/src/components/ui/breadcrumb/BreadcrumbItem.vue +4 -4
  109. package/src/components/ui/breadcrumb/BreadcrumbLink.vue +4 -4
  110. package/src/components/ui/breadcrumb/BreadcrumbList.vue +4 -4
  111. package/src/components/ui/breadcrumb/BreadcrumbPage.vue +4 -4
  112. package/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +4 -4
  113. package/src/components/ui/busy/Busy.vue +33 -33
  114. package/src/components/ui/button/Button.vue +23 -23
  115. package/src/components/ui/button/index.ts +56 -56
  116. package/src/components/ui/calendar/Calendar.vue +138 -138
  117. package/src/components/ui/calendar/CalendarCell.vue +7 -7
  118. package/src/components/ui/calendar/CalendarCellTrigger.vue +18 -18
  119. package/src/components/ui/calendar/CalendarGrid.vue +3 -3
  120. package/src/components/ui/calendar/CalendarGridBody.vue +3 -3
  121. package/src/components/ui/calendar/CalendarGridHead.vue +3 -3
  122. package/src/components/ui/calendar/CalendarGridRow.vue +3 -3
  123. package/src/components/ui/calendar/CalendarHeadCell.vue +7 -7
  124. package/src/components/ui/calendar/CalendarHeader.vue +7 -7
  125. package/src/components/ui/calendar/CalendarHeading.vue +6 -6
  126. package/src/components/ui/calendar/CalendarNextButton.vue +15 -15
  127. package/src/components/ui/calendar/CalendarPrevButton.vue +15 -15
  128. package/src/components/ui/calendar/index.ts +4 -4
  129. package/src/components/ui/card/Card.vue +20 -20
  130. package/src/components/ui/card/CardAction.vue +8 -8
  131. package/src/components/ui/card/CardContent.vue +4 -4
  132. package/src/components/ui/card/CardDescription.vue +12 -12
  133. package/src/components/ui/card/CardFooter.vue +4 -4
  134. package/src/components/ui/card/CardHeader.vue +8 -8
  135. package/src/components/ui/card/CardText.vue +8 -8
  136. package/src/components/ui/card/CardTitle.vue +8 -8
  137. package/src/components/ui/checkbox/Checkbox.vue +27 -27
  138. package/src/components/ui/collapsible/Collapsible.vue +3 -3
  139. package/src/components/ui/collapsible/CollapsibleContent.vue +3 -3
  140. package/src/components/ui/collapsible/CollapsibleTrigger.vue +3 -3
  141. package/src/components/ui/combobox/Combobox.vue +4 -4
  142. package/src/components/ui/combobox/ComboboxAnchor.vue +16 -16
  143. package/src/components/ui/combobox/ComboboxEmpty.vue +3 -3
  144. package/src/components/ui/combobox/ComboboxGroup.vue +9 -9
  145. package/src/components/ui/combobox/ComboboxInput.vue +6 -6
  146. package/src/components/ui/combobox/ComboboxItem.vue +17 -17
  147. package/src/components/ui/combobox/ComboboxList.vue +19 -19
  148. package/src/components/ui/combobox/ComboboxSelect.vue +77 -77
  149. package/src/components/ui/combobox/ComboboxSeparator.vue +1 -1
  150. package/src/components/ui/combobox/ComboboxTrigger.vue +10 -10
  151. package/src/components/ui/combobox/types.ts +5 -5
  152. package/src/components/ui/command/Command.vue +48 -48
  153. package/src/components/ui/command/CommandDialog.vue +14 -14
  154. package/src/components/ui/command/CommandEmpty.vue +4 -4
  155. package/src/components/ui/command/CommandGroup.vue +23 -23
  156. package/src/components/ui/command/CommandInput.vue +9 -9
  157. package/src/components/ui/command/CommandItem.vue +40 -40
  158. package/src/components/ui/command/CommandList.vue +9 -9
  159. package/src/components/ui/command/CommandSeparator.vue +3 -3
  160. package/src/components/ui/command/CommandShortcut.vue +8 -8
  161. package/src/components/ui/command/index.ts +11 -11
  162. package/src/components/ui/data-table/DataTable.vue +145 -145
  163. package/src/components/ui/data-table/index.ts +11 -11
  164. package/src/components/ui/date-picker/DatePicker.vue +31 -31
  165. package/src/components/ui/date-time-picker/DateTimePicker.vue +97 -97
  166. package/src/components/ui/dialog/Dialog.vue +3 -3
  167. package/src/components/ui/dialog/DialogClose.vue +3 -3
  168. package/src/components/ui/dialog/DialogContent.vue +21 -21
  169. package/src/components/ui/dialog/DialogDescription.vue +7 -7
  170. package/src/components/ui/dialog/DialogFooter.vue +8 -8
  171. package/src/components/ui/dialog/DialogHeader.vue +4 -4
  172. package/src/components/ui/dialog/DialogOverlay.vue +7 -7
  173. package/src/components/ui/dialog/DialogScrollContent.vue +32 -32
  174. package/src/components/ui/dialog/DialogTitle.vue +3 -3
  175. package/src/components/ui/dialog/DialogTrigger.vue +3 -3
  176. package/src/components/ui/dropdown-menu/DropdownMenu.vue +3 -3
  177. package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +11 -11
  178. package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +15 -15
  179. package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +3 -3
  180. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +12 -12
  181. package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +9 -9
  182. package/src/components/ui/dropdown-menu/DropdownMenuPortal.vue +3 -3
  183. package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +3 -3
  184. package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +11 -11
  185. package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +1 -1
  186. package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +4 -4
  187. package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +3 -3
  188. package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +15 -15
  189. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +15 -15
  190. package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +3 -3
  191. package/src/components/ui/dropdown-menu/classes.ts +16 -16
  192. package/src/components/ui/field/Field.vue +5 -5
  193. package/src/components/ui/field/FieldContent.vue +8 -8
  194. package/src/components/ui/field/FieldDescription.vue +10 -10
  195. package/src/components/ui/field/FieldError.vue +39 -39
  196. package/src/components/ui/field/FieldGroup.vue +8 -8
  197. package/src/components/ui/field/FieldLabel.vue +10 -10
  198. package/src/components/ui/field/FieldLegend.vue +11 -11
  199. package/src/components/ui/field/FieldSeparator.vue +11 -11
  200. package/src/components/ui/field/FieldSet.vue +9 -9
  201. package/src/components/ui/field/FieldTitle.vue +8 -8
  202. package/src/components/ui/field/field-context.ts +36 -36
  203. package/src/components/ui/field/index.ts +22 -22
  204. package/src/components/ui/file-dropzone/FileDropzone.vue +144 -144
  205. package/src/components/ui/file-dropzone/index.ts +19 -19
  206. package/src/components/ui/hover-card/HoverCard.vue +5 -5
  207. package/src/components/ui/hover-card/HoverCardContent.vue +13 -13
  208. package/src/components/ui/hover-card/HoverCardTrigger.vue +3 -3
  209. package/src/components/ui/input/Input.vue +13 -13
  210. package/src/components/ui/kbd/Kbd.vue +10 -10
  211. package/src/components/ui/kbd/KbdGroup.vue +49 -49
  212. package/src/components/ui/label/Label.vue +7 -7
  213. package/src/components/ui/native-select/NativeSelect.vue +16 -16
  214. package/src/components/ui/native-select/NativeSelectOptGroup.vue +3 -3
  215. package/src/components/ui/native-select/NativeSelectOption.vue +3 -3
  216. package/src/components/ui/number-field/NumberField.vue +88 -88
  217. package/src/components/ui/pagination/Pagination.vue +4 -4
  218. package/src/components/ui/pagination/PaginationContent.vue +4 -4
  219. package/src/components/ui/pagination/PaginationEllipsis.vue +9 -9
  220. package/src/components/ui/pagination/PaginationFirst.vue +8 -8
  221. package/src/components/ui/pagination/PaginationItem.vue +14 -14
  222. package/src/components/ui/pagination/PaginationLast.vue +8 -8
  223. package/src/components/ui/pagination/PaginationNext.vue +8 -8
  224. package/src/components/ui/pagination/PaginationPrevious.vue +8 -8
  225. package/src/components/ui/pagination/SimplePagination.vue +25 -25
  226. package/src/components/ui/pin-input/PinInput.vue +20 -20
  227. package/src/components/ui/pin-input/PinInputInput.vue +17 -17
  228. package/src/components/ui/pin-input/context.ts +1 -1
  229. package/src/components/ui/popover/Popover.vue +3 -3
  230. package/src/components/ui/popover/PopoverAnchor.vue +3 -3
  231. package/src/components/ui/popover/PopoverContent.vue +12 -12
  232. package/src/components/ui/popover/PopoverTrigger.vue +3 -3
  233. package/src/components/ui/progress/Progress.vue +16 -16
  234. package/src/components/ui/radio-group/RadioGroup.vue +7 -7
  235. package/src/components/ui/radio-group/RadioGroupCard.vue +39 -39
  236. package/src/components/ui/radio-group/RadioGroupItem.vue +20 -20
  237. package/src/components/ui/scroll-area/ScrollArea.vue +14 -14
  238. package/src/components/ui/scroll-area/ScrollBar.vue +12 -12
  239. package/src/components/ui/select/Select.vue +3 -3
  240. package/src/components/ui/select/SelectContent.vue +25 -25
  241. package/src/components/ui/select/SelectGroup.vue +3 -3
  242. package/src/components/ui/select/SelectItem.vue +24 -24
  243. package/src/components/ui/select/SelectItemContent.vue +32 -32
  244. package/src/components/ui/select/SelectItemText.vue +3 -3
  245. package/src/components/ui/select/SelectLabel.vue +3 -3
  246. package/src/components/ui/select/SelectScrollDownButton.vue +9 -9
  247. package/src/components/ui/select/SelectScrollUpButton.vue +9 -9
  248. package/src/components/ui/select/SelectSeparator.vue +2 -2
  249. package/src/components/ui/select/SelectTrigger.vue +14 -14
  250. package/src/components/ui/select/SelectValue.vue +3 -3
  251. package/src/components/ui/select/SelectValueContent.vue +39 -39
  252. package/src/components/ui/separator/Separator.vue +7 -7
  253. package/src/components/ui/sheet/Sheet.vue +3 -3
  254. package/src/components/ui/sheet/SheetClose.vue +3 -3
  255. package/src/components/ui/sheet/SheetContent.vue +22 -22
  256. package/src/components/ui/sheet/SheetDescription.vue +3 -3
  257. package/src/components/ui/sheet/SheetFooter.vue +3 -3
  258. package/src/components/ui/sheet/SheetHeader.vue +3 -3
  259. package/src/components/ui/sheet/SheetOverlay.vue +7 -7
  260. package/src/components/ui/sheet/SheetTitle.vue +3 -3
  261. package/src/components/ui/sheet/SheetTrigger.vue +3 -3
  262. package/src/components/ui/sidebar/Sidebar.vue +57 -57
  263. package/src/components/ui/sidebar/SidebarContent.vue +8 -8
  264. package/src/components/ui/sidebar/SidebarFooter.vue +8 -8
  265. package/src/components/ui/sidebar/SidebarGroup.vue +8 -8
  266. package/src/components/ui/sidebar/SidebarGroupAction.vue +9 -9
  267. package/src/components/ui/sidebar/SidebarGroupContent.vue +4 -4
  268. package/src/components/ui/sidebar/SidebarGroupLabel.vue +8 -8
  269. package/src/components/ui/sidebar/SidebarHeader.vue +8 -8
  270. package/src/components/ui/sidebar/SidebarInput.vue +8 -8
  271. package/src/components/ui/sidebar/SidebarInset.vue +9 -9
  272. package/src/components/ui/sidebar/SidebarMenu.vue +4 -4
  273. package/src/components/ui/sidebar/SidebarMenuAction.vue +18 -18
  274. package/src/components/ui/sidebar/SidebarMenuBadge.vue +14 -14
  275. package/src/components/ui/sidebar/SidebarMenuButton.vue +21 -21
  276. package/src/components/ui/sidebar/SidebarMenuButtonChild.vue +10 -10
  277. package/src/components/ui/sidebar/SidebarMenuItem.vue +4 -4
  278. package/src/components/ui/sidebar/SidebarMenuSkeleton.vue +10 -10
  279. package/src/components/ui/sidebar/SidebarMenuSub.vue +9 -9
  280. package/src/components/ui/sidebar/SidebarMenuSubButton.vue +17 -17
  281. package/src/components/ui/sidebar/SidebarMenuSubItem.vue +4 -4
  282. package/src/components/ui/sidebar/SidebarProvider.vue +46 -46
  283. package/src/components/ui/sidebar/SidebarRail.vue +13 -13
  284. package/src/components/ui/sidebar/SidebarSeparator.vue +4 -4
  285. package/src/components/ui/sidebar/SidebarTrigger.vue +5 -5
  286. package/src/components/ui/sidebar/index.ts +29 -29
  287. package/src/components/ui/sidebar/utils.ts +7 -7
  288. package/src/components/ui/skeleton/Skeleton.vue +6 -6
  289. package/src/components/ui/slider/Slider.vue +14 -14
  290. package/src/components/ui/spinner/Spinner.vue +21 -21
  291. package/src/components/ui/stepper/SimpleStepper.vue +26 -26
  292. package/src/components/ui/stepper/Stepper.vue +6 -6
  293. package/src/components/ui/stepper/StepperDescription.vue +5 -5
  294. package/src/components/ui/stepper/StepperIndicator.vue +17 -17
  295. package/src/components/ui/stepper/StepperItem.vue +4 -4
  296. package/src/components/ui/stepper/StepperSeparator.vue +7 -7
  297. package/src/components/ui/stepper/StepperTitle.vue +5 -5
  298. package/src/components/ui/stepper/StepperTrigger.vue +4 -4
  299. package/src/components/ui/stepper/index.ts +3 -3
  300. package/src/components/ui/switch/Switch.vue +15 -15
  301. package/src/components/ui/table/Table.vue +6 -6
  302. package/src/components/ui/table/TableBody.vue +4 -4
  303. package/src/components/ui/table/TableCaption.vue +4 -4
  304. package/src/components/ui/table/TableCell.vue +8 -8
  305. package/src/components/ui/table/TableEmpty.vue +14 -14
  306. package/src/components/ui/table/TableFooter.vue +8 -8
  307. package/src/components/ui/table/TableHead.vue +8 -8
  308. package/src/components/ui/table/TableHeader.vue +4 -4
  309. package/src/components/ui/table/TableRow.vue +8 -8
  310. package/src/components/ui/tabs/Tabs.vue +4 -4
  311. package/src/components/ui/tabs/TabsContent.vue +4 -4
  312. package/src/components/ui/tabs/TabsList.vue +15 -15
  313. package/src/components/ui/tabs/TabsTrigger.vue +10 -10
  314. package/src/components/ui/tags-input/TagsInput.vue +17 -17
  315. package/src/components/ui/tags-input/TagsInputInput.vue +6 -6
  316. package/src/components/ui/tags-input/TagsInputItem.vue +15 -15
  317. package/src/components/ui/tags-input/TagsInputItemDelete.vue +12 -12
  318. package/src/components/ui/tags-input/TagsInputItemText.vue +4 -4
  319. package/src/components/ui/textarea/Textarea.vue +12 -12
  320. package/src/components/ui/time-picker/TimePicker.vue +25 -25
  321. package/src/components/ui/toggle-group/Toggle.vue +10 -10
  322. package/src/components/ui/toggle-group/ToggleGroup.vue +38 -38
  323. package/src/components/ui/toggle-group/ToggleGroupItem.vue +9 -9
  324. package/src/components/ui/toggle-group/index.ts +46 -46
  325. package/src/components/ui/tooltip/Tooltip.vue +3 -3
  326. package/src/components/ui/tooltip/TooltipContent.vue +13 -13
  327. package/src/components/ui/tooltip/TooltipProvider.vue +4 -4
  328. package/src/components/ui/tooltip/TooltipTrigger.vue +3 -3
  329. package/src/i18n/de.json +206 -185
  330. package/src/i18n/en.json +206 -185
  331. package/src/i18n/es.json +206 -185
  332. package/src/i18n/fr.json +206 -185
  333. package/src/i18n/it.json +206 -185
  334. package/src/i18n/ja.json +206 -185
  335. package/src/i18n/pt.json +206 -185
  336. package/src/i18n/ru.json +206 -185
  337. package/src/i18n/zh.json +206 -185
  338. package/src/lib/color-tokens.ts +24 -24
  339. package/src/lib/i18n.ts +65 -65
  340. package/src/lib/utils.ts +33 -33
  341. package/src/nuxt-components.d.ts +2 -2
  342. package/src/runtime/ui-vintage-i18n-plugin.ts +49 -49
  343. package/src/shims-vue.d.ts +3 -3
  344. package/src/styles.css +377 -377
@@ -24,58 +24,58 @@ const { filterState, allItems, allGroups } = useCommand();
24
24
  const groupContext = useCommandGroup();
25
25
 
26
26
  const isRender = computed(() => {
27
- if (!filterState.search) {
28
- return true;
29
- } else {
30
- const filteredCurrentItem = filterState.filtered.items.get(id);
31
- // If the filtered items is undefined means not in the all times map yet
32
- // Do the first render to add into the map
33
- if (filteredCurrentItem === undefined) {
34
- return true;
35
- }
27
+ if (!filterState.search) {
28
+ return true;
29
+ } else {
30
+ const filteredCurrentItem = filterState.filtered.items.get(id);
31
+ // If the filtered items is undefined means not in the all times map yet
32
+ // Do the first render to add into the map
33
+ if (filteredCurrentItem === undefined) {
34
+ return true;
35
+ }
36
36
 
37
- // Check with filter
38
- return filteredCurrentItem > 0;
39
- }
37
+ // Check with filter
38
+ return filteredCurrentItem > 0;
39
+ }
40
40
  });
41
41
 
42
42
  const itemRef = ref();
43
43
  const currentElement = useCurrentElement(itemRef);
44
44
  onMounted(() => {
45
- if (!(currentElement.value instanceof HTMLElement)) return;
45
+ if (!(currentElement.value instanceof HTMLElement)) return;
46
46
 
47
- // Index both the rendered text and a string value (used as extra search keywords)
48
- const keywords = typeof props.value === 'string' ? props.value : '';
49
- allItems.value.set(
50
- id,
51
- `${currentElement.value.textContent ?? ''} ${keywords}`.trim(),
52
- );
47
+ // Index both the rendered text and a string value (used as extra search keywords)
48
+ const keywords = typeof props.value === 'string' ? props.value : '';
49
+ allItems.value.set(
50
+ id,
51
+ `${currentElement.value.textContent ?? ''} ${keywords}`.trim(),
52
+ );
53
53
 
54
- const groupId = groupContext?.id;
55
- if (groupId) {
56
- if (!allGroups.value.has(groupId)) {
57
- allGroups.value.set(groupId, new Set([id]));
58
- } else {
59
- allGroups.value.get(groupId)?.add(id);
60
- }
61
- }
54
+ const groupId = groupContext?.id;
55
+ if (groupId) {
56
+ if (!allGroups.value.has(groupId)) {
57
+ allGroups.value.set(groupId, new Set([id]));
58
+ } else {
59
+ allGroups.value.get(groupId)?.add(id);
60
+ }
61
+ }
62
62
  });
63
63
  onUnmounted(() => {
64
- allItems.value.delete(id);
64
+ allItems.value.delete(id);
65
65
  });
66
66
  </script>
67
67
 
68
68
  <template>
69
- <ListboxItem v-if="isRender" v-bind="forwarded" :id="id" ref="itemRef" data-slot="command-item" :class="cn(
70
- 'relative flex cursor-pointer items-center gap-3 rounded px-3 py-2 text-left text-sm font-semibold text-muted-foreground outline-hidden transition-colors duration-150 select-none data-highlighted:bg-accent data-highlighted:text-foreground data-[state=checked]:bg-accent data-[state=checked]:text-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
71
- 'before:absolute before:inset-y-1.5 before:left-0 before:w-0.5 before:scale-y-0 before:rounded-full before:bg-primary before:transition-transform before:duration-300 before:ease-spring data-highlighted:before:scale-y-100 data-[state=checked]:before:scale-y-100',
72
- '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 [&_svg:not([class*=\'text-\'])]:text-current',
73
- props.class,
74
- )" @select="
75
- () => {
76
- filterState.search = '';
77
- }
78
- ">
79
- <slot />
80
- </ListboxItem>
69
+ <ListboxItem v-if="isRender" v-bind="forwarded" :id="id" ref="itemRef" data-slot="command-item" :class="cn(
70
+ 'relative flex cursor-pointer items-center gap-3 rounded px-3 py-2 text-left text-sm font-semibold text-muted-foreground outline-hidden transition-colors duration-150 select-none data-highlighted:bg-accent data-highlighted:text-foreground data-[state=checked]:bg-accent data-[state=checked]:text-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
71
+ 'before:absolute before:inset-y-1.5 before:left-0 before:w-0.5 before:scale-y-0 before:rounded-full before:bg-primary before:transition-transform before:duration-300 before:ease-spring data-highlighted:before:scale-y-100 data-[state=checked]:before:scale-y-100',
72
+ '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 [&_svg:not([class*=\'text-\'])]:text-current',
73
+ props.class,
74
+ )" @select="
75
+ () => {
76
+ filterState.search = '';
77
+ }
78
+ ">
79
+ <slot />
80
+ </ListboxItem>
81
81
  </template>
@@ -20,13 +20,13 @@ const { t } = useI18n();
20
20
  </script>
21
21
 
22
22
  <template>
23
- <ListboxContent data-slot="command-list" :aria-label="t('uiVintage.commandDialog.title')" v-bind="forwarded" :class="cn(
24
- 'max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto',
25
- props.class,
26
- )
27
- ">
28
- <div role="presentation">
29
- <slot />
30
- </div>
31
- </ListboxContent>
23
+ <ListboxContent data-slot="command-list" :aria-label="t('uiVintage.commandDialog.title')" v-bind="forwarded" :class="cn(
24
+ 'max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto',
25
+ props.class,
26
+ )
27
+ ">
28
+ <div role="presentation">
29
+ <slot />
30
+ </div>
31
+ </ListboxContent>
32
32
  </template>
@@ -13,7 +13,7 @@ const delegatedProps = reactiveOmit(props, 'class');
13
13
  </script>
14
14
 
15
15
  <template>
16
- <Separator data-slot="command-separator" v-bind="delegatedProps" :class="cn('bg-border -mx-1 h-px', props.class)">
17
- <slot />
18
- </Separator>
16
+ <Separator data-slot="command-separator" v-bind="delegatedProps" :class="cn('bg-border -mx-1 h-px', props.class)">
17
+ <slot />
18
+ </Separator>
19
19
  </template>
@@ -4,16 +4,16 @@ import { cn } from '../../../lib/utils';
4
4
 
5
5
  // Props
6
6
  const props = defineProps<{
7
- class?: HTMLAttributes['class'];
7
+ class?: HTMLAttributes['class'];
8
8
  }>();
9
9
  </script>
10
10
 
11
11
  <template>
12
- <span data-slot="command-shortcut" :class="cn(
13
- 'text-muted-foreground ml-auto text-xs tracking-widest',
14
- props.class,
15
- )
16
- ">
17
- <slot />
18
- </span>
12
+ <span data-slot="command-shortcut" :class="cn(
13
+ 'text-muted-foreground ml-auto text-xs tracking-widest',
14
+ props.class,
15
+ )
16
+ ">
17
+ <slot />
18
+ </span>
19
19
  </template>
@@ -12,18 +12,18 @@ export { default as CommandSeparator } from './CommandSeparator.vue';
12
12
  export { default as CommandShortcut } from './CommandShortcut.vue';
13
13
 
14
14
  export const [useCommand, provideCommandContext] = createContext<{
15
- allItems: Ref<Map<string, string>>;
16
- allGroups: Ref<Map<string, Set<string>>>;
17
- filterState: {
18
- search: string;
19
- filtered: {
20
- count: number;
21
- items: Map<string, number>;
22
- groups: Set<string>;
23
- };
24
- };
15
+ allItems: Ref<Map<string, string>>;
16
+ allGroups: Ref<Map<string, Set<string>>>;
17
+ filterState: {
18
+ search: string;
19
+ filtered: {
20
+ count: number;
21
+ items: Map<string, number>;
22
+ groups: Set<string>;
23
+ };
24
+ };
25
25
  }>('Command');
26
26
 
27
27
  export const [useCommandGroup, provideCommandGroupContext] = createContext<{
28
- id?: string;
28
+ id?: string;
29
29
  }>('CommandGroup');
@@ -12,44 +12,44 @@ import type { DataTableColumn, DataTableSort } from '.';
12
12
 
13
13
  // Props
14
14
  const props = withDefaults(defineProps<{
15
- columns: DataTableColumn<T>[];
16
- rows: T[];
17
- rowKey?: keyof T | ((row: T) => PropertyKey);
18
- selectable?: boolean;
19
- selected?: PropertyKey[];
20
- sort?: DataTableSort | null;
21
- manualSort?: boolean;
22
- loading?: boolean;
23
- loadingRows?: number;
24
- emptyText?: string;
25
- rowClickable?: boolean;
26
- stickyHeader?: boolean;
27
- class?: HTMLAttributes['class'];
15
+ columns: DataTableColumn<T>[];
16
+ rows: T[];
17
+ rowKey?: keyof T | ((row: T) => PropertyKey);
18
+ selectable?: boolean;
19
+ selected?: PropertyKey[];
20
+ sort?: DataTableSort | null;
21
+ manualSort?: boolean;
22
+ loading?: boolean;
23
+ loadingRows?: number;
24
+ emptyText?: string;
25
+ rowClickable?: boolean;
26
+ stickyHeader?: boolean;
27
+ class?: HTMLAttributes['class'];
28
28
  }>(), {
29
- rowKey: 'id' as never,
30
- selectable: false,
31
- selected: () => [],
32
- sort: null,
33
- manualSort: false,
34
- loading: false,
35
- loadingRows: 5,
36
- emptyText: '',
37
- rowClickable: false,
38
- stickyHeader: false
29
+ rowKey: 'id' as never,
30
+ selectable: false,
31
+ selected: () => [],
32
+ sort: null,
33
+ manualSort: false,
34
+ loading: false,
35
+ loadingRows: 5,
36
+ emptyText: '',
37
+ rowClickable: false,
38
+ stickyHeader: false
39
39
  });
40
40
 
41
41
  // Emits
42
42
  const emits = defineEmits<{
43
- 'update:selected': [value: PropertyKey[]];
44
- 'update:sort': [value: DataTableSort | null];
45
- 'row-click': [row: T, event: MouseEvent | KeyboardEvent];
43
+ 'update:selected': [value: PropertyKey[]];
44
+ 'update:sort': [value: DataTableSort | null];
45
+ 'row-click': [row: T, event: MouseEvent | KeyboardEvent];
46
46
  }>();
47
47
 
48
48
  // Slots
49
49
  defineSlots<{
50
- [key: `cell-${string}`]: (props: { row: T; value: unknown; index: number }) => unknown;
51
- [key: `header-${string}`]: (props: { column: DataTableColumn<T> }) => unknown;
52
- empty?: () => unknown;
50
+ [key: `cell-${string}`]: (props: { row: T; value: unknown; index: number }) => unknown;
51
+ [key: `header-${string}`]: (props: { column: DataTableColumn<T> }) => unknown;
52
+ empty?: () => unknown;
53
53
  }>();
54
54
 
55
55
  const { t } = useI18n();
@@ -57,7 +57,7 @@ const resolvedEmptyText = computed(() => props.emptyText || t('uiVintage.common.
57
57
 
58
58
  // Resolve a stable key for each row
59
59
  const getRowKey = (row: T): PropertyKey => {
60
- return typeof props.rowKey === 'function' ? props.rowKey(row) : row[props.rowKey as keyof T] as PropertyKey;
60
+ return typeof props.rowKey === 'function' ? props.rowKey(row) : row[props.rowKey as keyof T] as PropertyKey;
61
61
  };
62
62
 
63
63
  // Read the raw value of a cell
@@ -65,39 +65,39 @@ const getCellValue = (row: T, column: DataTableColumn<T>) => row[column.key];
65
65
 
66
66
  // Format a cell for display when no slot is provided
67
67
  const formatCell = (row: T, column: DataTableColumn<T>) => {
68
- const value = getCellValue(row, column);
69
- return column.format ? column.format(value, row) : value ?? '';
68
+ const value = getCellValue(row, column);
69
+ return column.format ? column.format(value, row) : value ?? '';
70
70
  };
71
71
 
72
72
  // Sort rows on the client unless sorting is delegated to the parent
73
73
  const sortedRows = computed(() => {
74
- if (!props.sort || props.manualSort) return props.rows;
75
- const column = props.columns.find(item => item.key === props.sort?.key);
76
- if (!column) return props.rows;
77
- const direction = props.sort.direction === 'asc' ? 1 : -1;
78
- const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
79
-
80
- // Compare values with the column accessor (strings use natural ordering)
81
- return [...props.rows].sort((first, second) => {
82
- const firstValue = column.sortValue ? column.sortValue(first) : getCellValue(first, column);
83
- const secondValue = column.sortValue ? column.sortValue(second) : getCellValue(second, column);
84
- if (firstValue == null) return 1;
85
- if (secondValue == null) return -1;
86
- if (typeof firstValue === 'number' && typeof secondValue === 'number') return (firstValue - secondValue) * direction;
87
- return collator.compare(String(firstValue), String(secondValue)) * direction;
88
- });
74
+ if (!props.sort || props.manualSort) return props.rows;
75
+ const column = props.columns.find(item => item.key === props.sort?.key);
76
+ if (!column) return props.rows;
77
+ const direction = props.sort.direction === 'asc' ? 1 : -1;
78
+ const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
79
+
80
+ // Compare values with the column accessor (strings use natural ordering)
81
+ return [...props.rows].sort((first, second) => {
82
+ const firstValue = column.sortValue ? column.sortValue(first) : getCellValue(first, column);
83
+ const secondValue = column.sortValue ? column.sortValue(second) : getCellValue(second, column);
84
+ if (firstValue == null) return 1;
85
+ if (secondValue == null) return -1;
86
+ if (typeof firstValue === 'number' && typeof secondValue === 'number') return (firstValue - secondValue) * direction;
87
+ return collator.compare(String(firstValue), String(secondValue)) * direction;
88
+ });
89
89
  });
90
90
 
91
91
  // Cycle a column through ascending, descending, and unsorted
92
92
  const toggleSort = (column: DataTableColumn<T>) => {
93
- if (!column.sortable) return;
94
- if (props.sort?.key !== column.key) {
95
- emits('update:sort', { key: column.key, direction: 'asc' });
96
- } else if (props.sort.direction === 'asc') {
97
- emits('update:sort', { key: column.key, direction: 'desc' });
98
- } else {
99
- emits('update:sort', null);
100
- }
93
+ if (!column.sortable) return;
94
+ if (props.sort?.key !== column.key) {
95
+ emits('update:sort', { key: column.key, direction: 'asc' });
96
+ } else if (props.sort.direction === 'asc') {
97
+ emits('update:sort', { key: column.key, direction: 'desc' });
98
+ } else {
99
+ emits('update:sort', null);
100
+ }
101
101
  };
102
102
 
103
103
  // Selection helpers
@@ -108,19 +108,19 @@ const headerCheckboxState = computed(() => allSelected.value ? true : someSelect
108
108
 
109
109
  // Toggle all rows
110
110
  const toggleAll = () => {
111
- emits('update:selected', allSelected.value ? [] : props.rows.map(getRowKey));
111
+ emits('update:selected', allSelected.value ? [] : props.rows.map(getRowKey));
112
112
  };
113
113
 
114
114
  // Toggle a single row
115
115
  const toggleRow = (row: T) => {
116
- const key = getRowKey(row);
117
- const next = new Set(props.selected);
118
- if (next.has(key)) {
119
- next.delete(key);
120
- } else {
121
- next.add(key);
122
- }
123
- emits('update:selected', [...next]);
116
+ const key = getRowKey(row);
117
+ const next = new Set(props.selected);
118
+ if (next.has(key)) {
119
+ next.delete(key);
120
+ } else {
121
+ next.add(key);
122
+ }
123
+ emits('update:selected', [...next]);
124
124
  };
125
125
 
126
126
  // Alignment classes for header and body cells
@@ -130,110 +130,110 @@ const columnCount = computed(() => props.columns.length + (props.selectable ? 1
130
130
 
131
131
  // Emit row clicks (ignoring clicks on interactive children)
132
132
  const handleRowClick = (row: T, event: MouseEvent) => {
133
- if ((event.target as HTMLElement).closest('button, a, input, [role=checkbox]')) return;
134
- emits('row-click', row, event);
133
+ if ((event.target as HTMLElement).closest('button, a, input, [role=checkbox]')) return;
134
+ emits('row-click', row, event);
135
135
  };
136
136
 
137
137
  // Let keyboard users activate a focused clickable row with Enter or Space
138
138
  const handleRowKeydown = (row: T, event: KeyboardEvent) => {
139
- if (!props.rowClickable || event.target !== event.currentTarget) return;
140
- if (event.key !== 'Enter' && event.key !== ' ') return;
141
- event.preventDefault();
142
- emits('row-click', row, event);
139
+ if (!props.rowClickable || event.target !== event.currentTarget) return;
140
+ if (event.key !== 'Enter' && event.key !== ' ') return;
141
+ event.preventDefault();
142
+ emits('row-click', row, event);
143
143
  };
144
144
  </script>
145
145
 
146
146
  <template>
147
- <div data-slot="data-table" :class="cn('relative w-full overflow-auto rounded border border-border bg-card shadow-elevated-sm', props.class)">
148
- <table class="w-full caption-bottom border-collapse text-sm">
149
- <!-- Header -->
150
- <thead :class="cn('bg-surface/80 backdrop-blur', props.stickyHeader && 'sticky top-0 z-10')">
151
- <tr class="border-b border-border">
152
- <!-- Select all -->
153
- <th v-if="props.selectable" class="w-12 px-4 py-3">
154
- <Checkbox :model-value="headerCheckboxState" :aria-label="t('uiVintage.dataTable.selectAll')" @update:model-value="toggleAll" />
155
- </th>
156
-
157
- <!-- Column headers -->
158
- <th v-for="column in props.columns" :key="column.key" :style="column.width ? { width: column.width } : undefined" :aria-sort="props.sort?.key === column.key ? (props.sort.direction === 'asc' ? 'ascending' : 'descending') : undefined" :class="cn('h-11 px-4 text-xs font-semibold whitespace-nowrap text-muted-foreground', alignClasses[column.align ?? 'left'], column.headerClass)">
159
- <slot :name="`header-${column.key}`" :column="column">
160
- <button v-if="column.sortable" type="button" :class="cn('group/sort -mx-1.5 inline-flex cursor-pointer items-center gap-1.5 rounded-sm px-1.5 py-1 outline-none transition-colors duration-150 hover:bg-accent hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/45', props.sort?.key === column.key && 'text-foreground', justifyClasses[column.align ?? 'left'])" @click="toggleSort(column)">
161
- {{ column.label }}
162
- <HugeiconsIcon :icon="ArrowUp01Icon" :class="cn('size-3.5 transition-[rotate,opacity,color] duration-300 ease-spring', props.sort?.key === column.key ? 'text-primary opacity-100' : 'opacity-0 group-hover/sort:opacity-50', props.sort?.key === column.key && props.sort.direction === 'desc' && 'rotate-180')" />
163
- </button>
164
- <template v-else>
165
- {{ column.label }}
166
- </template>
167
- </slot>
168
- </th>
169
- </tr>
170
- </thead>
171
-
172
- <!-- Loading rows -->
173
- <tbody v-if="props.loading">
174
- <tr v-for="index in props.loadingRows" :key="`loading-${index}`" class="border-b border-border last:border-b-0">
175
- <td v-if="props.selectable" class="px-4 py-3">
176
- <Skeleton class="size-5" />
177
- </td>
178
- <td v-for="column in props.columns" :key="column.key" class="px-4 py-3">
179
- <Skeleton class="h-4" :style="{ width: `${45 + ((index * 17 + column.key.length * 11) % 45)}%` }" />
180
- </td>
181
- </tr>
182
- </tbody>
183
-
184
- <!-- Empty state -->
185
- <tbody v-else-if="sortedRows.length === 0">
186
- <tr>
187
- <td :colspan="columnCount" class="px-4 py-12">
188
- <slot name="empty">
189
- <div class="flex flex-col items-center gap-2 text-center text-muted-foreground">
190
- <HugeiconsIcon :icon="PackageIcon" class="size-8 opacity-40" />
191
- <span class="text-xs sm:text-sm">{{ resolvedEmptyText }}</span>
192
- </div>
193
- </slot>
194
- </td>
195
- </tr>
196
- </tbody>
197
-
198
- <!-- Rows (animated when sorted, added, or removed) -->
199
- <TransitionGroup v-else tag="tbody" name="uv-data-table-row">
200
- <tr v-for="(row, index) in sortedRows" :key="getRowKey(row)" :data-state="selectedSet.has(getRowKey(row)) ? 'selected' : undefined" :class="cn('border-b border-border transition-colors duration-150 last:border-b-0 hover:bg-surface/70 data-[state=selected]:bg-primary/5', props.rowClickable && 'cursor-pointer focus-visible:bg-surface/70 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring')" :tabindex="props.rowClickable ? 0 : undefined" @click="handleRowClick(row, $event)" @keydown="handleRowKeydown(row, $event)">
201
- <!-- Row selection -->
202
- <td v-if="props.selectable" class="px-4 py-3">
203
- <Checkbox :model-value="selectedSet.has(getRowKey(row))" :aria-label="t('uiVintage.dataTable.selectRow')" @update:model-value="toggleRow(row)" />
204
- </td>
205
-
206
- <!-- Cells -->
207
- <td v-for="column in props.columns" :key="column.key" :class="cn('px-4 py-3 align-middle text-xs whitespace-nowrap md:text-sm', alignClasses[column.align ?? 'left'], column.class)">
208
- <slot :name="`cell-${column.key}`" :row="row" :value="getCellValue(row, column)" :index="index">
209
- {{ formatCell(row, column) }}
210
- </slot>
211
- </td>
212
- </tr>
213
- </TransitionGroup>
214
- </table>
215
- </div>
147
+ <div data-slot="data-table" :class="cn('relative w-full overflow-auto rounded border border-border bg-card shadow-elevated-sm', props.class)">
148
+ <table class="w-full caption-bottom border-collapse text-sm">
149
+ <!-- Header -->
150
+ <thead :class="cn('bg-surface/80 backdrop-blur', props.stickyHeader && 'sticky top-0 z-10')">
151
+ <tr class="border-b border-border">
152
+ <!-- Select all -->
153
+ <th v-if="props.selectable" class="w-12 px-4 py-3">
154
+ <Checkbox :model-value="headerCheckboxState" :aria-label="t('uiVintage.dataTable.selectAll')" @update:model-value="toggleAll" />
155
+ </th>
156
+
157
+ <!-- Column headers -->
158
+ <th v-for="column in props.columns" :key="column.key" :style="column.width ? { width: column.width } : undefined" :aria-sort="props.sort?.key === column.key ? (props.sort.direction === 'asc' ? 'ascending' : 'descending') : undefined" :class="cn('h-11 px-4 text-xs font-semibold whitespace-nowrap text-muted-foreground', alignClasses[column.align ?? 'left'], column.headerClass)">
159
+ <slot :name="`header-${column.key}`" :column="column">
160
+ <button v-if="column.sortable" type="button" :class="cn('group/sort -mx-1.5 inline-flex cursor-pointer items-center gap-1.5 rounded-sm px-1.5 py-1 outline-none transition-colors duration-150 hover:bg-accent hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/45', props.sort?.key === column.key && 'text-foreground', justifyClasses[column.align ?? 'left'])" @click="toggleSort(column)">
161
+ {{ column.label }}
162
+ <HugeiconsIcon :icon="ArrowUp01Icon" :class="cn('size-3.5 transition-[rotate,opacity,color] duration-300 ease-spring', props.sort?.key === column.key ? 'text-primary opacity-100' : 'opacity-0 group-hover/sort:opacity-50', props.sort?.key === column.key && props.sort.direction === 'desc' && 'rotate-180')" />
163
+ </button>
164
+ <template v-else>
165
+ {{ column.label }}
166
+ </template>
167
+ </slot>
168
+ </th>
169
+ </tr>
170
+ </thead>
171
+
172
+ <!-- Loading rows -->
173
+ <tbody v-if="props.loading">
174
+ <tr v-for="index in props.loadingRows" :key="`loading-${index}`" class="border-b border-border last:border-b-0">
175
+ <td v-if="props.selectable" class="px-4 py-3">
176
+ <Skeleton class="size-5" />
177
+ </td>
178
+ <td v-for="column in props.columns" :key="column.key" class="px-4 py-3">
179
+ <Skeleton class="h-4" :style="{ width: `${45 + ((index * 17 + column.key.length * 11) % 45)}%` }" />
180
+ </td>
181
+ </tr>
182
+ </tbody>
183
+
184
+ <!-- Empty state -->
185
+ <tbody v-else-if="sortedRows.length === 0">
186
+ <tr>
187
+ <td :colspan="columnCount" class="px-4 py-12">
188
+ <slot name="empty">
189
+ <div class="flex flex-col items-center gap-2 text-center text-muted-foreground">
190
+ <HugeiconsIcon :icon="PackageIcon" class="size-8 opacity-40" />
191
+ <span class="text-xs sm:text-sm">{{ resolvedEmptyText }}</span>
192
+ </div>
193
+ </slot>
194
+ </td>
195
+ </tr>
196
+ </tbody>
197
+
198
+ <!-- Rows (animated when sorted, added, or removed) -->
199
+ <TransitionGroup v-else tag="tbody" name="uv-data-table-row">
200
+ <tr v-for="(row, index) in sortedRows" :key="getRowKey(row)" :data-state="selectedSet.has(getRowKey(row)) ? 'selected' : undefined" :class="cn('border-b border-border transition-colors duration-150 last:border-b-0 hover:bg-surface/70 data-[state=selected]:bg-primary/5', props.rowClickable && 'cursor-pointer focus-visible:bg-surface/70 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring')" :tabindex="props.rowClickable ? 0 : undefined" @click="handleRowClick(row, $event)" @keydown="handleRowKeydown(row, $event)">
201
+ <!-- Row selection -->
202
+ <td v-if="props.selectable" class="px-4 py-3">
203
+ <Checkbox :model-value="selectedSet.has(getRowKey(row))" :aria-label="t('uiVintage.dataTable.selectRow')" @update:model-value="toggleRow(row)" />
204
+ </td>
205
+
206
+ <!-- Cells -->
207
+ <td v-for="column in props.columns" :key="column.key" :class="cn('px-4 py-3 align-middle text-xs whitespace-nowrap md:text-sm', alignClasses[column.align ?? 'left'], column.class)">
208
+ <slot :name="`cell-${column.key}`" :row="row" :value="getCellValue(row, column)" :index="index">
209
+ {{ formatCell(row, column) }}
210
+ </slot>
211
+ </td>
212
+ </tr>
213
+ </TransitionGroup>
214
+ </table>
215
+ </div>
216
216
  </template>
217
217
 
218
218
  <style>
219
219
  .uv-data-table-row-move {
220
- transition: transform 0.45s var(--ease-spring);
220
+ transition: transform 0.45s var(--ease-spring);
221
221
  }
222
222
 
223
223
  .uv-data-table-row-enter-active {
224
- transition: opacity 0.3s var(--ease-out-expo), transform 0.4s var(--ease-spring);
224
+ transition: opacity 0.3s var(--ease-out-expo), transform 0.4s var(--ease-spring);
225
225
  }
226
226
 
227
227
  .uv-data-table-row-leave-active {
228
- transition: opacity 0.18s var(--ease-snappy);
228
+ transition: opacity 0.18s var(--ease-snappy);
229
229
  }
230
230
 
231
231
  .uv-data-table-row-enter-from {
232
- opacity: 0;
233
- transform: translateY(-6px);
232
+ opacity: 0;
233
+ transform: translateY(-6px);
234
234
  }
235
235
 
236
236
  .uv-data-table-row-leave-to {
237
- opacity: 0;
237
+ opacity: 0;
238
238
  }
239
239
  </style>
@@ -3,18 +3,18 @@ export { default as DataTable } from './DataTable.vue';
3
3
  export type DataTableSortDirection = 'asc' | 'desc';
4
4
 
5
5
  export interface DataTableSort {
6
- key: string;
7
- direction: DataTableSortDirection;
6
+ key: string;
7
+ direction: DataTableSortDirection;
8
8
  }
9
9
 
10
10
  export interface DataTableColumn<T> {
11
- key: string;
12
- label: string;
13
- sortable?: boolean;
14
- align?: 'left' | 'center' | 'right';
15
- width?: string;
16
- class?: string;
17
- headerClass?: string;
18
- format?: (value: unknown, row: T) => string;
19
- sortValue?: (row: T) => string | number | null | undefined;
11
+ key: string;
12
+ label: string;
13
+ sortable?: boolean;
14
+ align?: 'left' | 'center' | 'right';
15
+ width?: string;
16
+ class?: string;
17
+ headerClass?: string;
18
+ format?: (value: unknown, row: T) => string;
19
+ sortValue?: (row: T) => string | number | null | undefined;
20
20
  }