@brumaombra/ui-vintage 0.9.1 → 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 (334) hide show
  1. package/module.mjs +43 -43
  2. package/package.json +175 -175
  3. package/src/components/background-grid/BackgroundGrid.vue +4 -4
  4. package/src/components/blog/AllPostsSection.vue +33 -33
  5. package/src/components/blog/BlogContentRenderer.vue +111 -111
  6. package/src/components/blog/BlogFAQSection.vue +19 -19
  7. package/src/components/blog/BlogHeaderSection.vue +80 -80
  8. package/src/components/blog/BlogInfoSection.vue +47 -47
  9. package/src/components/blog/BlogPostHeader.vue +135 -135
  10. package/src/components/blog/BlogProgressBar.vue +33 -33
  11. package/src/components/blog/BlogSectionTitle.vue +22 -22
  12. package/src/components/blog/BlogTagsSection.vue +25 -25
  13. package/src/components/blog/CategoriesList.vue +14 -14
  14. package/src/components/blog/CategoriesSection.vue +11 -11
  15. package/src/components/blog/CategoryCard.vue +25 -25
  16. package/src/components/blog/HeaderCarousel.vue +97 -97
  17. package/src/components/blog/PostCard.vue +33 -33
  18. package/src/components/blog/PostsList.vue +15 -15
  19. package/src/components/blog/SocialShareSidebar.vue +84 -84
  20. package/src/components/blog/TableOfContents.vue +307 -307
  21. package/src/components/busy-indicator/BusyIndicator.vue +7 -7
  22. package/src/components/busy-indicator/busy-indicator-state.ts +56 -56
  23. package/src/components/card-grid/CardGrid.vue +34 -34
  24. package/src/components/chip/Chip.vue +15 -15
  25. package/src/components/confirm-dialog/ConfirmDialog.vue +50 -50
  26. package/src/components/confirm-dialog/confirm-dialog-state.ts +131 -131
  27. package/src/components/content/BlogList.vue +30 -30
  28. package/src/components/content/BlogTable.vue +36 -36
  29. package/src/components/content/CodeBlock.vue +136 -136
  30. package/src/components/content/Flow.vue +60 -60
  31. package/src/components/content/ProseHr.vue +15 -15
  32. package/src/components/content/ProsePre.vue +12 -12
  33. package/src/components/content/Terminal.vue +59 -59
  34. package/src/components/cookie-consent/CookieConsent.vue +36 -36
  35. package/src/components/cookie-consent/useCookieConsent.ts +37 -37
  36. package/src/components/dashboard-shell/DashboardShell.vue +171 -171
  37. package/src/components/data-list/DataList.vue +6 -6
  38. package/src/components/data-list/DataListItem.vue +4 -4
  39. package/src/components/data-list/DataListLabel.vue +4 -4
  40. package/src/components/data-list/DataListText.vue +4 -4
  41. package/src/components/data-list/DataListValue.vue +4 -4
  42. package/src/components/empty-state-card/EmptyStateCard.vue +40 -40
  43. package/src/components/error-page/ErrorPage.vue +72 -72
  44. package/src/components/info-card/InfoCard.vue +31 -31
  45. package/src/components/landing/landing-content/LandingContent.vue +4 -4
  46. package/src/components/landing/landing-footer/LandingFooter.vue +128 -128
  47. package/src/components/landing/landing-navbar/LandingNavbar.vue +50 -50
  48. package/src/components/landing/landing-shell/LandingShell.vue +36 -36
  49. package/src/components/language-flag/LanguageFlag.vue +25 -25
  50. package/src/components/language-flag/flags/ChineseFlag.vue +8 -8
  51. package/src/components/language-flag/flags/EnglishFlag.vue +7 -7
  52. package/src/components/language-flag/flags/FrenchFlag.vue +5 -5
  53. package/src/components/language-flag/flags/GermanFlag.vue +5 -5
  54. package/src/components/language-flag/flags/ItalianFlag.vue +7 -7
  55. package/src/components/language-flag/flags/JapaneseFlag.vue +4 -4
  56. package/src/components/language-flag/flags/PortugueseFlag.vue +57 -57
  57. package/src/components/language-flag/flags/RussianFlag.vue +5 -5
  58. package/src/components/language-flag/flags/SpanishFlag.vue +4 -4
  59. package/src/components/language-selector/LanguageSelector.vue +76 -76
  60. package/src/components/load-more-button/LoadMoreButton.vue +17 -17
  61. package/src/components/loading-state-card/LoadingStateCard.vue +28 -28
  62. package/src/components/message-dialog/MessageDialog.vue +59 -59
  63. package/src/components/message-dialog/message-dialog-state.ts +105 -105
  64. package/src/components/message-toast/MessageToast.vue +149 -149
  65. package/src/components/message-toast/message-toast-state.ts +146 -146
  66. package/src/components/page-header/PageHeader.vue +34 -34
  67. package/src/components/progress-component/ProgressComponent.vue +33 -33
  68. package/src/components/single-value-card/SingleValueCard.vue +57 -57
  69. package/src/components/slider-form-component/SliderFormComponent.vue +36 -36
  70. package/src/components/stat-strip/StatStrip.vue +22 -22
  71. package/src/components/switch-form-component/SwitchFormComponent.vue +20 -20
  72. package/src/components/text-link/TextLink.vue +14 -14
  73. package/src/components/theme-selector/ThemeSelector.vue +61 -61
  74. package/src/components/theme-selector/theme-transition.ts +63 -63
  75. package/src/components/ui/accordion/Accordion.vue +38 -38
  76. package/src/components/ui/alert/Alert.vue +5 -5
  77. package/src/components/ui/alert/AlertDescription.vue +4 -4
  78. package/src/components/ui/alert/AlertTitle.vue +4 -4
  79. package/src/components/ui/alert/index.ts +2 -2
  80. package/src/components/ui/alert-dialog/AlertDialog.vue +3 -3
  81. package/src/components/ui/alert-dialog/AlertDialogAction.vue +3 -3
  82. package/src/components/ui/alert-dialog/AlertDialogCancel.vue +3 -3
  83. package/src/components/ui/alert-dialog/AlertDialogContent.vue +7 -7
  84. package/src/components/ui/alert-dialog/AlertDialogDescription.vue +3 -3
  85. package/src/components/ui/alert-dialog/AlertDialogFooter.vue +4 -4
  86. package/src/components/ui/alert-dialog/AlertDialogHeader.vue +4 -4
  87. package/src/components/ui/alert-dialog/AlertDialogTitle.vue +3 -3
  88. package/src/components/ui/alert-dialog/AlertDialogTrigger.vue +3 -3
  89. package/src/components/ui/animated-number/AnimatedNumber.vue +49 -48
  90. package/src/components/ui/avatar/Avatar.vue +28 -28
  91. package/src/components/ui/avatar/AvatarFallback.vue +6 -6
  92. package/src/components/ui/avatar/AvatarGroup.vue +40 -40
  93. package/src/components/ui/avatar/AvatarImage.vue +3 -3
  94. package/src/components/ui/avatar/index.ts +17 -17
  95. package/src/components/ui/badge/Badge.vue +20 -20
  96. package/src/components/ui/breadcrumb/Breadcrumb.vue +4 -4
  97. package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +9 -9
  98. package/src/components/ui/breadcrumb/BreadcrumbItem.vue +4 -4
  99. package/src/components/ui/breadcrumb/BreadcrumbLink.vue +4 -4
  100. package/src/components/ui/breadcrumb/BreadcrumbList.vue +4 -4
  101. package/src/components/ui/breadcrumb/BreadcrumbPage.vue +4 -4
  102. package/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +4 -4
  103. package/src/components/ui/busy/Busy.vue +33 -33
  104. package/src/components/ui/button/Button.vue +23 -23
  105. package/src/components/ui/button/index.ts +56 -56
  106. package/src/components/ui/calendar/Calendar.vue +138 -138
  107. package/src/components/ui/calendar/CalendarCell.vue +7 -7
  108. package/src/components/ui/calendar/CalendarCellTrigger.vue +18 -18
  109. package/src/components/ui/calendar/CalendarGrid.vue +3 -3
  110. package/src/components/ui/calendar/CalendarGridBody.vue +3 -3
  111. package/src/components/ui/calendar/CalendarGridHead.vue +3 -3
  112. package/src/components/ui/calendar/CalendarGridRow.vue +3 -3
  113. package/src/components/ui/calendar/CalendarHeadCell.vue +7 -7
  114. package/src/components/ui/calendar/CalendarHeader.vue +7 -7
  115. package/src/components/ui/calendar/CalendarHeading.vue +6 -6
  116. package/src/components/ui/calendar/CalendarNextButton.vue +15 -15
  117. package/src/components/ui/calendar/CalendarPrevButton.vue +15 -15
  118. package/src/components/ui/calendar/index.ts +4 -4
  119. package/src/components/ui/card/Card.vue +20 -20
  120. package/src/components/ui/card/CardAction.vue +8 -8
  121. package/src/components/ui/card/CardContent.vue +4 -4
  122. package/src/components/ui/card/CardDescription.vue +12 -12
  123. package/src/components/ui/card/CardFooter.vue +4 -4
  124. package/src/components/ui/card/CardHeader.vue +8 -8
  125. package/src/components/ui/card/CardText.vue +8 -8
  126. package/src/components/ui/card/CardTitle.vue +8 -8
  127. package/src/components/ui/checkbox/Checkbox.vue +27 -27
  128. package/src/components/ui/collapsible/Collapsible.vue +3 -3
  129. package/src/components/ui/collapsible/CollapsibleContent.vue +3 -3
  130. package/src/components/ui/collapsible/CollapsibleTrigger.vue +3 -3
  131. package/src/components/ui/combobox/Combobox.vue +4 -4
  132. package/src/components/ui/combobox/ComboboxAnchor.vue +16 -16
  133. package/src/components/ui/combobox/ComboboxEmpty.vue +3 -3
  134. package/src/components/ui/combobox/ComboboxGroup.vue +9 -9
  135. package/src/components/ui/combobox/ComboboxInput.vue +6 -6
  136. package/src/components/ui/combobox/ComboboxItem.vue +17 -17
  137. package/src/components/ui/combobox/ComboboxList.vue +19 -19
  138. package/src/components/ui/combobox/ComboboxSelect.vue +77 -77
  139. package/src/components/ui/combobox/ComboboxSeparator.vue +1 -1
  140. package/src/components/ui/combobox/ComboboxTrigger.vue +10 -10
  141. package/src/components/ui/combobox/types.ts +5 -5
  142. package/src/components/ui/command/Command.vue +48 -48
  143. package/src/components/ui/command/CommandDialog.vue +14 -14
  144. package/src/components/ui/command/CommandEmpty.vue +4 -4
  145. package/src/components/ui/command/CommandGroup.vue +23 -23
  146. package/src/components/ui/command/CommandInput.vue +9 -9
  147. package/src/components/ui/command/CommandItem.vue +40 -40
  148. package/src/components/ui/command/CommandList.vue +9 -9
  149. package/src/components/ui/command/CommandSeparator.vue +3 -3
  150. package/src/components/ui/command/CommandShortcut.vue +8 -8
  151. package/src/components/ui/command/index.ts +11 -11
  152. package/src/components/ui/data-table/DataTable.vue +145 -145
  153. package/src/components/ui/data-table/index.ts +11 -11
  154. package/src/components/ui/date-picker/DatePicker.vue +31 -31
  155. package/src/components/ui/date-time-picker/DateTimePicker.vue +97 -97
  156. package/src/components/ui/dialog/Dialog.vue +3 -3
  157. package/src/components/ui/dialog/DialogClose.vue +3 -3
  158. package/src/components/ui/dialog/DialogContent.vue +21 -21
  159. package/src/components/ui/dialog/DialogDescription.vue +7 -7
  160. package/src/components/ui/dialog/DialogFooter.vue +8 -8
  161. package/src/components/ui/dialog/DialogHeader.vue +4 -4
  162. package/src/components/ui/dialog/DialogOverlay.vue +7 -7
  163. package/src/components/ui/dialog/DialogScrollContent.vue +32 -32
  164. package/src/components/ui/dialog/DialogTitle.vue +3 -3
  165. package/src/components/ui/dialog/DialogTrigger.vue +3 -3
  166. package/src/components/ui/dropdown-menu/DropdownMenu.vue +3 -3
  167. package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +11 -11
  168. package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +15 -15
  169. package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +3 -3
  170. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +12 -12
  171. package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +9 -9
  172. package/src/components/ui/dropdown-menu/DropdownMenuPortal.vue +3 -3
  173. package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +3 -3
  174. package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +11 -11
  175. package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +1 -1
  176. package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +4 -4
  177. package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +3 -3
  178. package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +15 -15
  179. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +15 -15
  180. package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +3 -3
  181. package/src/components/ui/dropdown-menu/classes.ts +16 -16
  182. package/src/components/ui/field/Field.vue +5 -5
  183. package/src/components/ui/field/FieldContent.vue +8 -8
  184. package/src/components/ui/field/FieldDescription.vue +10 -10
  185. package/src/components/ui/field/FieldError.vue +39 -39
  186. package/src/components/ui/field/FieldGroup.vue +8 -8
  187. package/src/components/ui/field/FieldLabel.vue +10 -10
  188. package/src/components/ui/field/FieldLegend.vue +11 -11
  189. package/src/components/ui/field/FieldSeparator.vue +11 -11
  190. package/src/components/ui/field/FieldSet.vue +9 -9
  191. package/src/components/ui/field/FieldTitle.vue +8 -8
  192. package/src/components/ui/field/field-context.ts +36 -36
  193. package/src/components/ui/field/index.ts +22 -22
  194. package/src/components/ui/file-dropzone/FileDropzone.vue +144 -144
  195. package/src/components/ui/file-dropzone/index.ts +19 -19
  196. package/src/components/ui/hover-card/HoverCard.vue +5 -5
  197. package/src/components/ui/hover-card/HoverCardContent.vue +13 -13
  198. package/src/components/ui/hover-card/HoverCardTrigger.vue +3 -3
  199. package/src/components/ui/input/Input.vue +13 -13
  200. package/src/components/ui/kbd/Kbd.vue +10 -10
  201. package/src/components/ui/kbd/KbdGroup.vue +49 -49
  202. package/src/components/ui/label/Label.vue +7 -7
  203. package/src/components/ui/native-select/NativeSelect.vue +16 -16
  204. package/src/components/ui/native-select/NativeSelectOptGroup.vue +3 -3
  205. package/src/components/ui/native-select/NativeSelectOption.vue +3 -3
  206. package/src/components/ui/number-field/NumberField.vue +88 -88
  207. package/src/components/ui/pagination/Pagination.vue +4 -4
  208. package/src/components/ui/pagination/PaginationContent.vue +4 -4
  209. package/src/components/ui/pagination/PaginationEllipsis.vue +9 -9
  210. package/src/components/ui/pagination/PaginationFirst.vue +8 -8
  211. package/src/components/ui/pagination/PaginationItem.vue +14 -14
  212. package/src/components/ui/pagination/PaginationLast.vue +8 -8
  213. package/src/components/ui/pagination/PaginationNext.vue +8 -8
  214. package/src/components/ui/pagination/PaginationPrevious.vue +8 -8
  215. package/src/components/ui/pagination/SimplePagination.vue +25 -25
  216. package/src/components/ui/pin-input/PinInput.vue +20 -20
  217. package/src/components/ui/pin-input/PinInputInput.vue +17 -17
  218. package/src/components/ui/pin-input/context.ts +1 -1
  219. package/src/components/ui/popover/Popover.vue +3 -3
  220. package/src/components/ui/popover/PopoverAnchor.vue +3 -3
  221. package/src/components/ui/popover/PopoverContent.vue +12 -12
  222. package/src/components/ui/popover/PopoverTrigger.vue +3 -3
  223. package/src/components/ui/progress/Progress.vue +16 -16
  224. package/src/components/ui/radio-group/RadioGroup.vue +7 -7
  225. package/src/components/ui/radio-group/RadioGroupCard.vue +39 -39
  226. package/src/components/ui/radio-group/RadioGroupItem.vue +20 -20
  227. package/src/components/ui/scroll-area/ScrollArea.vue +14 -14
  228. package/src/components/ui/scroll-area/ScrollBar.vue +12 -12
  229. package/src/components/ui/select/Select.vue +3 -3
  230. package/src/components/ui/select/SelectContent.vue +25 -25
  231. package/src/components/ui/select/SelectGroup.vue +3 -3
  232. package/src/components/ui/select/SelectItem.vue +24 -24
  233. package/src/components/ui/select/SelectItemContent.vue +32 -32
  234. package/src/components/ui/select/SelectItemText.vue +3 -3
  235. package/src/components/ui/select/SelectLabel.vue +3 -3
  236. package/src/components/ui/select/SelectScrollDownButton.vue +9 -9
  237. package/src/components/ui/select/SelectScrollUpButton.vue +9 -9
  238. package/src/components/ui/select/SelectSeparator.vue +2 -2
  239. package/src/components/ui/select/SelectTrigger.vue +14 -14
  240. package/src/components/ui/select/SelectValue.vue +3 -3
  241. package/src/components/ui/select/SelectValueContent.vue +39 -39
  242. package/src/components/ui/separator/Separator.vue +7 -7
  243. package/src/components/ui/sheet/Sheet.vue +3 -3
  244. package/src/components/ui/sheet/SheetClose.vue +3 -3
  245. package/src/components/ui/sheet/SheetContent.vue +22 -22
  246. package/src/components/ui/sheet/SheetDescription.vue +3 -3
  247. package/src/components/ui/sheet/SheetFooter.vue +3 -3
  248. package/src/components/ui/sheet/SheetHeader.vue +3 -3
  249. package/src/components/ui/sheet/SheetOverlay.vue +7 -7
  250. package/src/components/ui/sheet/SheetTitle.vue +3 -3
  251. package/src/components/ui/sheet/SheetTrigger.vue +3 -3
  252. package/src/components/ui/sidebar/Sidebar.vue +57 -57
  253. package/src/components/ui/sidebar/SidebarContent.vue +8 -8
  254. package/src/components/ui/sidebar/SidebarFooter.vue +8 -8
  255. package/src/components/ui/sidebar/SidebarGroup.vue +8 -8
  256. package/src/components/ui/sidebar/SidebarGroupAction.vue +9 -9
  257. package/src/components/ui/sidebar/SidebarGroupContent.vue +4 -4
  258. package/src/components/ui/sidebar/SidebarGroupLabel.vue +8 -8
  259. package/src/components/ui/sidebar/SidebarHeader.vue +8 -8
  260. package/src/components/ui/sidebar/SidebarInput.vue +8 -8
  261. package/src/components/ui/sidebar/SidebarInset.vue +9 -9
  262. package/src/components/ui/sidebar/SidebarMenu.vue +4 -4
  263. package/src/components/ui/sidebar/SidebarMenuAction.vue +18 -18
  264. package/src/components/ui/sidebar/SidebarMenuBadge.vue +14 -14
  265. package/src/components/ui/sidebar/SidebarMenuButton.vue +21 -21
  266. package/src/components/ui/sidebar/SidebarMenuButtonChild.vue +10 -10
  267. package/src/components/ui/sidebar/SidebarMenuItem.vue +4 -4
  268. package/src/components/ui/sidebar/SidebarMenuSkeleton.vue +10 -10
  269. package/src/components/ui/sidebar/SidebarMenuSub.vue +9 -9
  270. package/src/components/ui/sidebar/SidebarMenuSubButton.vue +17 -17
  271. package/src/components/ui/sidebar/SidebarMenuSubItem.vue +4 -4
  272. package/src/components/ui/sidebar/SidebarProvider.vue +46 -46
  273. package/src/components/ui/sidebar/SidebarRail.vue +13 -13
  274. package/src/components/ui/sidebar/SidebarSeparator.vue +4 -4
  275. package/src/components/ui/sidebar/SidebarTrigger.vue +5 -5
  276. package/src/components/ui/sidebar/index.ts +29 -29
  277. package/src/components/ui/sidebar/utils.ts +7 -7
  278. package/src/components/ui/skeleton/Skeleton.vue +6 -6
  279. package/src/components/ui/slider/Slider.vue +14 -14
  280. package/src/components/ui/spinner/Spinner.vue +21 -21
  281. package/src/components/ui/stepper/SimpleStepper.vue +26 -26
  282. package/src/components/ui/stepper/Stepper.vue +6 -6
  283. package/src/components/ui/stepper/StepperDescription.vue +5 -5
  284. package/src/components/ui/stepper/StepperIndicator.vue +17 -17
  285. package/src/components/ui/stepper/StepperItem.vue +4 -4
  286. package/src/components/ui/stepper/StepperSeparator.vue +7 -7
  287. package/src/components/ui/stepper/StepperTitle.vue +5 -5
  288. package/src/components/ui/stepper/StepperTrigger.vue +4 -4
  289. package/src/components/ui/stepper/index.ts +3 -3
  290. package/src/components/ui/switch/Switch.vue +15 -15
  291. package/src/components/ui/table/Table.vue +6 -6
  292. package/src/components/ui/table/TableBody.vue +4 -4
  293. package/src/components/ui/table/TableCaption.vue +4 -4
  294. package/src/components/ui/table/TableCell.vue +8 -8
  295. package/src/components/ui/table/TableEmpty.vue +14 -14
  296. package/src/components/ui/table/TableFooter.vue +8 -8
  297. package/src/components/ui/table/TableHead.vue +8 -8
  298. package/src/components/ui/table/TableHeader.vue +4 -4
  299. package/src/components/ui/table/TableRow.vue +8 -8
  300. package/src/components/ui/tabs/Tabs.vue +4 -4
  301. package/src/components/ui/tabs/TabsContent.vue +4 -4
  302. package/src/components/ui/tabs/TabsList.vue +15 -15
  303. package/src/components/ui/tabs/TabsTrigger.vue +10 -10
  304. package/src/components/ui/tags-input/TagsInput.vue +17 -17
  305. package/src/components/ui/tags-input/TagsInputInput.vue +6 -6
  306. package/src/components/ui/tags-input/TagsInputItem.vue +15 -15
  307. package/src/components/ui/tags-input/TagsInputItemDelete.vue +12 -12
  308. package/src/components/ui/tags-input/TagsInputItemText.vue +4 -4
  309. package/src/components/ui/textarea/Textarea.vue +12 -12
  310. package/src/components/ui/time-picker/TimePicker.vue +25 -25
  311. package/src/components/ui/toggle-group/Toggle.vue +10 -10
  312. package/src/components/ui/toggle-group/ToggleGroup.vue +38 -38
  313. package/src/components/ui/toggle-group/ToggleGroupItem.vue +9 -9
  314. package/src/components/ui/toggle-group/index.ts +46 -46
  315. package/src/components/ui/tooltip/Tooltip.vue +3 -3
  316. package/src/components/ui/tooltip/TooltipContent.vue +13 -13
  317. package/src/components/ui/tooltip/TooltipProvider.vue +4 -4
  318. package/src/components/ui/tooltip/TooltipTrigger.vue +3 -3
  319. package/src/i18n/de.json +185 -185
  320. package/src/i18n/en.json +185 -185
  321. package/src/i18n/es.json +185 -185
  322. package/src/i18n/fr.json +185 -185
  323. package/src/i18n/it.json +185 -185
  324. package/src/i18n/ja.json +185 -185
  325. package/src/i18n/pt.json +185 -185
  326. package/src/i18n/ru.json +185 -185
  327. package/src/i18n/zh.json +185 -185
  328. package/src/lib/color-tokens.ts +24 -24
  329. package/src/lib/i18n.ts +65 -65
  330. package/src/lib/utils.ts +33 -33
  331. package/src/nuxt-components.d.ts +2 -2
  332. package/src/runtime/ui-vintage-i18n-plugin.ts +49 -49
  333. package/src/shims-vue.d.ts +3 -3
  334. package/src/styles.css +377 -377
