@brumaombra/ui-vintage 0.9.0 → 0.9.2

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 (337) hide show
  1. package/AGENTS.md +1 -1
  2. package/COMPONENTS.md +1 -1
  3. package/README.md +3 -3
  4. package/module.mjs +43 -43
  5. package/package.json +175 -175
  6. package/src/components/background-grid/BackgroundGrid.vue +4 -4
  7. package/src/components/blog/AllPostsSection.vue +33 -33
  8. package/src/components/blog/BlogContentRenderer.vue +111 -111
  9. package/src/components/blog/BlogFAQSection.vue +20 -20
  10. package/src/components/blog/BlogHeaderSection.vue +81 -81
  11. package/src/components/blog/BlogInfoSection.vue +50 -48
  12. package/src/components/blog/BlogPostHeader.vue +135 -135
  13. package/src/components/blog/BlogProgressBar.vue +33 -33
  14. package/src/components/blog/BlogSectionTitle.vue +22 -22
  15. package/src/components/blog/BlogTagsSection.vue +25 -25
  16. package/src/components/blog/CategoriesList.vue +14 -14
  17. package/src/components/blog/CategoriesSection.vue +11 -11
  18. package/src/components/blog/CategoryCard.vue +27 -26
  19. package/src/components/blog/HeaderCarousel.vue +99 -98
  20. package/src/components/blog/PostCard.vue +34 -34
  21. package/src/components/blog/PostsList.vue +15 -15
  22. package/src/components/blog/SocialShareSidebar.vue +92 -85
  23. package/src/components/blog/TableOfContents.vue +310 -308
  24. package/src/components/busy-indicator/BusyIndicator.vue +7 -7
  25. package/src/components/busy-indicator/busy-indicator-state.ts +56 -56
  26. package/src/components/card-grid/CardGrid.vue +35 -35
  27. package/src/components/chip/Chip.vue +16 -16
  28. package/src/components/confirm-dialog/ConfirmDialog.vue +51 -51
  29. package/src/components/confirm-dialog/confirm-dialog-state.ts +131 -131
  30. package/src/components/content/BlogList.vue +32 -31
  31. package/src/components/content/BlogTable.vue +36 -36
  32. package/src/components/content/CodeBlock.vue +139 -137
  33. package/src/components/content/Flow.vue +60 -60
  34. package/src/components/content/ProseHr.vue +16 -16
  35. package/src/components/content/ProsePre.vue +12 -12
  36. package/src/components/content/Terminal.vue +62 -60
  37. package/src/components/cookie-consent/CookieConsent.vue +36 -36
  38. package/src/components/cookie-consent/useCookieConsent.ts +37 -37
  39. package/src/components/dashboard-shell/DashboardShell.vue +171 -171
  40. package/src/components/data-list/DataList.vue +6 -6
  41. package/src/components/data-list/DataListItem.vue +4 -4
  42. package/src/components/data-list/DataListLabel.vue +4 -4
  43. package/src/components/data-list/DataListText.vue +4 -4
  44. package/src/components/data-list/DataListValue.vue +4 -4
  45. package/src/components/empty-state-card/EmptyStateCard.vue +41 -41
  46. package/src/components/error-page/ErrorPage.vue +73 -73
  47. package/src/components/info-card/InfoCard.vue +31 -31
  48. package/src/components/landing/landing-content/LandingContent.vue +4 -4
  49. package/src/components/landing/landing-footer/LandingFooter.vue +129 -129
  50. package/src/components/landing/landing-navbar/LandingNavbar.vue +50 -50
  51. package/src/components/landing/landing-shell/LandingShell.vue +36 -36
  52. package/src/components/language-flag/LanguageFlag.vue +25 -25
  53. package/src/components/language-flag/flags/ChineseFlag.vue +8 -8
  54. package/src/components/language-flag/flags/EnglishFlag.vue +7 -7
  55. package/src/components/language-flag/flags/FrenchFlag.vue +5 -5
  56. package/src/components/language-flag/flags/GermanFlag.vue +5 -5
  57. package/src/components/language-flag/flags/ItalianFlag.vue +7 -7
  58. package/src/components/language-flag/flags/JapaneseFlag.vue +4 -4
  59. package/src/components/language-flag/flags/PortugueseFlag.vue +57 -57
  60. package/src/components/language-flag/flags/RussianFlag.vue +5 -5
  61. package/src/components/language-flag/flags/SpanishFlag.vue +4 -4
  62. package/src/components/language-selector/LanguageSelector.vue +77 -77
  63. package/src/components/load-more-button/LoadMoreButton.vue +19 -18
  64. package/src/components/loading-state-card/LoadingStateCard.vue +29 -29
  65. package/src/components/message-dialog/MessageDialog.vue +62 -60
  66. package/src/components/message-dialog/message-dialog-state.ts +105 -105
  67. package/src/components/message-toast/MessageToast.vue +153 -150
  68. package/src/components/message-toast/message-toast-state.ts +146 -146
  69. package/src/components/page-header/PageHeader.vue +34 -34
  70. package/src/components/progress-component/ProgressComponent.vue +33 -33
  71. package/src/components/single-value-card/SingleValueCard.vue +59 -58
  72. package/src/components/slider-form-component/SliderFormComponent.vue +36 -36
  73. package/src/components/stat-strip/StatStrip.vue +22 -22
  74. package/src/components/switch-form-component/SwitchFormComponent.vue +20 -20
  75. package/src/components/text-link/TextLink.vue +14 -14
  76. package/src/components/theme-selector/ThemeSelector.vue +65 -62
  77. package/src/components/theme-selector/theme-transition.ts +63 -63
  78. package/src/components/ui/accordion/Accordion.vue +39 -39
  79. package/src/components/ui/alert/Alert.vue +5 -5
  80. package/src/components/ui/alert/AlertDescription.vue +4 -4
  81. package/src/components/ui/alert/AlertTitle.vue +4 -4
  82. package/src/components/ui/alert/index.ts +2 -2
  83. package/src/components/ui/alert-dialog/AlertDialog.vue +3 -3
  84. package/src/components/ui/alert-dialog/AlertDialogAction.vue +3 -3
  85. package/src/components/ui/alert-dialog/AlertDialogCancel.vue +3 -3
  86. package/src/components/ui/alert-dialog/AlertDialogContent.vue +7 -7
  87. package/src/components/ui/alert-dialog/AlertDialogDescription.vue +3 -3
  88. package/src/components/ui/alert-dialog/AlertDialogFooter.vue +4 -4
  89. package/src/components/ui/alert-dialog/AlertDialogHeader.vue +4 -4
  90. package/src/components/ui/alert-dialog/AlertDialogTitle.vue +3 -3
  91. package/src/components/ui/alert-dialog/AlertDialogTrigger.vue +3 -3
  92. package/src/components/ui/animated-number/AnimatedNumber.vue +49 -48
  93. package/src/components/ui/avatar/Avatar.vue +28 -28
  94. package/src/components/ui/avatar/AvatarFallback.vue +6 -6
  95. package/src/components/ui/avatar/AvatarGroup.vue +40 -40
  96. package/src/components/ui/avatar/AvatarImage.vue +3 -3
  97. package/src/components/ui/avatar/index.ts +17 -17
  98. package/src/components/ui/badge/Badge.vue +20 -20
  99. package/src/components/ui/breadcrumb/Breadcrumb.vue +4 -4
  100. package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +10 -10
  101. package/src/components/ui/breadcrumb/BreadcrumbItem.vue +4 -4
  102. package/src/components/ui/breadcrumb/BreadcrumbLink.vue +4 -4
  103. package/src/components/ui/breadcrumb/BreadcrumbList.vue +4 -4
  104. package/src/components/ui/breadcrumb/BreadcrumbPage.vue +4 -4
  105. package/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +4 -4
  106. package/src/components/ui/busy/Busy.vue +33 -33
  107. package/src/components/ui/button/Button.vue +23 -23
  108. package/src/components/ui/button/index.ts +56 -56
  109. package/src/components/ui/calendar/Calendar.vue +138 -138
  110. package/src/components/ui/calendar/CalendarCell.vue +7 -7
  111. package/src/components/ui/calendar/CalendarCellTrigger.vue +18 -18
  112. package/src/components/ui/calendar/CalendarGrid.vue +3 -3
  113. package/src/components/ui/calendar/CalendarGridBody.vue +3 -3
  114. package/src/components/ui/calendar/CalendarGridHead.vue +3 -3
  115. package/src/components/ui/calendar/CalendarGridRow.vue +3 -3
  116. package/src/components/ui/calendar/CalendarHeadCell.vue +7 -7
  117. package/src/components/ui/calendar/CalendarHeader.vue +7 -7
  118. package/src/components/ui/calendar/CalendarHeading.vue +6 -6
  119. package/src/components/ui/calendar/CalendarNextButton.vue +16 -16
  120. package/src/components/ui/calendar/CalendarPrevButton.vue +16 -16
  121. package/src/components/ui/calendar/index.ts +4 -4
  122. package/src/components/ui/card/Card.vue +20 -20
  123. package/src/components/ui/card/CardAction.vue +8 -8
  124. package/src/components/ui/card/CardContent.vue +4 -4
  125. package/src/components/ui/card/CardDescription.vue +12 -12
  126. package/src/components/ui/card/CardFooter.vue +4 -4
  127. package/src/components/ui/card/CardHeader.vue +8 -8
  128. package/src/components/ui/card/CardText.vue +8 -8
  129. package/src/components/ui/card/CardTitle.vue +8 -8
  130. package/src/components/ui/checkbox/Checkbox.vue +27 -27
  131. package/src/components/ui/collapsible/Collapsible.vue +3 -3
  132. package/src/components/ui/collapsible/CollapsibleContent.vue +3 -3
  133. package/src/components/ui/collapsible/CollapsibleTrigger.vue +3 -3
  134. package/src/components/ui/combobox/Combobox.vue +4 -4
  135. package/src/components/ui/combobox/ComboboxAnchor.vue +16 -16
  136. package/src/components/ui/combobox/ComboboxEmpty.vue +3 -3
  137. package/src/components/ui/combobox/ComboboxGroup.vue +9 -9
  138. package/src/components/ui/combobox/ComboboxInput.vue +6 -6
  139. package/src/components/ui/combobox/ComboboxItem.vue +18 -18
  140. package/src/components/ui/combobox/ComboboxList.vue +19 -19
  141. package/src/components/ui/combobox/ComboboxSelect.vue +79 -78
  142. package/src/components/ui/combobox/ComboboxSeparator.vue +1 -1
  143. package/src/components/ui/combobox/ComboboxTrigger.vue +11 -11
  144. package/src/components/ui/combobox/types.ts +5 -5
  145. package/src/components/ui/command/Command.vue +48 -48
  146. package/src/components/ui/command/CommandDialog.vue +14 -14
  147. package/src/components/ui/command/CommandEmpty.vue +4 -4
  148. package/src/components/ui/command/CommandGroup.vue +23 -23
  149. package/src/components/ui/command/CommandInput.vue +10 -10
  150. package/src/components/ui/command/CommandItem.vue +40 -40
  151. package/src/components/ui/command/CommandList.vue +9 -9
  152. package/src/components/ui/command/CommandSeparator.vue +3 -3
  153. package/src/components/ui/command/CommandShortcut.vue +8 -8
  154. package/src/components/ui/command/index.ts +11 -11
  155. package/src/components/ui/data-table/DataTable.vue +147 -146
  156. package/src/components/ui/data-table/index.ts +11 -11
  157. package/src/components/ui/date-picker/DatePicker.vue +32 -32
  158. package/src/components/ui/date-time-picker/DateTimePicker.vue +97 -97
  159. package/src/components/ui/dialog/Dialog.vue +3 -3
  160. package/src/components/ui/dialog/DialogClose.vue +3 -3
  161. package/src/components/ui/dialog/DialogContent.vue +22 -22
  162. package/src/components/ui/dialog/DialogDescription.vue +7 -7
  163. package/src/components/ui/dialog/DialogFooter.vue +8 -8
  164. package/src/components/ui/dialog/DialogHeader.vue +4 -4
  165. package/src/components/ui/dialog/DialogOverlay.vue +7 -7
  166. package/src/components/ui/dialog/DialogScrollContent.vue +33 -33
  167. package/src/components/ui/dialog/DialogTitle.vue +3 -3
  168. package/src/components/ui/dialog/DialogTrigger.vue +3 -3
  169. package/src/components/ui/dropdown-menu/DropdownMenu.vue +3 -3
  170. package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +12 -12
  171. package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +15 -15
  172. package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +3 -3
  173. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +12 -12
  174. package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +9 -9
  175. package/src/components/ui/dropdown-menu/DropdownMenuPortal.vue +3 -3
  176. package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +3 -3
  177. package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +11 -11
  178. package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +1 -1
  179. package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +4 -4
  180. package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +3 -3
  181. package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +15 -15
  182. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +16 -16
  183. package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +3 -3
  184. package/src/components/ui/dropdown-menu/classes.ts +16 -16
  185. package/src/components/ui/field/Field.vue +5 -5
  186. package/src/components/ui/field/FieldContent.vue +8 -8
  187. package/src/components/ui/field/FieldDescription.vue +10 -10
  188. package/src/components/ui/field/FieldError.vue +39 -39
  189. package/src/components/ui/field/FieldGroup.vue +8 -8
  190. package/src/components/ui/field/FieldLabel.vue +10 -10
  191. package/src/components/ui/field/FieldLegend.vue +11 -11
  192. package/src/components/ui/field/FieldSeparator.vue +11 -11
  193. package/src/components/ui/field/FieldSet.vue +9 -9
  194. package/src/components/ui/field/FieldTitle.vue +8 -8
  195. package/src/components/ui/field/field-context.ts +36 -36
  196. package/src/components/ui/field/index.ts +22 -22
  197. package/src/components/ui/file-dropzone/FileDropzone.vue +163 -154
  198. package/src/components/ui/file-dropzone/index.ts +19 -19
  199. package/src/components/ui/hover-card/HoverCard.vue +5 -5
  200. package/src/components/ui/hover-card/HoverCardContent.vue +13 -13
  201. package/src/components/ui/hover-card/HoverCardTrigger.vue +3 -3
  202. package/src/components/ui/input/Input.vue +13 -13
  203. package/src/components/ui/kbd/Kbd.vue +10 -10
  204. package/src/components/ui/kbd/KbdGroup.vue +49 -49
  205. package/src/components/ui/label/Label.vue +7 -7
  206. package/src/components/ui/native-select/NativeSelect.vue +17 -17
  207. package/src/components/ui/native-select/NativeSelectOptGroup.vue +3 -3
  208. package/src/components/ui/native-select/NativeSelectOption.vue +3 -3
  209. package/src/components/ui/number-field/NumberField.vue +90 -89
  210. package/src/components/ui/pagination/Pagination.vue +4 -4
  211. package/src/components/ui/pagination/PaginationContent.vue +4 -4
  212. package/src/components/ui/pagination/PaginationEllipsis.vue +10 -10
  213. package/src/components/ui/pagination/PaginationFirst.vue +9 -9
  214. package/src/components/ui/pagination/PaginationItem.vue +14 -14
  215. package/src/components/ui/pagination/PaginationLast.vue +9 -9
  216. package/src/components/ui/pagination/PaginationNext.vue +9 -9
  217. package/src/components/ui/pagination/PaginationPrevious.vue +9 -9
  218. package/src/components/ui/pagination/SimplePagination.vue +25 -25
  219. package/src/components/ui/pin-input/PinInput.vue +20 -20
  220. package/src/components/ui/pin-input/PinInputInput.vue +17 -17
  221. package/src/components/ui/pin-input/context.ts +1 -1
  222. package/src/components/ui/popover/Popover.vue +3 -3
  223. package/src/components/ui/popover/PopoverAnchor.vue +3 -3
  224. package/src/components/ui/popover/PopoverContent.vue +12 -12
  225. package/src/components/ui/popover/PopoverTrigger.vue +3 -3
  226. package/src/components/ui/progress/Progress.vue +16 -16
  227. package/src/components/ui/radio-group/RadioGroup.vue +7 -7
  228. package/src/components/ui/radio-group/RadioGroupCard.vue +39 -39
  229. package/src/components/ui/radio-group/RadioGroupItem.vue +20 -20
  230. package/src/components/ui/scroll-area/ScrollArea.vue +14 -14
  231. package/src/components/ui/scroll-area/ScrollBar.vue +12 -12
  232. package/src/components/ui/select/Select.vue +3 -3
  233. package/src/components/ui/select/SelectContent.vue +25 -25
  234. package/src/components/ui/select/SelectGroup.vue +3 -3
  235. package/src/components/ui/select/SelectItem.vue +25 -25
  236. package/src/components/ui/select/SelectItemContent.vue +32 -32
  237. package/src/components/ui/select/SelectItemText.vue +3 -3
  238. package/src/components/ui/select/SelectLabel.vue +3 -3
  239. package/src/components/ui/select/SelectScrollDownButton.vue +10 -10
  240. package/src/components/ui/select/SelectScrollUpButton.vue +10 -10
  241. package/src/components/ui/select/SelectSeparator.vue +2 -2
  242. package/src/components/ui/select/SelectTrigger.vue +15 -15
  243. package/src/components/ui/select/SelectValue.vue +3 -3
  244. package/src/components/ui/select/SelectValueContent.vue +39 -39
  245. package/src/components/ui/separator/Separator.vue +7 -7
  246. package/src/components/ui/sheet/Sheet.vue +3 -3
  247. package/src/components/ui/sheet/SheetClose.vue +3 -3
  248. package/src/components/ui/sheet/SheetContent.vue +22 -22
  249. package/src/components/ui/sheet/SheetDescription.vue +3 -3
  250. package/src/components/ui/sheet/SheetFooter.vue +3 -3
  251. package/src/components/ui/sheet/SheetHeader.vue +3 -3
  252. package/src/components/ui/sheet/SheetOverlay.vue +7 -7
  253. package/src/components/ui/sheet/SheetTitle.vue +3 -3
  254. package/src/components/ui/sheet/SheetTrigger.vue +3 -3
  255. package/src/components/ui/sidebar/Sidebar.vue +57 -57
  256. package/src/components/ui/sidebar/SidebarContent.vue +8 -8
  257. package/src/components/ui/sidebar/SidebarFooter.vue +8 -8
  258. package/src/components/ui/sidebar/SidebarGroup.vue +8 -8
  259. package/src/components/ui/sidebar/SidebarGroupAction.vue +9 -9
  260. package/src/components/ui/sidebar/SidebarGroupContent.vue +4 -4
  261. package/src/components/ui/sidebar/SidebarGroupLabel.vue +8 -8
  262. package/src/components/ui/sidebar/SidebarHeader.vue +8 -8
  263. package/src/components/ui/sidebar/SidebarInput.vue +8 -8
  264. package/src/components/ui/sidebar/SidebarInset.vue +9 -9
  265. package/src/components/ui/sidebar/SidebarMenu.vue +4 -4
  266. package/src/components/ui/sidebar/SidebarMenuAction.vue +18 -18
  267. package/src/components/ui/sidebar/SidebarMenuBadge.vue +14 -14
  268. package/src/components/ui/sidebar/SidebarMenuButton.vue +21 -21
  269. package/src/components/ui/sidebar/SidebarMenuButtonChild.vue +10 -10
  270. package/src/components/ui/sidebar/SidebarMenuItem.vue +4 -4
  271. package/src/components/ui/sidebar/SidebarMenuSkeleton.vue +10 -10
  272. package/src/components/ui/sidebar/SidebarMenuSub.vue +9 -9
  273. package/src/components/ui/sidebar/SidebarMenuSubButton.vue +17 -17
  274. package/src/components/ui/sidebar/SidebarMenuSubItem.vue +4 -4
  275. package/src/components/ui/sidebar/SidebarProvider.vue +46 -46
  276. package/src/components/ui/sidebar/SidebarRail.vue +13 -13
  277. package/src/components/ui/sidebar/SidebarSeparator.vue +4 -4
  278. package/src/components/ui/sidebar/SidebarTrigger.vue +6 -6
  279. package/src/components/ui/sidebar/index.ts +29 -29
  280. package/src/components/ui/sidebar/utils.ts +7 -7
  281. package/src/components/ui/skeleton/Skeleton.vue +6 -6
  282. package/src/components/ui/slider/Slider.vue +14 -14
  283. package/src/components/ui/spinner/Spinner.vue +21 -21
  284. package/src/components/ui/stepper/SimpleStepper.vue +27 -27
  285. package/src/components/ui/stepper/Stepper.vue +6 -6
  286. package/src/components/ui/stepper/StepperDescription.vue +5 -5
  287. package/src/components/ui/stepper/StepperIndicator.vue +18 -18
  288. package/src/components/ui/stepper/StepperItem.vue +4 -4
  289. package/src/components/ui/stepper/StepperSeparator.vue +7 -7
  290. package/src/components/ui/stepper/StepperTitle.vue +5 -5
  291. package/src/components/ui/stepper/StepperTrigger.vue +4 -4
  292. package/src/components/ui/stepper/index.ts +3 -3
  293. package/src/components/ui/switch/Switch.vue +15 -15
  294. package/src/components/ui/table/Table.vue +6 -6
  295. package/src/components/ui/table/TableBody.vue +4 -4
  296. package/src/components/ui/table/TableCaption.vue +4 -4
  297. package/src/components/ui/table/TableCell.vue +8 -8
  298. package/src/components/ui/table/TableEmpty.vue +14 -14
  299. package/src/components/ui/table/TableFooter.vue +8 -8
  300. package/src/components/ui/table/TableHead.vue +8 -8
  301. package/src/components/ui/table/TableHeader.vue +4 -4
  302. package/src/components/ui/table/TableRow.vue +8 -8
  303. package/src/components/ui/tabs/Tabs.vue +4 -4
  304. package/src/components/ui/tabs/TabsContent.vue +4 -4
  305. package/src/components/ui/tabs/TabsList.vue +15 -15
  306. package/src/components/ui/tabs/TabsTrigger.vue +10 -10
  307. package/src/components/ui/tags-input/TagsInput.vue +17 -17
  308. package/src/components/ui/tags-input/TagsInputInput.vue +6 -6
  309. package/src/components/ui/tags-input/TagsInputItem.vue +15 -15
  310. package/src/components/ui/tags-input/TagsInputItemDelete.vue +13 -13
  311. package/src/components/ui/tags-input/TagsInputItemText.vue +4 -4
  312. package/src/components/ui/textarea/Textarea.vue +12 -12
  313. package/src/components/ui/time-picker/TimePicker.vue +25 -25
  314. package/src/components/ui/toggle-group/Toggle.vue +10 -10
  315. package/src/components/ui/toggle-group/ToggleGroup.vue +38 -38
  316. package/src/components/ui/toggle-group/ToggleGroupItem.vue +9 -9
  317. package/src/components/ui/toggle-group/index.ts +46 -46
  318. package/src/components/ui/tooltip/Tooltip.vue +3 -3
  319. package/src/components/ui/tooltip/TooltipContent.vue +13 -13
  320. package/src/components/ui/tooltip/TooltipProvider.vue +4 -4
  321. package/src/components/ui/tooltip/TooltipTrigger.vue +3 -3
  322. package/src/i18n/de.json +185 -185
  323. package/src/i18n/en.json +185 -185
  324. package/src/i18n/es.json +185 -185
  325. package/src/i18n/fr.json +185 -185
  326. package/src/i18n/it.json +185 -185
  327. package/src/i18n/ja.json +185 -185
  328. package/src/i18n/pt.json +185 -185
  329. package/src/i18n/ru.json +185 -185
  330. package/src/i18n/zh.json +185 -185
  331. package/src/lib/color-tokens.ts +24 -24
  332. package/src/lib/i18n.ts +65 -65
  333. package/src/lib/utils.ts +33 -33
  334. package/src/nuxt-components.d.ts +2 -2
  335. package/src/runtime/ui-vintage-i18n-plugin.ts +49 -49
  336. package/src/shims-vue.d.ts +3 -3
  337. package/src/styles.css +377 -377
