@brumaombra/ui-vintage 0.9.1 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +102 -2
- package/module.mjs +43 -43
- package/package.json +175 -175
- package/src/components/background-grid/BackgroundGrid.vue +4 -4
- package/src/components/blog/AllPostsSection.vue +33 -33
- package/src/components/blog/BlogCategoriesPage.vue +46 -0
- package/src/components/blog/BlogContentRenderer.vue +111 -111
- package/src/components/blog/BlogFAQSection.vue +19 -19
- package/src/components/blog/BlogHeaderSection.vue +80 -80
- package/src/components/blog/BlogIndexPage.vue +118 -0
- package/src/components/blog/BlogInfoSection.vue +47 -47
- package/src/components/blog/BlogPageBreadcrumbs.vue +43 -0
- package/src/components/blog/BlogPostHeader.vue +135 -135
- package/src/components/blog/BlogPostPage.vue +116 -0
- package/src/components/blog/BlogPostsPage.vue +74 -0
- package/src/components/blog/BlogProgressBar.vue +33 -33
- package/src/components/blog/BlogSectionTitle.vue +22 -22
- package/src/components/blog/BlogTagsPage.vue +46 -0
- package/src/components/blog/BlogTagsSection.vue +25 -25
- package/src/components/blog/BlogViewAllLink.vue +18 -0
- package/src/components/blog/CategoriesList.vue +14 -14
- package/src/components/blog/CategoriesSection.vue +11 -11
- package/src/components/blog/CategoryCard.vue +25 -25
- package/src/components/blog/HeaderCarousel.vue +97 -97
- package/src/components/blog/PostCard.vue +33 -33
- package/src/components/blog/PostsList.vue +15 -15
- package/src/components/blog/SocialShareSidebar.vue +84 -84
- package/src/components/blog/TableOfContents.vue +307 -307
- package/src/components/blog/blogPageUtils.ts +98 -0
- package/src/components/blog/index.ts +12 -1
- package/src/components/busy-indicator/BusyIndicator.vue +7 -7
- package/src/components/busy-indicator/busy-indicator-state.ts +56 -56
- package/src/components/card-grid/CardGrid.vue +34 -34
- package/src/components/chip/Chip.vue +15 -15
- package/src/components/confirm-dialog/ConfirmDialog.vue +50 -50
- package/src/components/confirm-dialog/confirm-dialog-state.ts +131 -131
- package/src/components/content/BlogList.vue +30 -30
- package/src/components/content/BlogTable.vue +36 -36
- package/src/components/content/CodeBlock.vue +136 -136
- package/src/components/content/Flow.vue +60 -60
- package/src/components/content/ProseHr.vue +15 -15
- package/src/components/content/ProsePre.vue +12 -12
- package/src/components/content/Terminal.vue +59 -59
- package/src/components/cookie-consent/CookieConsent.vue +36 -36
- package/src/components/cookie-consent/useCookieConsent.ts +37 -37
- package/src/components/dashboard-shell/DashboardShell.vue +171 -171
- package/src/components/data-list/DataList.vue +6 -6
- package/src/components/data-list/DataListItem.vue +4 -4
- package/src/components/data-list/DataListLabel.vue +4 -4
- package/src/components/data-list/DataListText.vue +4 -4
- package/src/components/data-list/DataListValue.vue +4 -4
- package/src/components/empty-state-card/EmptyStateCard.vue +40 -40
- package/src/components/error-page/ErrorPage.vue +72 -72
- package/src/components/info-card/InfoCard.vue +31 -31
- package/src/components/landing/landing-content/LandingContent.vue +4 -4
- package/src/components/landing/landing-footer/LandingFooter.vue +128 -128
- package/src/components/landing/landing-navbar/LandingNavbar.vue +50 -50
- package/src/components/landing/landing-shell/LandingShell.vue +36 -36
- package/src/components/language-flag/LanguageFlag.vue +25 -25
- package/src/components/language-flag/flags/ChineseFlag.vue +8 -8
- package/src/components/language-flag/flags/EnglishFlag.vue +7 -7
- package/src/components/language-flag/flags/FrenchFlag.vue +5 -5
- package/src/components/language-flag/flags/GermanFlag.vue +5 -5
- package/src/components/language-flag/flags/ItalianFlag.vue +7 -7
- package/src/components/language-flag/flags/JapaneseFlag.vue +4 -4
- package/src/components/language-flag/flags/PortugueseFlag.vue +57 -57
- package/src/components/language-flag/flags/RussianFlag.vue +5 -5
- package/src/components/language-flag/flags/SpanishFlag.vue +4 -4
- package/src/components/language-selector/LanguageSelector.vue +76 -76
- package/src/components/load-more-button/LoadMoreButton.vue +17 -17
- package/src/components/loading-state-card/LoadingStateCard.vue +28 -28
- package/src/components/message-dialog/MessageDialog.vue +59 -59
- package/src/components/message-dialog/message-dialog-state.ts +105 -105
- package/src/components/message-toast/MessageToast.vue +149 -149
- package/src/components/message-toast/message-toast-state.ts +146 -146
- package/src/components/page-header/PageHeader.vue +34 -34
- package/src/components/progress-component/ProgressComponent.vue +33 -33
- package/src/components/single-value-card/SingleValueCard.vue +57 -57
- package/src/components/slider-form-component/SliderFormComponent.vue +36 -36
- package/src/components/stat-strip/StatStrip.vue +22 -22
- package/src/components/switch-form-component/SwitchFormComponent.vue +20 -20
- package/src/components/text-link/TextLink.vue +14 -14
- package/src/components/theme-selector/ThemeSelector.vue +61 -61
- package/src/components/theme-selector/theme-transition.ts +63 -63
- package/src/components/ui/accordion/Accordion.vue +38 -38
- package/src/components/ui/alert/Alert.vue +5 -5
- package/src/components/ui/alert/AlertDescription.vue +4 -4
- package/src/components/ui/alert/AlertTitle.vue +4 -4
- package/src/components/ui/alert/index.ts +2 -2
- package/src/components/ui/alert-dialog/AlertDialog.vue +3 -3
- package/src/components/ui/alert-dialog/AlertDialogAction.vue +3 -3
- package/src/components/ui/alert-dialog/AlertDialogCancel.vue +3 -3
- package/src/components/ui/alert-dialog/AlertDialogContent.vue +7 -7
- package/src/components/ui/alert-dialog/AlertDialogDescription.vue +3 -3
- package/src/components/ui/alert-dialog/AlertDialogFooter.vue +4 -4
- package/src/components/ui/alert-dialog/AlertDialogHeader.vue +4 -4
- package/src/components/ui/alert-dialog/AlertDialogTitle.vue +3 -3
- package/src/components/ui/alert-dialog/AlertDialogTrigger.vue +3 -3
- package/src/components/ui/animated-number/AnimatedNumber.vue +49 -48
- package/src/components/ui/avatar/Avatar.vue +28 -28
- package/src/components/ui/avatar/AvatarFallback.vue +6 -6
- package/src/components/ui/avatar/AvatarGroup.vue +40 -40
- package/src/components/ui/avatar/AvatarImage.vue +3 -3
- package/src/components/ui/avatar/index.ts +17 -17
- package/src/components/ui/badge/Badge.vue +20 -20
- package/src/components/ui/breadcrumb/Breadcrumb.vue +4 -4
- package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +9 -9
- package/src/components/ui/breadcrumb/BreadcrumbItem.vue +4 -4
- package/src/components/ui/breadcrumb/BreadcrumbLink.vue +4 -4
- package/src/components/ui/breadcrumb/BreadcrumbList.vue +4 -4
- package/src/components/ui/breadcrumb/BreadcrumbPage.vue +4 -4
- package/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +4 -4
- package/src/components/ui/busy/Busy.vue +33 -33
- package/src/components/ui/button/Button.vue +23 -23
- package/src/components/ui/button/index.ts +56 -56
- package/src/components/ui/calendar/Calendar.vue +138 -138
- package/src/components/ui/calendar/CalendarCell.vue +7 -7
- package/src/components/ui/calendar/CalendarCellTrigger.vue +18 -18
- package/src/components/ui/calendar/CalendarGrid.vue +3 -3
- package/src/components/ui/calendar/CalendarGridBody.vue +3 -3
- package/src/components/ui/calendar/CalendarGridHead.vue +3 -3
- package/src/components/ui/calendar/CalendarGridRow.vue +3 -3
- package/src/components/ui/calendar/CalendarHeadCell.vue +7 -7
- package/src/components/ui/calendar/CalendarHeader.vue +7 -7
- package/src/components/ui/calendar/CalendarHeading.vue +6 -6
- package/src/components/ui/calendar/CalendarNextButton.vue +15 -15
- package/src/components/ui/calendar/CalendarPrevButton.vue +15 -15
- package/src/components/ui/calendar/index.ts +4 -4
- package/src/components/ui/card/Card.vue +20 -20
- package/src/components/ui/card/CardAction.vue +8 -8
- package/src/components/ui/card/CardContent.vue +4 -4
- package/src/components/ui/card/CardDescription.vue +12 -12
- package/src/components/ui/card/CardFooter.vue +4 -4
- package/src/components/ui/card/CardHeader.vue +8 -8
- package/src/components/ui/card/CardText.vue +8 -8
- package/src/components/ui/card/CardTitle.vue +8 -8
- package/src/components/ui/checkbox/Checkbox.vue +27 -27
- package/src/components/ui/collapsible/Collapsible.vue +3 -3
- package/src/components/ui/collapsible/CollapsibleContent.vue +3 -3
- package/src/components/ui/collapsible/CollapsibleTrigger.vue +3 -3
- package/src/components/ui/combobox/Combobox.vue +4 -4
- package/src/components/ui/combobox/ComboboxAnchor.vue +16 -16
- package/src/components/ui/combobox/ComboboxEmpty.vue +3 -3
- package/src/components/ui/combobox/ComboboxGroup.vue +9 -9
- package/src/components/ui/combobox/ComboboxInput.vue +6 -6
- package/src/components/ui/combobox/ComboboxItem.vue +17 -17
- package/src/components/ui/combobox/ComboboxList.vue +19 -19
- package/src/components/ui/combobox/ComboboxSelect.vue +77 -77
- package/src/components/ui/combobox/ComboboxSeparator.vue +1 -1
- package/src/components/ui/combobox/ComboboxTrigger.vue +10 -10
- package/src/components/ui/combobox/types.ts +5 -5
- package/src/components/ui/command/Command.vue +48 -48
- package/src/components/ui/command/CommandDialog.vue +14 -14
- package/src/components/ui/command/CommandEmpty.vue +4 -4
- package/src/components/ui/command/CommandGroup.vue +23 -23
- package/src/components/ui/command/CommandInput.vue +9 -9
- package/src/components/ui/command/CommandItem.vue +40 -40
- package/src/components/ui/command/CommandList.vue +9 -9
- package/src/components/ui/command/CommandSeparator.vue +3 -3
- package/src/components/ui/command/CommandShortcut.vue +8 -8
- package/src/components/ui/command/index.ts +11 -11
- package/src/components/ui/data-table/DataTable.vue +145 -145
- package/src/components/ui/data-table/index.ts +11 -11
- package/src/components/ui/date-picker/DatePicker.vue +31 -31
- package/src/components/ui/date-time-picker/DateTimePicker.vue +97 -97
- package/src/components/ui/dialog/Dialog.vue +3 -3
- package/src/components/ui/dialog/DialogClose.vue +3 -3
- package/src/components/ui/dialog/DialogContent.vue +21 -21
- package/src/components/ui/dialog/DialogDescription.vue +7 -7
- package/src/components/ui/dialog/DialogFooter.vue +8 -8
- package/src/components/ui/dialog/DialogHeader.vue +4 -4
- package/src/components/ui/dialog/DialogOverlay.vue +7 -7
- package/src/components/ui/dialog/DialogScrollContent.vue +32 -32
- package/src/components/ui/dialog/DialogTitle.vue +3 -3
- package/src/components/ui/dialog/DialogTrigger.vue +3 -3
- package/src/components/ui/dropdown-menu/DropdownMenu.vue +3 -3
- package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +11 -11
- package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +15 -15
- package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +3 -3
- package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +12 -12
- package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +9 -9
- package/src/components/ui/dropdown-menu/DropdownMenuPortal.vue +3 -3
- package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +3 -3
- package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +11 -11
- package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +1 -1
- package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +4 -4
- package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +3 -3
- package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +15 -15
- package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +15 -15
- package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +3 -3
- package/src/components/ui/dropdown-menu/classes.ts +16 -16
- package/src/components/ui/field/Field.vue +5 -5
- package/src/components/ui/field/FieldContent.vue +8 -8
- package/src/components/ui/field/FieldDescription.vue +10 -10
- package/src/components/ui/field/FieldError.vue +39 -39
- package/src/components/ui/field/FieldGroup.vue +8 -8
- package/src/components/ui/field/FieldLabel.vue +10 -10
- package/src/components/ui/field/FieldLegend.vue +11 -11
- package/src/components/ui/field/FieldSeparator.vue +11 -11
- package/src/components/ui/field/FieldSet.vue +9 -9
- package/src/components/ui/field/FieldTitle.vue +8 -8
- package/src/components/ui/field/field-context.ts +36 -36
- package/src/components/ui/field/index.ts +22 -22
- package/src/components/ui/file-dropzone/FileDropzone.vue +144 -144
- package/src/components/ui/file-dropzone/index.ts +19 -19
- package/src/components/ui/hover-card/HoverCard.vue +5 -5
- package/src/components/ui/hover-card/HoverCardContent.vue +13 -13
- package/src/components/ui/hover-card/HoverCardTrigger.vue +3 -3
- package/src/components/ui/input/Input.vue +13 -13
- package/src/components/ui/kbd/Kbd.vue +10 -10
- package/src/components/ui/kbd/KbdGroup.vue +49 -49
- package/src/components/ui/label/Label.vue +7 -7
- package/src/components/ui/native-select/NativeSelect.vue +16 -16
- package/src/components/ui/native-select/NativeSelectOptGroup.vue +3 -3
- package/src/components/ui/native-select/NativeSelectOption.vue +3 -3
- package/src/components/ui/number-field/NumberField.vue +88 -88
- package/src/components/ui/pagination/Pagination.vue +4 -4
- package/src/components/ui/pagination/PaginationContent.vue +4 -4
- package/src/components/ui/pagination/PaginationEllipsis.vue +9 -9
- package/src/components/ui/pagination/PaginationFirst.vue +8 -8
- package/src/components/ui/pagination/PaginationItem.vue +14 -14
- package/src/components/ui/pagination/PaginationLast.vue +8 -8
- package/src/components/ui/pagination/PaginationNext.vue +8 -8
- package/src/components/ui/pagination/PaginationPrevious.vue +8 -8
- package/src/components/ui/pagination/SimplePagination.vue +25 -25
- package/src/components/ui/pin-input/PinInput.vue +20 -20
- package/src/components/ui/pin-input/PinInputInput.vue +17 -17
- package/src/components/ui/pin-input/context.ts +1 -1
- package/src/components/ui/popover/Popover.vue +3 -3
- package/src/components/ui/popover/PopoverAnchor.vue +3 -3
- package/src/components/ui/popover/PopoverContent.vue +12 -12
- package/src/components/ui/popover/PopoverTrigger.vue +3 -3
- package/src/components/ui/progress/Progress.vue +16 -16
- package/src/components/ui/radio-group/RadioGroup.vue +7 -7
- package/src/components/ui/radio-group/RadioGroupCard.vue +39 -39
- package/src/components/ui/radio-group/RadioGroupItem.vue +20 -20
- package/src/components/ui/scroll-area/ScrollArea.vue +14 -14
- package/src/components/ui/scroll-area/ScrollBar.vue +12 -12
- package/src/components/ui/select/Select.vue +3 -3
- package/src/components/ui/select/SelectContent.vue +25 -25
- package/src/components/ui/select/SelectGroup.vue +3 -3
- package/src/components/ui/select/SelectItem.vue +24 -24
- package/src/components/ui/select/SelectItemContent.vue +32 -32
- package/src/components/ui/select/SelectItemText.vue +3 -3
- package/src/components/ui/select/SelectLabel.vue +3 -3
- package/src/components/ui/select/SelectScrollDownButton.vue +9 -9
- package/src/components/ui/select/SelectScrollUpButton.vue +9 -9
- package/src/components/ui/select/SelectSeparator.vue +2 -2
- package/src/components/ui/select/SelectTrigger.vue +14 -14
- package/src/components/ui/select/SelectValue.vue +3 -3
- package/src/components/ui/select/SelectValueContent.vue +39 -39
- package/src/components/ui/separator/Separator.vue +7 -7
- package/src/components/ui/sheet/Sheet.vue +3 -3
- package/src/components/ui/sheet/SheetClose.vue +3 -3
- package/src/components/ui/sheet/SheetContent.vue +22 -22
- package/src/components/ui/sheet/SheetDescription.vue +3 -3
- package/src/components/ui/sheet/SheetFooter.vue +3 -3
- package/src/components/ui/sheet/SheetHeader.vue +3 -3
- package/src/components/ui/sheet/SheetOverlay.vue +7 -7
- package/src/components/ui/sheet/SheetTitle.vue +3 -3
- package/src/components/ui/sheet/SheetTrigger.vue +3 -3
- package/src/components/ui/sidebar/Sidebar.vue +57 -57
- package/src/components/ui/sidebar/SidebarContent.vue +8 -8
- package/src/components/ui/sidebar/SidebarFooter.vue +8 -8
- package/src/components/ui/sidebar/SidebarGroup.vue +8 -8
- package/src/components/ui/sidebar/SidebarGroupAction.vue +9 -9
- package/src/components/ui/sidebar/SidebarGroupContent.vue +4 -4
- package/src/components/ui/sidebar/SidebarGroupLabel.vue +8 -8
- package/src/components/ui/sidebar/SidebarHeader.vue +8 -8
- package/src/components/ui/sidebar/SidebarInput.vue +8 -8
- package/src/components/ui/sidebar/SidebarInset.vue +9 -9
- package/src/components/ui/sidebar/SidebarMenu.vue +4 -4
- package/src/components/ui/sidebar/SidebarMenuAction.vue +18 -18
- package/src/components/ui/sidebar/SidebarMenuBadge.vue +14 -14
- package/src/components/ui/sidebar/SidebarMenuButton.vue +21 -21
- package/src/components/ui/sidebar/SidebarMenuButtonChild.vue +10 -10
- package/src/components/ui/sidebar/SidebarMenuItem.vue +4 -4
- package/src/components/ui/sidebar/SidebarMenuSkeleton.vue +10 -10
- package/src/components/ui/sidebar/SidebarMenuSub.vue +9 -9
- package/src/components/ui/sidebar/SidebarMenuSubButton.vue +17 -17
- package/src/components/ui/sidebar/SidebarMenuSubItem.vue +4 -4
- package/src/components/ui/sidebar/SidebarProvider.vue +46 -46
- package/src/components/ui/sidebar/SidebarRail.vue +13 -13
- package/src/components/ui/sidebar/SidebarSeparator.vue +4 -4
- package/src/components/ui/sidebar/SidebarTrigger.vue +5 -5
- package/src/components/ui/sidebar/index.ts +29 -29
- package/src/components/ui/sidebar/utils.ts +7 -7
- package/src/components/ui/skeleton/Skeleton.vue +6 -6
- package/src/components/ui/slider/Slider.vue +14 -14
- package/src/components/ui/spinner/Spinner.vue +21 -21
- package/src/components/ui/stepper/SimpleStepper.vue +26 -26
- package/src/components/ui/stepper/Stepper.vue +6 -6
- package/src/components/ui/stepper/StepperDescription.vue +5 -5
- package/src/components/ui/stepper/StepperIndicator.vue +17 -17
- package/src/components/ui/stepper/StepperItem.vue +4 -4
- package/src/components/ui/stepper/StepperSeparator.vue +7 -7
- package/src/components/ui/stepper/StepperTitle.vue +5 -5
- package/src/components/ui/stepper/StepperTrigger.vue +4 -4
- package/src/components/ui/stepper/index.ts +3 -3
- package/src/components/ui/switch/Switch.vue +15 -15
- package/src/components/ui/table/Table.vue +6 -6
- package/src/components/ui/table/TableBody.vue +4 -4
- package/src/components/ui/table/TableCaption.vue +4 -4
- package/src/components/ui/table/TableCell.vue +8 -8
- package/src/components/ui/table/TableEmpty.vue +14 -14
- package/src/components/ui/table/TableFooter.vue +8 -8
- package/src/components/ui/table/TableHead.vue +8 -8
- package/src/components/ui/table/TableHeader.vue +4 -4
- package/src/components/ui/table/TableRow.vue +8 -8
- package/src/components/ui/tabs/Tabs.vue +4 -4
- package/src/components/ui/tabs/TabsContent.vue +4 -4
- package/src/components/ui/tabs/TabsList.vue +15 -15
- package/src/components/ui/tabs/TabsTrigger.vue +10 -10
- package/src/components/ui/tags-input/TagsInput.vue +17 -17
- package/src/components/ui/tags-input/TagsInputInput.vue +6 -6
- package/src/components/ui/tags-input/TagsInputItem.vue +15 -15
- package/src/components/ui/tags-input/TagsInputItemDelete.vue +12 -12
- package/src/components/ui/tags-input/TagsInputItemText.vue +4 -4
- package/src/components/ui/textarea/Textarea.vue +12 -12
- package/src/components/ui/time-picker/TimePicker.vue +25 -25
- package/src/components/ui/toggle-group/Toggle.vue +10 -10
- package/src/components/ui/toggle-group/ToggleGroup.vue +38 -38
- package/src/components/ui/toggle-group/ToggleGroupItem.vue +9 -9
- package/src/components/ui/toggle-group/index.ts +46 -46
- package/src/components/ui/tooltip/Tooltip.vue +3 -3
- package/src/components/ui/tooltip/TooltipContent.vue +13 -13
- package/src/components/ui/tooltip/TooltipProvider.vue +4 -4
- package/src/components/ui/tooltip/TooltipTrigger.vue +3 -3
- package/src/i18n/de.json +206 -185
- package/src/i18n/en.json +206 -185
- package/src/i18n/es.json +206 -185
- package/src/i18n/fr.json +206 -185
- package/src/i18n/it.json +206 -185
- package/src/i18n/ja.json +206 -185
- package/src/i18n/pt.json +206 -185
- package/src/i18n/ru.json +206 -185
- package/src/i18n/zh.json +206 -185
- package/src/lib/color-tokens.ts +24 -24
- package/src/lib/i18n.ts +65 -65
- package/src/lib/utils.ts +33 -33
- package/src/nuxt-components.d.ts +2 -2
- package/src/runtime/ui-vintage-i18n-plugin.ts +49 -49
- package/src/shims-vue.d.ts +3 -3
- 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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
34
|
-
|
|
33
|
+
multiple: true,
|
|
34
|
+
disabled: false,
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
// Emits
|
|
38
38
|
const emits = defineEmits<{
|
|
39
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
if (props.disabled || incoming.length === 0) return;
|
|
100
|
+
const rejections: FileRejection[] = [];
|
|
101
|
+
const accepted: File[] = [];
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
161
|
-
|
|
160
|
+
if (props.disabled || !event.dataTransfer) return;
|
|
161
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
function onDragLeave() {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
8
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
9
|
+
inheritAttrs: false
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
// Props
|
|
13
13
|
const props = withDefaults(defineProps<HoverCardContentProps & { class?: HTMLAttributes['class'] }>(), {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
16
|
+
(e: 'update:modelValue', payload: string | number): void;
|
|
17
17
|
}>();
|
|
18
18
|
|
|
19
19
|
const modelValue = useVModel(props, 'modelValue', emits, {
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
8
|
-
|
|
7
|
+
class?: HTMLAttributes['class'];
|
|
8
|
+
pressed?: boolean;
|
|
9
9
|
}>();
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<template>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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>
|