@@ -21,22 +21,22 @@ import { formatFileSize, isFileAccepted } from '.';
21
21
 
22
22
  // Props
23
23
  const props = withDefaults(defineProps<{
24
- accept?: string;
25
- multiple?: boolean;
26
- maxSize?: number;
27
- maxFiles?: number;
28
- disabled?: boolean;
29
- title?: string;
30
- description?: string;
31
- class?: HTMLAttributes['class'];
24
+ accept?: string;
25
+ multiple?: boolean;
26
+ maxSize?: number;
27
+ maxFiles?: number;
28
+ disabled?: boolean;
29
+ title?: string;
30
+ description?: string;
31
+ class?: HTMLAttributes['class'];
32
32
  }>(), {
33
- multiple: true,
34
- disabled: false,
33
+ multiple: true,
34
+ disabled: false,
35
35
  });
36
36
 
37
37
  // Emits
38
38
  const emits = defineEmits<{
39
- reject: [rejections: FileRejection[]];
39
+ reject: [rejections: FileRejection[]];
40
40
  }>();
41
41
 
42
42
  // Selected files (v-model)
@@ -56,204 +56,204 @@ const resolvedDescription = computed(() => props.description || t('uiVintage.fil
56
56
 
57
57
  // Constraint hints shown under the description
58
58
  const hints = computed(() => {
59
- const list: string[] = [];
60
- if (props.maxSize) list.push(t('uiVintage.fileDropzone.maxSize', { size: formatFileSize(props.maxSize) }));
61
- if (props.multiple && props.maxFiles) list.push(t('uiVintage.fileDropzone.maxFiles', { count: props.maxFiles }));
62
- return list;
59
+ const list: string[] = [];
60
+ if (props.maxSize) list.push(t('uiVintage.fileDropzone.maxSize', { size: formatFileSize(props.maxSize) }));
61
+ if (props.multiple && props.maxFiles) list.push(t('uiVintage.fileDropzone.maxFiles', { count: props.maxFiles }));
62
+ return list;
63
63
  });
64
64
 
65
65
  // Stable keys for the file list
66
66
  const fileIds = new WeakMap<File, number>();
67
67
  let nextFileId = 0;
68
68
  function getFileId(file: File) {
69
- if (!fileIds.has(file)) fileIds.set(file, nextFileId++);
70
- return fileIds.get(file)!;
69
+ if (!fileIds.has(file)) fileIds.set(file, nextFileId++);
70
+ return fileIds.get(file)!;
71
71
  }
72
72
 
73
73
  // Pick an icon from the file type or extension
74
74
  function getFileIcon(file: File): HugeiconsIconDefinition {
75
- const type = file.type.toLowerCase();
76
- const extension = file.name.split('.').pop()?.toLowerCase() ?? '';
77
- if (type.startsWith('image/')) return Image01Icon;
78
- if (type.startsWith('video/')) return Video01Icon;
79
- if (type.startsWith('audio/')) return MusicNote01Icon;
80
- if (type === 'application/pdf' || extension === 'pdf') return Pdf01Icon;
81
- if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) return FileZipIcon;
82
- if (['doc', 'docx', 'odt', 'rtf', 'txt', 'md'].includes(extension)) return Doc01Icon;
83
- if (['xls', 'xlsx', 'ods', 'csv'].includes(extension)) return Xls01Icon;
84
- return File01Icon;
75
+ const type = file.type.toLowerCase();
76
+ const extension = file.name.split('.').pop()?.toLowerCase() ?? '';
77
+ if (type.startsWith('image/')) return Image01Icon;
78
+ if (type.startsWith('video/')) return Video01Icon;
79
+ if (type.startsWith('audio/')) return MusicNote01Icon;
80
+ if (type === 'application/pdf' || extension === 'pdf') return Pdf01Icon;
81
+ if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) return FileZipIcon;
82
+ if (['doc', 'docx', 'odt', 'rtf', 'txt', 'md'].includes(extension)) return Doc01Icon;
83
+ if (['xls', 'xlsx', 'ods', 'csv'].includes(extension)) return Xls01Icon;
84
+ return File01Icon;
85
85
  }