@@ -1,98 +1,98 @@
1
1
  export type ThemeMode = 'light' | 'dark' | 'auto';
2
2
 
3
3
  export interface ThemeTransitionOrigin {
4
- x: number;
5
- y: number;
4
+ x: number;
5
+ y: number;
6
6
  }
7
7
 
8
8
  const THEME_STORAGE_KEY = 'theme';
9
9
 
10
10
  type DocumentWithViewTransition = Document & {
11
- startViewTransition?: (callback: () => void) => {
12
- ready: Promise<void>;
13
- finished: Promise<void>;
14
- };
11
+ startViewTransition?: (callback: () => void) => {
12
+ ready: Promise<void>;
13
+ finished: Promise<void>;
14
+ };
15
15
  };
16
16
 
17
17
  // Check if the system prefers dark mode
18
18
  const isSystemDarkPreferred = () => {
19
- return typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
19
+ return typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
20
20
  };
21
21
 
22
22
  // Resolve whether a theme mode ends up dark
23
23
  export const isDarkTheme = (theme: ThemeMode) => {
24
- return theme === 'dark' || (theme === 'auto' && isSystemDarkPreferred());
24
+ return theme === 'dark' || (theme === 'auto' && isSystemDarkPreferred());
25
25
  };
26
26
 