86
86
 
87
87
  // Briefly shake the drop zone when something is refused
88
88
  function flashRejected() {
89
- isRejected.value = false;
90
- clearTimeout(rejectTimer);
91
- requestAnimationFrame(() => {
92
- isRejected.value = true;
93
- rejectTimer = setTimeout(() => (isRejected.value = false), 450);
94
- });
89
+ isRejected.value = false;
90
+ clearTimeout(rejectTimer);
91
+ requestAnimationFrame(() => {
92
+ isRejected.value = true;
93
+ rejectTimer = setTimeout(() => (isRejected.value = false), 450);
94
+ });
95
95
  }
96
96
 
97
97
  // Validate incoming files and merge them into the model
98
98
  function addFiles(incoming: File[]) {
99
- if (props.disabled || incoming.length === 0) return;
100
- const rejections: FileRejection[] = [];
101
- const accepted: File[] = [];
99
+ if (props.disabled || incoming.length === 0) return;
100
+ const rejections: FileRejection[] = [];
101
+ const accepted: File[] = [];
102
102
 
103
- // Type and size checks
104
- for (const file of incoming) {
105
- if (!isFileAccepted(file, props.accept)) rejections.push({ file, reason: 'type' });
106
- else if (props.maxSize !== undefined && file.size > props.maxSize) rejections.push({ file, reason: 'size' });
107
- else accepted.push(file);
108
- }
103
+ // Type and size checks
104
+ for (const file of incoming) {
105
+ if (!isFileAccepted(file, props.accept)) rejections.push({ file, reason: 'type' });
106
+ else if (props.maxSize !== undefined && file.size > props.maxSize) rejections.push({ file, reason: 'size' });
107
+ else accepted.push(file);
108
+ }
109
109
 
110
- let next: File[];
111
- if (!props.multiple) {
112
- // Single mode keeps only the first valid file
113
- next = accepted.slice(0, 1);
114
- for (const file of accepted.slice(1)) rejections.push({ file, reason: 'count' });
115
- if (next.length === 0) next = [...files.value];
116
- } else {
117
- // Skip exact duplicates already in the list
118
- const isDuplicate = (file: File) => files.value.some(existing => existing.name === file.name && existing.size === file.size && existing.lastModified === file.lastModified);
119
- const fresh = accepted.filter(file => !isDuplicate(file));
110
+ let next: File[];
111
+ if (!props.multiple) {
112
+ // Single mode keeps only the first valid file
113
+ next = accepted.slice(0, 1);
114
+ for (const file of accepted.slice(1)) rejections.push({ file, reason: 'count' });
115
+ if (next.length === 0) next = [...files.value];
116
+ } else {
117
+ // Skip exact duplicates already in the list
118
+ const isDuplicate = (file: File) => files.value.some(existing => existing.name === file.name && existing.size === file.size && existing.lastModified === file.lastModified);
119
+ const fresh = accepted.filter(file => !isDuplicate(file));
120
120
 
121
- // Enforce the max file count
122
- const room = props.maxFiles !== undefined ? Math.max(0, props.maxFiles - files.value.length) : fresh.length;
123
- for (const file of fresh.slice(room)) rejections.push({ file, reason: 'count' });
124
- next = [...files.value, ...fresh.slice(0, room)];
125
- }
121
+ // Enforce the max file count
122
+ const room = props.maxFiles !== undefined ? Math.max(0, props.maxFiles - files.value.length) : fresh.length;
123
+ for (const file of fresh.slice(room)) rejections.push({ file, reason: 'count' });
124
+ next = [...files.value, ...fresh.slice(0, room)];
125
+ }
126
126
 
127
- if (next.length !== files.value.length || next.some((file, index) => file !== files.value[index])) files.value = next;
128
- if (rejections.length > 0) {
129
- emits('reject', rejections);
130
- flashRejected();
131
- }
127
+ if (next.length !== files.value.length || next.some((file, index) => file !== files.value[index])) files.value = next;
128
+ if (rejections.length > 0) {
129
+ emits('reject', rejections);
130
+ flashRejected();
131
+ }
132
132
  }
133
133
 
134
134
  // Remove a file from the list
135
135
  function removeFile(file: File) {
136
- files.value = files.value.filter(existing => existing !== file);
136
+ files.value = files.value.filter(existing => existing !== file);
137
137
  }
138
138
 
139
139
  // Open the native file picker
140
140
  function openPicker() {
141
- if (props.disabled) return;
142
- inputRef.value?.click();
141
+ if (props.disabled) return;
142
+ inputRef.value?.click();
143
143
  }
144
144
 
145
145
  // Native picker selection
146
146
  function onInputChange(event: Event) {
147
- const input = event.target as HTMLInputElement;
148
- addFiles(Array.from(input.files ?? []));
149
- input.value = '';
147
+ const input = event.target as HTMLInputElement;
148
+ addFiles(Array.from(input.files ?? []));
149
+ input.value = '';
150
150
  }
151
151
 
152
152
  // Drag tracking (depth counter avoids flicker when crossing child elements)
153
153
  function onDragEnter(event: DragEvent) {
154
- if (props.disabled || !event.dataTransfer?.types.includes('Files')) return;
155
- dragDepth++;
156
- isDragging.value = true;
154
+ if (props.disabled || !event.dataTransfer?.types.includes('Files')) return;
155
+ dragDepth++;
156
+ isDragging.value = true;
157
157
  }