27
27
  // Read the persisted theme mode
28
28
  export const getStoredTheme = (): ThemeMode => {
29
- if (typeof localStorage === 'undefined') return 'auto';
29
+ if (typeof localStorage === 'undefined') return 'auto';
30
30
 
31
- // Storage can throw when the browser blocks it (privacy settings, sandboxed frames)
32
- try {
33
- const storedTheme = localStorage.getItem(THEME_STORAGE_KEY);
34
- return storedTheme === 'light' || storedTheme === 'dark' ? storedTheme : 'auto';
35
- } catch {
36
- return 'auto';
37
- }
31
+ // Storage can throw when the browser blocks it (privacy settings, sandboxed frames)
32
+ try {
33
+ const storedTheme = localStorage.getItem(THEME_STORAGE_KEY);
34
+ return storedTheme === 'light' || storedTheme === 'dark' ? storedTheme : 'auto';
35
+ } catch {
36
+ return 'auto';
37
+ }
38
38
  };
39
39
 
40
40
  // Apply the selected theme to the document and persist it
41
41
  export const applyTheme = (theme: ThemeMode) => {
42
- if (typeof document === 'undefined') return;
43
- const root = document.documentElement;
44
- root.classList.remove('light', 'dark');
45
- root.classList.add(isDarkTheme(theme) ? 'dark' : 'light');
42
+ if (typeof document === 'undefined') return;
43
+ const root = document.documentElement;
44
+ root.classList.remove('light', 'dark');
45
+ root.classList.add(isDarkTheme(theme) ? 'dark' : 'light');
46
46
 
47
- // Persisting is best effort: the theme still applies when storage is blocked
48
- try {
49
- localStorage.setItem(THEME_STORAGE_KEY, theme);
50
- } catch {
51
- // Ignore storage errors
52
- }
47
+ // Persisting is best effort: the theme still applies when storage is blocked
48
+ try {
49
+ localStorage.setItem(THEME_STORAGE_KEY, theme);
50
+ } catch {
51
+ // Ignore storage errors
52
+ }
53
53
  };
54
54
 
55
55
  // Apply a theme with a circular reveal that grows from the origin point (View Transitions API)
56
56
  export const applyThemeWithTransition = async (theme: ThemeMode, origin?: ThemeTransitionOrigin | Element | null) => {
57
- const doc = document as DocumentWithViewTransition;
58
- const root = document.documentElement;
59
- const willChange = root.classList.contains('dark') !== isDarkTheme(theme);
60
- const prefersReducedMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
57
+ const doc = document as DocumentWithViewTransition;
58
+ const root = document.documentElement;
59
+ const willChange = root.classList.contains('dark') !== isDarkTheme(theme);
60
+ const prefersReducedMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
61
61
 
62
- // Fall back to an instant switch when the transition is unsupported or pointless
63
- if (!doc.startViewTransition || !willChange || prefersReducedMotion) {
64
- applyTheme(theme);
65
- return;
66
- }
62
+ // Fall back to an instant switch when the transition is unsupported or pointless
63
+ if (!doc.startViewTransition || !willChange || prefersReducedMotion) {
64
+ applyTheme(theme);
65
+ return;
66
+ }
67
67
 
68
- // Resolve the reveal origin (defaults to the top-right corner)
69
- let x = window.innerWidth;
70
- let y = 0;
71
- if (origin instanceof Element) {
72
- const rect = origin.getBoundingClientRect();
73
- x = rect.left + rect.width / 2;
74
- y = rect.top + rect.height / 2;
75
- } else if (origin) {
76
- x = origin.x;
77
- y = origin.y;
78
- }
79
- const radius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y));
68
+ // Resolve the reveal origin (defaults to the top-right corner)
69
+ let x = window.innerWidth;
70
+ let y = 0;
71
+ if (origin instanceof Element) {
72
+ const rect = origin.getBoundingClientRect();
73
+ x = rect.left + rect.width / 2;
74
+ y = rect.top + rect.height / 2;
75
+ } else if (origin) {
76
+ x = origin.x;
77
+ y = origin.y;
78
+ }
79
+ const radius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y));
80
80
 