158
158
 
159
159
  function onDragOver(event: DragEvent) {
160
- if (props.disabled || !event.dataTransfer) return;
161
- event.dataTransfer.dropEffect = 'copy';
160
+ if (props.disabled || !event.dataTransfer) return;
161
+ event.dataTransfer.dropEffect = 'copy';
162
162
  }
163
163
 
164
164
  function onDragLeave() {
165
- if (!isDragging.value) return;
166
- dragDepth = Math.max(0, dragDepth - 1);
167
- if (dragDepth === 0) isDragging.value = false;
165
+ if (!isDragging.value) return;
166
+ dragDepth = Math.max(0, dragDepth - 1);
167
+ if (dragDepth === 0) isDragging.value = false;
168
168
  }
169
169
 
170
170
  function onDrop(event: DragEvent) {
171
- dragDepth = 0;
172
- isDragging.value = false;
173
- if (props.disabled) return;
174
- addFiles(Array.from(event.dataTransfer?.files ?? []));
171
+ dragDepth = 0;
172
+ isDragging.value = false;
173
+ if (props.disabled) return;
174
+ addFiles(Array.from(event.dataTransfer?.files ?? []));
175
175
  }
176
176
 
177
177
  onBeforeUnmount(() => clearTimeout(rejectTimer));
178
178
  </script>
179
179
 
180
180
  <template>