81
- // Freeze CSS transitions so the snapshots capture the final colors
82
- root.classList.add('uv-theme-switching');
83
- const transition = doc.startViewTransition(() => applyTheme(theme));
81
+ // Freeze CSS transitions so the snapshots capture the final colors
82
+ root.classList.add('uv-theme-switching');
83
+ const transition = doc.startViewTransition(() => applyTheme(theme));
84
84
 
85
- try {
86
- await transition.ready;
87
- root.animate({
88
- clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${radius}px at ${x}px ${y}px)`]
89
- }, {
90
- duration: 620,
91
- easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
92
- pseudoElement: '::view-transition-new(root)'
93
- });
94
- await transition.finished;
95
- } finally {
96
- root.classList.remove('uv-theme-switching');
97
- }
85
+ try {
86
+ await transition.ready;
87
+ root.animate({
88
+ clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${radius}px at ${x}px ${y}px)`]
89
+ }, {
90
+ duration: 620,
91
+ easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
92
+ pseudoElement: '::view-transition-new(root)'
93
+ });
94
+ await transition.finished;
95
+ } finally {
96
+ root.classList.remove('uv-theme-switching');
97
+ }
98
98
  };
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { HTMLAttributes } from 'vue';
3
3
  import { computed, ref, useId } from 'vue';
4
- import { ArrowDown01Icon } from '@hugeicons/core-free-icons';
4
+ import ArrowDown01Icon from '@hugeicons/core-free-icons/ArrowDown01Icon';
5
5
  import { HugeiconsIcon } from '@hugeicons/vue';
6
6
  import { Card } from '../card';
7
7
  import { cn } from '../../../lib/utils';
@@ -9,20 +9,20 @@ import type { HugeiconsIconDefinition } from '../../../lib/common-types';
9
9
 
10
10
  // Props
11
11
  const props = withDefaults(defineProps<{
12
- title: string;
13
- initiallyExpanded?: boolean;
14
- open?: boolean;
15
- icon?: HugeiconsIconDefinition | null;
16
- iconClass?: HTMLAttributes['class'];
17
- class?: HTMLAttributes['class'];
12
+ title: string;
13
+ initiallyExpanded?: boolean;
14
+ open?: boolean;
15
+ icon?: HugeiconsIconDefinition | null;
16
+ iconClass?: HTMLAttributes['class'];
17
+ class?: HTMLAttributes['class'];
18
18
  }>(), {
19
- initiallyExpanded: false,
20
- open: undefined
19
+ initiallyExpanded: false,
20
+ open: undefined
21
21
  });
22
22
 
23
23
  // Emits
24
24
  const emits = defineEmits<{
25
- 'update:open': [value: boolean];
25
+ 'update:open': [value: boolean];
26
26
  }>();
27
27
 
28
28
  const contentId = useId();
@@ -34,40 +34,40 @@ const expanded = computed(() => props.open ?? internalExpanded.value);
34
34
 
35
35
  // Toggle expanded state
36
36
  const toggleExpanded = () => {
37
- const nextValue = !expanded.value;
38
- internalExpanded.value = nextValue;
39
- emits('update:open', nextValue);
37
+ const nextValue = !expanded.value;
38
+ internalExpanded.value = nextValue;
39
+ emits('update:open', nextValue);
40
40
  };
41
41
  </script>
42
42
 
43
43
  <template>
44
- <Card :data-state="expanded ? 'open' : 'closed'" :class="cn('group/accordion gap-0! overflow-hidden p-0!', props.class)">
45
- <!-- Header -->
46
- <button :id="triggerId" type="button" :aria-expanded="expanded" :aria-controls="contentId" class="group/trigger flex w-full cursor-pointer items-center gap-3 px-5 py-3.5 text-left outline-none focus-visible:bg-surface focus-visible:ring-[3px] focus-visible:ring-inset focus-visible:ring-ring/40" @click="toggleExpanded">
47
- <!-- Icon -->
48
- <span v-if="props.icon" class="flex size-7 shrink-0 items-center justify-center rounded border border-primary/30 text-primary">
49
- <HugeiconsIcon :icon="props.icon" :class="cn('size-3.5', props.iconClass)" />
50
- </span>
44
+ <Card :data-state="expanded ? 'open' : 'closed'" :class="cn('group/accordion gap-0! overflow-hidden p-0!', props.class)">
45
+ <!-- Header -->
46
+ <button :id="triggerId" type="button" :aria-expanded="expanded" :aria-controls="contentId" class="group/trigger flex w-full cursor-pointer items-center gap-3 px-5 py-3.5 text-left outline-none focus-visible:bg-surface focus-visible:ring-[3px] focus-visible:ring-inset focus-visible:ring-ring/40" @click="toggleExpanded">
47
+ <!-- Icon -->
48
+ <span v-if="props.icon" class="flex size-7 shrink-0 items-center justify-center rounded border border-primary/30 text-primary">
49
+ <HugeiconsIcon :icon="props.icon" :class="cn('size-3.5', props.iconClass)" />
50
+ </span>
51
51
 
52
- <!-- Title -->
53
- <span class="min-w-0 flex-1 text-sm font-semibold text-foreground">
54
- {{ props.title }}
55
- </span>
52
+ <!-- Title -->
53
+ <span class="min-w-0 flex-1 text-sm font-semibold text-foreground">
54
+ {{ props.title }}
55
+ </span>
56
56
 
57
- <!-- Optional content before the toggle icon -->
58
- <slot name="trailing" />
57
+ <!-- Optional content before the toggle icon -->
58
+ <slot name="trailing" />
59
59
 
60
- <!-- Toggle icon -->
61
- <HugeiconsIcon :icon="ArrowDown01Icon" class="size-4 shrink-0 text-muted-foreground transition-[rotate,color] duration-[420ms] ease-spring group-hover/trigger:text-primary group-data-[state=open]/accordion:rotate-180 group-data-[state=open]/accordion:text-primary" />
62
- </button>
60
+ <!-- Toggle icon -->
61
+ <HugeiconsIcon :icon="ArrowDown01Icon" class="size-4 shrink-0 text-muted-foreground transition-[rotate,color] duration-[420ms] ease-spring group-hover/trigger:text-primary group-data-[state=open]/accordion:rotate-180 group-data-[state=open]/accordion:text-primary" />
62
+ </button>
63
63
 
64
- <!-- Content -->
65
- <div :id="contentId" role="region" :aria-labelledby="triggerId" :aria-hidden="!expanded" :inert="!expanded || undefined" :class="['grid transition-[grid-template-rows,opacity] duration-[380ms] ease-out-expo', expanded ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0']">
66
- <div class="overflow-hidden">
67
- <div :class="['border-t border-border px-5 py-4 transition-[translate,filter] duration-[420ms] ease-spring', expanded ? 'translate-y-0 blur-none' : '-translate-y-2 blur-[2px]']">
68
- <slot />
69
- </div>
70
- </div>
71
- </div>
72
- </Card>
73
- </template>
64
+ <!-- Content -->
65
+ <div :id="contentId" role="region" :aria-labelledby="triggerId" :aria-hidden="!expanded" :inert="!expanded || undefined" :class="['grid transition-[grid-template-rows,opacity] duration-[380ms] ease-out-expo', expanded ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0']">
66
+ <div class="overflow-hidden">
67
+ <div :class="['border-t border-border px-5 py-4 transition-[translate,filter] duration-[420ms] ease-spring', expanded ? 'translate-y-0 blur-none' : '-translate-y-2 blur-[2px]']">
68
+ <slot />
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </Card>
73
+ </template>
@@ -6,13 +6,13 @@ import { alertClasses, type AlertColors } from '.';
6
6
 
7
7
  // Props
8
8
  const props = defineProps<{
9
- class?: HTMLAttributes['class'];
10
- color?: AlertColors['color'];
9
+ class?: HTMLAttributes['class'];
10
+ color?: AlertColors['color'];
11
11
  }>();
12
12
  </script>
13
13
 
14
14
  <template>
15
- <div data-slot="alert" role="alert" :class="cn(alertClasses(), getSurfaceToneClasses(props.color ?? 'gray'), props.class)">
16
- <slot />
17
- </div>
15
+ <div data-slot="alert" role="alert" :class="cn(alertClasses(), getSurfaceToneClasses(props.color ?? 'gray'), props.class)">
16
+ <slot />
17
+ </div>
18
18
  </template>
@@ -4,12 +4,12 @@ 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
- <div data-slot="alert-description" :class="cn('col-start-2 grid justify-items-start gap-1 [&_p]:leading-relaxed', props.class)">
13
- <slot />
14
- </div>
12
+ <div data-slot="alert-description" :class="cn('col-start-2 grid justify-items-start gap-1 [&_p]:leading-relaxed', props.class)">
13
+ <slot />
14
+ </div>
15
15
  </template>
@@ -4,12 +4,12 @@ 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
- <div data-slot="alert-title" :class="cn('col-start-2 line-clamp-1 min-h-4 font-semibold tracking-tight text-current', props.class)">
13
- <slot />
14
- </div>
12
+ <div data-slot="alert-title" :class="cn('col-start-2 line-clamp-1 min-h-4 font-semibold tracking-tight text-current', props.class)">
13
+ <slot />
14
+ </div>
15
15
  </template>
@@ -7,9 +7,9 @@ export { default as AlertDescription } from './AlertDescription.vue';
7
7
  export { default as AlertTitle } from './AlertTitle.vue';
8
8
 