181
- <div data-slot="file-dropzone" :class="cn('flex w-full flex-col gap-3', props.class)">
182
- <!-- Drop area -->
183
- <div role="button" :tabindex="props.disabled ? -1 : 0" :aria-disabled="props.disabled || undefined" :aria-label="resolvedTitle" :data-dragging="isDragging ? '' : undefined" :data-disabled="props.disabled ? '' : undefined" :class="cn(
184
- 'group/dropzone relative isolate flex min-h-44 cursor-pointer select-none flex-col items-center justify-center gap-3 rounded border-2 border-dashed border-border bg-surface/50 px-6 py-8 text-center outline-none',
185
- '[transition:border-color_150ms,background-color_220ms,box-shadow_220ms,transform_420ms_var(--ease-spring)]',
186
- 'hover:border-border-strong hover:bg-surface focus-visible:ring-[3px] focus-visible:ring-ring/45 active:scale-[0.99] active:duration-75',
187
- 'data-dragging:scale-[1.01] data-dragging:border-transparent data-dragging:bg-primary/5 data-dragging:hover:border-transparent data-dragging:hover:bg-primary/5',
188
- 'data-disabled:cursor-not-allowed data-disabled:opacity-50 data-disabled:hover:border-border data-disabled:hover:bg-surface/50 data-disabled:active:scale-100',
189
- isRejected && 'animate-uv-shake border-destructive/70'
190
- )" @click="openPicker" @keydown.enter.prevent="openPicker" @keydown.space.prevent="openPicker" @dragenter.prevent="onDragEnter" @dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop">
191
- <!-- Marching dashed border while dragging -->
192
- <span aria-hidden="true" class="uv-dropzone-ants pointer-events-none absolute -inset-0.5 rounded opacity-0 transition-opacity duration-150 group-data-dragging/dropzone:opacity-100" />
181
+ <div data-slot="file-dropzone" :class="cn('flex w-full flex-col gap-3', props.class)">
182
+ <!-- Drop area -->
183
+ <div role="button" :tabindex="props.disabled ? -1 : 0" :aria-disabled="props.disabled || undefined" :aria-label="resolvedTitle" :data-dragging="isDragging ? '' : undefined" :data-disabled="props.disabled ? '' : undefined" :class="cn(
184
+ 'group/dropzone relative isolate flex min-h-44 cursor-pointer select-none flex-col items-center justify-center gap-3 rounded border-2 border-dashed border-border bg-surface/50 px-6 py-8 text-center outline-none',
185
+ '[transition:border-color_150ms,background-color_220ms,box-shadow_220ms,transform_420ms_var(--ease-spring)]',
186
+ 'hover:border-border-strong hover:bg-surface focus-visible:ring-[3px] focus-visible:ring-ring/45 active:scale-[0.99] active:duration-75',
187
+ 'data-dragging:scale-[1.01] data-dragging:border-transparent data-dragging:bg-primary/5 data-dragging:hover:border-transparent data-dragging:hover:bg-primary/5',
188
+ 'data-disabled:cursor-not-allowed data-disabled:opacity-50 data-disabled:hover:border-border data-disabled:hover:bg-surface/50 data-disabled:active:scale-100',
189
+ isRejected && 'animate-uv-shake border-destructive/70'
190
+ )" @click="openPicker" @keydown.enter.prevent="openPicker" @keydown.space.prevent="openPicker" @dragenter.prevent="onDragEnter" @dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop">
191
+ <!-- Marching dashed border while dragging -->
192
+ <span aria-hidden="true" class="uv-dropzone-ants pointer-events-none absolute -inset-0.5 rounded opacity-0 transition-opacity duration-150 group-data-dragging/dropzone:opacity-100" />
193
193
 
194
- <!-- Upload icon (lifts and grows while dragging) -->
195
- <span aria-hidden="true" class="relative flex size-14 items-center justify-center rounded border border-border bg-card text-muted-foreground shadow-elevated-sm transition-[translate,scale,color,border-color,box-shadow] duration-420 ease-bounce group-hover/dropzone:-translate-y-0.5 group-hover/dropzone:text-foreground group-data-dragging/dropzone:-translate-y-2 group-data-dragging/dropzone:scale-115 group-data-dragging/dropzone:border-primary/50 group-data-dragging/dropzone:text-primary group-data-dragging/dropzone:shadow-elevated-md">
196
- <HugeiconsIcon :icon="CloudUploadIcon" class="size-6" />
197
- </span>
194
+ <!-- Upload icon (lifts and grows while dragging) -->
195
+ <span aria-hidden="true" class="relative flex size-14 items-center justify-center rounded border border-border bg-card text-muted-foreground shadow-elevated-sm transition-[translate,scale,color,border-color,box-shadow] duration-420 ease-bounce group-hover/dropzone:-translate-y-0.5 group-hover/dropzone:text-foreground group-data-dragging/dropzone:-translate-y-2 group-data-dragging/dropzone:scale-115 group-data-dragging/dropzone:border-primary/50 group-data-dragging/dropzone:text-primary group-data-dragging/dropzone:shadow-elevated-md">
196
+ <HugeiconsIcon :icon="CloudUploadIcon" class="size-6" />
197
+ </span>
198
198
 
199
- <!-- Texts -->
200
- <div class="flex flex-col items-center gap-1">
201
- <p class="text-xs font-semibold text-foreground sm:text-sm">
202
- {{ isDragging ? t('uiVintage.fileDropzone.dropActive') : resolvedTitle }}
203
- </p>
204
- <p class="text-xs text-muted-foreground">{{ resolvedDescription }}</p>
205
- <p v-if="hints.length" class="text-xs text-muted-foreground/80">{{ hints.join(' · ') }}</p>
206
- </div>
199
+ <!-- Texts -->
200
+ <div class="flex flex-col items-center gap-1">
201
+ <p class="text-xs font-semibold text-foreground sm:text-sm">
202
+ {{ isDragging ? t('uiVintage.fileDropzone.dropActive') : resolvedTitle }}
203
+ </p>
204
+ <p class="text-xs text-muted-foreground">{{ resolvedDescription }}</p>
205
+ <p v-if="hints.length" class="text-xs text-muted-foreground/80">{{ hints.join(' · ') }}</p>
206
+ </div>
207
207
 
208
- <!-- Browse hint (decorative, the whole zone is the button) -->
209
- <span aria-hidden="true" :class="cn(buttonVariants({ variant: 'secondary', size: 'sm' }), 'pointer-events-none transition-opacity group-data-dragging/dropzone:opacity-0')">
210
- {{ t('uiVintage.fileDropzone.browse') }}
211
- </span>
208
+ <!-- Browse hint (decorative, the whole zone is the button) -->
209
+ <span aria-hidden="true" :class="cn(buttonVariants({ variant: 'secondary', size: 'sm' }), 'pointer-events-none transition-opacity group-data-dragging/dropzone:opacity-0')">
210
+ {{ t('uiVintage.fileDropzone.browse') }}
211
+ </span>
212
212
 
213
- <!-- Hidden native input -->
214
- <input ref="inputRef" type="file" class="hidden" tabindex="-1" :accept="props.accept" :multiple="props.multiple" :disabled="props.disabled" @change="onInputChange" @click.stop>
215
- </div>
213
+ <!-- Hidden native input -->
214
+ <input ref="inputRef" type="file" class="hidden" tabindex="-1" :accept="props.accept" :multiple="props.multiple" :disabled="props.disabled" @change="onInputChange" @click.stop>
215
+ </div>
216
216
 
217
- <!-- Selected files -->
218
- <TransitionGroup tag="ul" data-slot="file-dropzone-list" :aria-label="t('uiVintage.fileDropzone.files')" class="flex flex-col" enter-active-class="transition-[opacity,translate,filter] duration-420 ease-out-expo" enter-from-class="translate-y-2.5 opacity-0 blur-[3px]" leave-active-class="transition-[grid-template-rows,opacity,scale] duration-220 ease-snappy" leave-to-class="grid-rows-[0fr] scale-[0.98] opacity-0">
219
- <li v-for="file in files" :key="getFileId(file)" class="grid grid-rows-[1fr]">
220
- <div class="min-h-0 overflow-hidden">
221
- <div class="pb-2">
222
- <!-- File row -->
223
- <div data-slot="file-dropzone-item" class="flex items-center gap-3 rounded border border-border bg-card py-2 pr-2 pl-3 shadow-elevated-sm">
224
- <span class="flex size-9 shrink-0 items-center justify-center rounded-sm bg-surface text-muted-foreground">
225
- <HugeiconsIcon :icon="getFileIcon(file)" class="size-4.5" />
226
- </span>
227
- <div class="flex min-w-0 flex-1 flex-col">
228
- <span class="truncate text-xs font-semibold text-foreground sm:text-sm" :title="file.name">{{ file.name }}</span>
229
- <span class="text-xs text-muted-foreground tabular-nums">{{ formatFileSize(file.size) }}</span>
230
- </div>
231
- <Button variant="ghost" size="icon-sm" :disabled="props.disabled" :aria-label="t('uiVintage.fileDropzone.remove', { name: file.name })" class="hover:text-destructive" @click="removeFile(file)">
232
- <HugeiconsIcon :icon="Cancel01Icon" class="size-4" />
233
- </Button>
234
- </div>
235
- </div>
236
- </div>
237
- </li>
238
- </TransitionGroup>
239
- </div>
217
+ <!-- Selected files -->
218
+ <TransitionGroup tag="ul" data-slot="file-dropzone-list" :aria-label="t('uiVintage.fileDropzone.files')" class="flex flex-col" enter-active-class="transition-[opacity,translate,filter] duration-420 ease-out-expo" enter-from-class="translate-y-2.5 opacity-0 blur-[3px]" leave-active-class="transition-[grid-template-rows,opacity,scale] duration-220 ease-snappy" leave-to-class="grid-rows-[0fr] scale-[0.98] opacity-0">
219
+ <li v-for="file in files" :key="getFileId(file)" class="grid grid-rows-[1fr]">
220
+ <div class="min-h-0 overflow-hidden">
221
+ <div class="pb-2">
222
+ <!-- File row -->
223
+ <div data-slot="file-dropzone-item" class="flex items-center gap-3 rounded border border-border bg-card py-2 pr-2 pl-3 shadow-elevated-sm">
224
+ <span class="flex size-9 shrink-0 items-center justify-center rounded-sm bg-surface text-muted-foreground">
225
+ <HugeiconsIcon :icon="getFileIcon(file)" class="size-4.5" />
226
+ </span>
227
+ <div class="flex min-w-0 flex-1 flex-col">
228
+ <span class="truncate text-xs font-semibold text-foreground sm:text-sm" :title="file.name">{{ file.name }}</span>
229
+ <span class="text-xs text-muted-foreground tabular-nums">{{ formatFileSize(file.size) }}</span>
230
+ </div>
231
+ <Button variant="ghost" size="icon-sm" :disabled="props.disabled" :aria-label="t('uiVintage.fileDropzone.remove', { name: file.name })" class="hover:text-destructive" @click="removeFile(file)">
232
+ <HugeiconsIcon :icon="Cancel01Icon" class="size-4" />
233
+ </Button>
234
+ </div>
235
+ </div>
236
+ </div>
237
+ </li>
238
+ </TransitionGroup>
239
+ </div>
240
240
  </template>
241
241
 
242
242
  <style>
243
243
  /* Animated dashed border shown while files are dragged over the drop zone */
244
244
  .uv-dropzone-ants {
245
- background-image:
246
- linear-gradient(90deg, var(--primary) 55%, transparent 0),
247
- linear-gradient(90deg, var(--primary) 55%, transparent 0),
248
- linear-gradient(0deg, var(--primary) 55%, transparent 0),
249
- linear-gradient(0deg, var(--primary) 55%, transparent 0);
250
- background-size: 16px 2px, 16px 2px, 2px 16px, 2px 16px;
251
- background-position: 0 0, 0 100%, 0 0, 100% 0;
252
- background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
253
- animation: uv-dropzone-march 0.7s linear infinite;
245
+ background-image:
246
+ linear-gradient(90deg, var(--primary) 55%, transparent 0),
247
+ linear-gradient(90deg, var(--primary) 55%, transparent 0),
248
+ linear-gradient(0deg, var(--primary) 55%, transparent 0),
249
+ linear-gradient(0deg, var(--primary) 55%, transparent 0);
250
+ background-size: 16px 2px, 16px 2px, 2px 16px, 2px 16px;
251
+ background-position: 0 0, 0 100%, 0 0, 100% 0;
252
+ background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
253
+ animation: uv-dropzone-march 0.7s linear infinite;
254
254
  }
255
255
 
256
256
  @keyframes uv-dropzone-march {
257
- to { background-position: 16px 0, -16px 100%, 0 -16px, 100% 16px; }
257
+ to { background-position: 16px 0, -16px 100%, 0 -16px, 100% 16px; }
258
258
  }
259
259
  </style>
@@ -3,33 +3,33 @@ export { default as FileDropzone } from './FileDropzone.vue';
3
3
  export type FileRejectionReason = 'type' | 'size' | 'count';
4
4
 
5
5
  export type FileRejection = {
6
- file: File;
7
- reason: FileRejectionReason;
6
+ file: File;
7
+ reason: FileRejectionReason;
8
8
  };
9
9
 
10
10
  // Format a byte count as a human readable size (1024 based)
11
11
  export function formatFileSize(bytes: number): string {
12
- if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';
13
- const units = ['B', 'KB', 'MB', 'GB', 'TB'];
14
- const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
15
- const value = bytes / 1024 ** exponent;
12
+ if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';
13
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
14
+ const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
15
+ const value = bytes / 1024 ** exponent;
16
16
 
17
- // One decimal for small values, none for bytes or large values
18
- const digits = exponent === 0 || value >= 10 ? 0 : 1;
19
- return `${value.toFixed(digits)} ${units[exponent]}`;
17
+ // One decimal for small values, none for bytes or large values
18
+ const digits = exponent === 0 || value >= 10 ? 0 : 1;
19
+ return `${value.toFixed(digits)} ${units[exponent]}`;
20
20
  }
21
21
 
22
22
  // Check a file against an accept string (".pdf,image/*,application/json")
23
23
  export function isFileAccepted(file: File, accept: string | undefined): boolean {
24
- if (!accept) return true;
25
- const tokens = accept.split(',').map(token => token.trim().toLowerCase()).filter(Boolean);
26
- if (tokens.length === 0) return true;
24
+ if (!accept) return true;
25
+ const tokens = accept.split(',').map(token => token.trim().toLowerCase()).filter(Boolean);
26
+ if (tokens.length === 0) return true;
27
27
 
28
- const name = file.name.toLowerCase();
29
- const type = (file.type || '').toLowerCase();
30
- return tokens.some((token) => {
31
- if (token.startsWith('.')) return name.endsWith(token);
32
- if (token.endsWith('/*')) return type.startsWith(token.slice(0, -1));
33
- return type === token;
34
- });
28
+ const name = file.name.toLowerCase();
29
+ const type = (file.type || '').toLowerCase();
30
+ return tokens.some((token) => {
31
+ if (token.startsWith('.')) return name.endsWith(token);
32
+ if (token.endsWith('/*')) return type.startsWith(token.slice(0, -1));
33
+ return type === token;
34
+ });
35
35
  }
@@ -4,8 +4,8 @@ import { HoverCardRoot, useForwardPropsEmits } from 'reka-ui';
4
4
 