9
9
  export type AlertColors = {
10
- color?: ToneColor | null | undefined;
10
+ color?: ToneColor | null | undefined;
11
11
  };
12
12
 
13
13
  export const alertClasses: (props?: ClassProp) => string = cva(
14
- 'relative w-full rounded px-3 py-2 text-xs transition-colors duration-300 ease-in-out grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 content-start items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current sm:px-4 sm:py-3 sm:text-sm',
14
+ 'relative w-full rounded px-3 py-2 text-xs transition-colors duration-300 ease-in-out grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 content-start items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current sm:px-4 sm:py-3 sm:text-sm',
15
15
  );
@@ -13,7 +13,7 @@ const forwarded = useForwardPropsEmits(props, emits);
13
13
  </script>
14
14
 
15
15
  <template>
16
- <AlertDialogRoot v-slot="slotProps" data-slot="alert-dialog" v-bind="forwarded">
17
- <slot v-bind="slotProps" />
18
- </AlertDialogRoot>
16
+ <AlertDialogRoot v-slot="slotProps" data-slot="alert-dialog" v-bind="forwarded">
17
+ <slot v-bind="slotProps" />
18
+ </AlertDialogRoot>
19
19
  </template>
@@ -14,7 +14,7 @@ const delegatedProps = reactiveOmit(props, 'class');
14
14
  </script>
15
15
 
16
16
  <template>
17
- <AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
18
- <slot />
19
- </AlertDialogAction>
17
+ <AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
18
+ <slot />
19
+ </AlertDialogAction>
20
20
  </template>
@@ -14,7 +14,7 @@ const delegatedProps = reactiveOmit(props, 'class');
14
14
  </script>
15
15
 
16
16
  <template>
17
- <AlertDialogCancel v-bind="delegatedProps" :class="cn(buttonVariants({ variant: 'secondary' }), 'mt-2 sm:mt-0', props.class)">
18
- <slot />
19
- </AlertDialogCancel>
17
+ <AlertDialogCancel v-bind="delegatedProps" :class="cn(buttonVariants({ variant: 'secondary' }), 'mt-2 sm:mt-0', props.class)">
18
+ <slot />
19
+ </AlertDialogCancel>
20
20
  </template>
@@ -7,7 +7,7 @@ import { cn } from '../../../lib/utils';
7
7
 
8
8
  // Keep attrs on the dialog content element
9
9
  defineOptions({
10
- inheritAttrs: false
10
+ inheritAttrs: false
11
11
  });
12
12
 
13
13
  // Props
@@ -24,10 +24,10 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
24
24
  </script>
25
25
 
26
26
  <template>
27
- <AlertDialogPortal>
28
- <AlertDialogOverlay data-slot="alert-dialog-overlay" class="uv-overlay-motion fixed inset-0 z-50 bg-black/40 backdrop-blur-[3px] dark:bg-black/60" />
29
- <AlertDialogContent data-slot="alert-dialog-content" v-bind="{ ...$attrs, ...forwarded }" :class="cn('uv-modal-motion bg-card text-card-foreground fixed top-[50%] left-[50%] z-50 grid w-[95%] max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-0 overflow-hidden rounded border border-border shadow-elevated-xl sm:max-w-lg', props.class)">
30
- <slot />
31
- </AlertDialogContent>
32
- </AlertDialogPortal>
27
+ <AlertDialogPortal>
28
+ <AlertDialogOverlay data-slot="alert-dialog-overlay" class="uv-overlay-motion fixed inset-0 z-50 bg-black/40 backdrop-blur-[3px] dark:bg-black/60" />
29
+ <AlertDialogContent data-slot="alert-dialog-content" v-bind="{ ...$attrs, ...forwarded }" :class="cn('uv-modal-motion bg-card text-card-foreground fixed top-[50%] left-[50%] z-50 grid w-[95%] max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-0 overflow-hidden rounded border border-border shadow-elevated-xl sm:max-w-lg', props.class)">
30
+ <slot />
31
+ </AlertDialogContent>
32
+ </AlertDialogPortal>
33
33
  </template>
@@ -13,7 +13,7 @@ const delegatedProps = reactiveOmit(props, 'class');
13
13
  </script>
14
14
 
15
15
  <template>
16
- <AlertDialogDescription data-slot="alert-dialog-description" v-bind="delegatedProps" :class="cn('text-xs leading-6 text-muted-foreground sm:text-sm', props.class)">
17
- <slot />
18
- </AlertDialogDescription>
16
+ <AlertDialogDescription data-slot="alert-dialog-description" v-bind="delegatedProps" :class="cn('text-xs leading-6 text-muted-foreground sm:text-sm', props.class)">
17
+ <slot />
18
+ </AlertDialogDescription>
19
19
  </template>
@@ -4,12 +4,12 @@ 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
- <div data-slot="alert-dialog-footer" :class="cn('flex flex-col-reverse gap-3 border-t border-border bg-surface/60 px-5 py-4 sm:flex-row-reverse sm:px-6', props.class)">
13
- <slot />
14
- </div>
12
+ <div data-slot="alert-dialog-footer" :class="cn('flex flex-col-reverse gap-3 border-t border-border bg-surface/60 px-5 py-4 sm:flex-row-reverse sm:px-6', props.class)">
13
+ <slot />
14
+ </div>
15
15
  </template>
@@ -4,12 +4,12 @@ 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
- <div data-slot="alert-dialog-header" :class="cn('flex flex-col gap-1.5 text-center sm:text-left', props.class)">
13
- <slot />
14
- </div>
12
+ <div data-slot="alert-dialog-header" :class="cn('flex flex-col gap-1.5 text-center sm:text-left', props.class)">
13
+ <slot />
14
+ </div>
15
15
  </template>
@@ -13,7 +13,7 @@ const delegatedProps = reactiveOmit(props, 'class');
13
13
  </script>
14
14
 
15
15
  <template>
16
- <AlertDialogTitle data-slot="alert-dialog-title" v-bind="delegatedProps" :class="cn('text-base font-semibold', props.class)">
17
- <slot />
18
- </AlertDialogTitle>
16
+ <AlertDialogTitle data-slot="alert-dialog-title" v-bind="delegatedProps" :class="cn('text-base font-semibold', props.class)">
17
+ <slot />
18
+ </AlertDialogTitle>
19
19
  </template>
@@ -7,7 +7,7 @@ const props = defineProps<AlertDialogTriggerProps>();
7
7
  </script>
8
8
 
9
9
  <template>