5
5
  // Props
6
6
  const props = withDefaults(defineProps<HoverCardRootProps>(), {
7
- openDelay: 350,
8
- closeDelay: 150
7
+ openDelay: 350,
8
+ closeDelay: 150
9
9
  });
10
10
 
11
11
  // Emits
@@ -15,7 +15,7 @@ const forwarded = useForwardPropsEmits(props, emits);
15
15
  </script>
16
16
 
17
17
  <template>
18
- <HoverCardRoot v-slot="slotProps" data-slot="hover-card" v-bind="forwarded">
19
- <slot v-bind="slotProps" />
20
- </HoverCardRoot>
18
+ <HoverCardRoot v-slot="slotProps" data-slot="hover-card" v-bind="forwarded">
19
+ <slot v-bind="slotProps" />
20
+ </HoverCardRoot>
21
21
  </template>
@@ -6,14 +6,14 @@ import { HoverCardContent, HoverCardPortal, useForwardProps } from 'reka-ui';
6
6
  import { cn } from '../../../lib/utils';
7
7
 
8
8
  defineOptions({
9
- inheritAttrs: false
9
+ inheritAttrs: false
10
10
  });
11
11
 
12
12
  // Props
13
13
  const props = withDefaults(defineProps<HoverCardContentProps & { class?: HTMLAttributes['class'] }>(), {
14
- align: 'center',
15
- sideOffset: 8,
16
- collisionPadding: 8
14
+ align: 'center',
15
+ sideOffset: 8,
16
+ collisionPadding: 8
17
17
  });
18
18
 
19
19
  const delegatedProps = reactiveOmit(props, 'class');
@@ -21,13 +21,13 @@ const forwardedProps = useForwardProps(delegatedProps);
21
21
  </script>
22
22
 
23
23
  <template>
24
- <HoverCardPortal>
25
- <HoverCardContent data-slot="hover-card-content" v-bind="{ ...$attrs, ...forwardedProps }" :class="cn(
26
- 'uv-floating-motion bg-popover text-popover-foreground z-50 w-72 rounded border border-border p-4 text-sm shadow-elevated-lg outline-hidden',
27
- props.class
28
- )
29
- ">
30
- <slot />
31
- </HoverCardContent>
32
- </HoverCardPortal>
24
+ <HoverCardPortal>
25
+ <HoverCardContent data-slot="hover-card-content" v-bind="{ ...$attrs, ...forwardedProps }" :class="cn(
26
+ 'uv-floating-motion bg-popover text-popover-foreground z-50 w-72 rounded border border-border p-4 text-sm shadow-elevated-lg outline-hidden',
27
+ props.class
28
+ )
29
+ ">
30
+ <slot />
31
+ </HoverCardContent>
32
+ </HoverCardPortal>
33
33
  </template>
@@ -7,7 +7,7 @@ const props = defineProps<HoverCardTriggerProps>();
7
7
  </script>
8
8
 
9
9
  <template>
10
- <HoverCardTrigger data-slot="hover-card-trigger" v-bind="props">
11
- <slot />
12
- </HoverCardTrigger>
10
+ <HoverCardTrigger data-slot="hover-card-trigger" v-bind="props">
11
+ <slot />
12
+ </HoverCardTrigger>
13
13
  </template>
@@ -6,19 +6,19 @@ import { useFieldControlId } from '../field/field-context';
6
6
 
7
7
  // Props
8
8
  const props = defineProps<{
9
- defaultValue?: string | number;
10
- modelValue?: string | number;
11
- class?: HTMLAttributes['class'];
9
+ defaultValue?: string | number;
10
+ modelValue?: string | number;
11
+ class?: HTMLAttributes['class'];
12
12
  }>();
13
13
 
14
14
  // Emits
15
15
  const emits = defineEmits<{
16
- (e: 'update:modelValue', payload: string | number): void;
16
+ (e: 'update:modelValue', payload: string | number): void;
17
17
  }>();
18
18
 
19
19
  const modelValue = useVModel(props, 'modelValue', emits, {
20
- passive: true,
21
- defaultValue: props.defaultValue
20
+ passive: true,
21
+ defaultValue: props.defaultValue
22
22
  });
23
23
 
24
24
  // Let a surrounding Field label point to this control
@@ -26,11 +26,11 @@ const fieldControlId = useFieldControlId();
26
26
  </script>
27
27
 
28
28
  <template>
29
- <input :id="fieldControlId" v-model="modelValue" data-slot="input" :class="cn(
30
- 'uv-field h-13 w-full min-w-0 rounded border border-input bg-secondary px-4 py-3 text-xs font-semibold text-foreground shadow-elevated-sm outline-none sm:text-sm',
31
- 'selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground placeholder:opacity-60 placeholder:transition-opacity focus-visible:placeholder:opacity-40',
32
- 'file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-semibold file:text-foreground',
33
- 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60 aria-invalid:animate-uv-shake',
34
- props.class
35
- )" />
29
+ <input :id="fieldControlId" v-model="modelValue" data-slot="input" :class="cn(
30
+ 'uv-field h-13 w-full min-w-0 rounded border border-input bg-secondary px-4 py-3 text-xs font-semibold text-foreground shadow-elevated-sm outline-none sm:text-sm',
31
+ 'selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground placeholder:opacity-60 placeholder:transition-opacity focus-visible:placeholder:opacity-40',
32
+ 'file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-semibold file:text-foreground',
33
+ 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60 aria-invalid:animate-uv-shake',
34
+ props.class
35
+ )" />
36
36
  </template>
@@ -4,18 +4,18 @@ import { cn } from '../../../lib/utils';
4
4
 
5
5
  // Props
6
6
  const props = defineProps<{
7
- class?: HTMLAttributes['class'];
8
- pressed?: boolean;
7
+ class?: HTMLAttributes['class'];
8
+ pressed?: boolean;
9
9
  }>();
10
10
  </script>
11
11
 
12
12
  <template>
13
- <kbd data-slot="kbd" :data-pressed="props.pressed ? '' : undefined" :class="cn(
14
- 'inline-flex h-5 min-w-5 select-none items-center justify-center gap-1 rounded-sm border border-b-2 border-border bg-surface px-1.5 font-mono text-[11px] leading-none font-semibold text-muted-foreground shadow-elevated-sm transition-[translate,border-width,box-shadow] duration-100 ease-snappy [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-3',
15
- 'data-pressed:translate-y-px data-pressed:border-b data-pressed:shadow-none',
16
- props.class
17
- )
18
- ">
19
- <slot />
20
- </kbd>
13
+ <kbd data-slot="kbd" :data-pressed="props.pressed ? '' : undefined" :class="cn(
14
+ 'inline-flex h-5 min-w-5 select-none items-center justify-center gap-1 rounded-sm border border-b-2 border-border bg-surface px-1.5 font-mono text-[11px] leading-none font-semibold text-muted-foreground shadow-elevated-sm transition-[translate,border-width,box-shadow] duration-100 ease-snappy [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-3',
15
+ 'data-pressed:translate-y-px data-pressed:border-b data-pressed:shadow-none',
16
+ props.class
17
+ )
18
+ ">
19
+ <slot />
20
+ </kbd>
21
21
  </template>