10
- <AlertDialogTrigger data-slot="alert-dialog-trigger" v-bind="props">
11
- <slot />
12
- </AlertDialogTrigger>
10
+ <AlertDialogTrigger data-slot="alert-dialog-trigger" v-bind="props">
11
+ <slot />
12
+ </AlertDialogTrigger>
13
13
  </template>
@@ -5,21 +5,21 @@ import { cn } from '../../../lib/utils';
5
5
 
6
6
  // Props
7
7
  const props = withDefaults(defineProps<{
8
- value: number;
9
- duration?: number;
10
- locale?: string;
11
- formatOptions?: Intl.NumberFormatOptions;
12
- prefix?: string;
13
- suffix?: string;
14
- animateOnMount?: boolean;
15
- class?: HTMLAttributes['class'];
8
+ value: number;
9
+ duration?: number;
10
+ locale?: string;
11
+ formatOptions?: Intl.NumberFormatOptions;
12
+ prefix?: string;
13
+ suffix?: string;
14
+ animateOnMount?: boolean;
15
+ class?: HTMLAttributes['class'];
16
16
  }>(), {
17
- duration: 900,
18
- locale: undefined,
19
- formatOptions: undefined,
20
- prefix: '',
21
- suffix: '',
22
- animateOnMount: true
17
+ duration: 900,
18
+ locale: undefined,
19
+ formatOptions: undefined,
20
+ prefix: '',
21
+ suffix: '',
22
+ animateOnMount: true
23
23
  });
24
24
 
25
25
  // The server always renders the final value (search engines, link previews and the first paint never see a 0), the count-up only runs for components created in the browser
@@ -32,9 +32,9 @@ let frame: number | null = null;
32
32
 
33
33
  // Format the displayed value
34
34
  const formatter = computed(() => {
35
- // Default to the precision of the target so intermediate frames never show stray decimals
36
- const decimals = (String(props.value).split('.')[1] ?? '').length;
37
- return new Intl.NumberFormat(props.locale, props.formatOptions ?? { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
35
+ // Default to the precision of the target so intermediate frames never show stray decimals
36
+ const decimals = (String(props.value).split('.')[1] ?? '').length;
37
+ return new Intl.NumberFormat(props.locale, props.formatOptions ?? { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
38
38
  });
39
39
  const formattedValue = computed(() => `${props.prefix}${formatter.value.format(displayValue.value)}${props.suffix}`);
40
40
 
@@ -46,32 +46,32 @@ const easeOutExpo = (progress: number) => progress === 1 ? 1 : 1 - Math.pow(2, -
46
46
 
47
47
  // Tween from the current displayed value to the target value
48
48
  const animateTo = (target: number) => {
49
- if (frame !== null) cancelAnimationFrame(frame);
49
+ if (frame !== null) cancelAnimationFrame(frame);
50
50
 
51
- // Jump straight to the value when motion is reduced or unavailable
52
- const prefersReducedMotion = typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
53
- if (typeof requestAnimationFrame === 'undefined' || prefersReducedMotion || props.duration <= 0) {
54
- displayValue.value = target;
55
- return;
56
- }
51
+ // Jump straight to the value when motion is reduced or unavailable
52
+ const prefersReducedMotion = typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
53
+ if (typeof requestAnimationFrame === 'undefined' || prefersReducedMotion || props.duration <= 0) {
54
+ displayValue.value = target;
55
+ return;
56
+ }
57
57
 
58
- const from = displayValue.value;
59
- const start = performance.now();
60
- direction.value = target >= from ? 'up' : 'down';
58
+ const from = displayValue.value;
59
+ const start = performance.now();
60
+ direction.value = target >= from ? 'up' : 'down';
61
61
 
62
- // Advance one frame of the tween
63
- const step = (now: number) => {
64
- const progress = Math.min(1, (now - start) / props.duration);
65
- displayValue.value = from + (target - from) * easeOutExpo(progress);
66
- if (progress < 1) {
67
- frame = requestAnimationFrame(step);
68
- } else {
69
- displayValue.value = target;
70
- direction.value = null;
71
- frame = null;
72
- }
73
- };
74
- frame = requestAnimationFrame(step);
62
+ // Advance one frame of the tween
63
+ const step = (now: number) => {
64
+ const progress = Math.min(1, (now - start) / props.duration);
65
+ displayValue.value = from + (target - from) * easeOutExpo(progress);
66
+ if (progress < 1) {
67
+ frame = requestAnimationFrame(step);
68
+ } else {
69
+ displayValue.value = target;
70
+ direction.value = null;
71
+ frame = null;
72
+ }
73
+ };
74
+ frame = requestAnimationFrame(step);
75
75
  };
76
76
 
77
77
  // Re-animate whenever the value changes
@@ -79,24 +79,25 @@ watch(() => props.value, value => animateTo(value));
79
79
 
80
80
  // When hydrating server HTML (the element already exists before the first render), keep the final value the server rendered, so there is no mismatch and no flash to 0
81
81
  onBeforeMount(() => {
82
- isHydrated = !!instance?.vnode.el;
83
- if (isHydrated) displayValue.value = props.value;
82
+ isHydrated = !!instance?.vnode.el;
83
+ if (isHydrated) displayValue.value = props.value;
84
84
  });
85
85
 
86
86
  // Count up on mount (only for components created in the browser, server-rendered ones already show the final value)
87
87
  onMounted(() => {
88
- if (props.animateOnMount && !isHydrated) animateTo(props.value);
88
+ if (props.animateOnMount && !isHydrated) animateTo(props.value);
89
89
  });
90
90
 
91
91
  // Stop any pending frame
92
92
  onBeforeUnmount(() => {
93
- if (frame !== null) cancelAnimationFrame(frame);
93
+ if (frame !== null) cancelAnimationFrame(frame);
94
94
  });
95
95
  </script>
96
96
 
97
97
  <template>
98
- <span data-slot="animated-number" :data-direction="direction ?? undefined" :class="cn('inline-block tabular-nums', props.class)">
99
- <span aria-hidden="true">{{ formattedValue }}</span>
100
- <span class="sr-only">{{ accessibleValue }}</span>
101
- </span>
98
+ <!-- The server and the browser can format the same number differently (e.g. Node writes "1000" and Chrome "1.000" in Italian, their locale data differ), so a text mismatch is expected and the browser's formatting wins -->
99
+ <span data-slot="animated-number" :data-direction="direction ?? undefined" :class="cn('inline-block tabular-nums', props.class)">
100
+ <span aria-hidden="true" data-allow-mismatch="text">{{ formattedValue }}</span>
101
+ <span class="sr-only" data-allow-mismatch="text">{{ accessibleValue }}</span>
102
+ </span>
102
103
  </template>