@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.
- 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/BlogContentRenderer.vue +111 -111
- package/src/components/blog/BlogFAQSection.vue +19 -19
- package/src/components/blog/BlogHeaderSection.vue +80 -80
- package/src/components/blog/BlogInfoSection.vue +47 -47
- package/src/components/blog/BlogPostHeader.vue +135 -135
- package/src/components/blog/BlogProgressBar.vue +33 -33
- package/src/components/blog/BlogSectionTitle.vue +22 -22
- package/src/components/blog/BlogTagsSection.vue +25 -25
- 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/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 +185 -185
- package/src/i18n/en.json +185 -185
- package/src/i18n/es.json +185 -185
- package/src/i18n/fr.json +185 -185
- package/src/i18n/it.json +185 -185
- package/src/i18n/ja.json +185 -185
- package/src/i18n/pt.json +185 -185
- package/src/i18n/ru.json +185 -185
- package/src/i18n/zh.json +185 -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
|
@@ -4,47 +4,47 @@ import type { HugeiconsIconDefinition } from '../../lib/common-types';
|
|
|
4
4
|
export type MessageToastType = 'success' | 'info' | 'warning' | 'error';
|
|
5
5
|
|
|
6
6
|
export interface MessageToastAction {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
label: string;
|
|
8
|
+
onClick: () => void | Promise<void>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export interface ShowMessageToastOptions {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
message: string;
|
|
13
|
+
type?: MessageToastType;
|
|
14
|
+
duration?: number;
|
|
15
|
+
title?: string;
|
|
16
|
+
icon?: HugeiconsIconDefinition | null;
|
|
17
|
+
action?: MessageToastAction | null;
|
|
18
|
+
dismissible?: boolean;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export interface MessageToastItem {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
id: number;
|
|
23
|
+
message: string;
|
|
24
|
+
type: MessageToastType;
|
|
25
|
+
title: string;
|
|
26
|
+
icon: HugeiconsIconDefinition | null;
|
|
27
|
+
action: MessageToastAction | null;
|
|
28
|
+
dismissible: boolean;
|
|
29
|
+
duration: number;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
interface MessageToastContent {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
message: string;
|
|
34
|
+
type: MessageToastType;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
interface MessageToastState {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
current: MessageToastContent | null;
|
|
39
|
+
isOpen: boolean;
|
|
40
|
+
toasts: MessageToastItem[];
|
|
41
|
+
paused: boolean;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
interface ToastTimer {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
handle: ReturnType<typeof setTimeout> | null;
|
|
46
|
+
remaining: number;
|
|
47
|
+
startedAt: number;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const DEFAULT_TOAST_DURATION_MS = 5000;
|
|
@@ -58,156 +58,156 @@ let nextToastId = 0;
|
|
|
58
58
|
|
|
59
59
|
// Shared message toast state (current/isOpen are kept for backward compatibility)
|
|
60
60
|
export const messageToastState: MessageToastState = reactive({
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
current: null,
|
|
62
|
+
isOpen: false,
|
|
63
|
+
toasts: [],
|
|
64
|
+
paused: false
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
// Keep the legacy single-toast fields in sync with the stack
|
|
68
68
|
const syncLegacyState = () => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
const latest = messageToastState.toasts[0];
|
|
70
|
+
messageToastState.current = latest ? { message: latest.message, type: latest.type } : null;
|
|
71
|
+
messageToastState.isOpen = Boolean(latest);
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
// Mount the toaster once on demand
|
|
75
75
|
const ensureMessageToastMounted = () => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
76
|
+
// Skip mounting during SSR
|
|
77
|
+
if (typeof document === 'undefined') {
|
|
78
|
+
return Promise.resolve();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Reuse the existing mount root
|
|
82
|
+
if (document.getElementById(MESSAGE_TOAST_ROOT_ID)) {
|
|
83
|
+
return Promise.resolve();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Reuse the in-flight mount
|
|
87
|
+
if (messageToastMountPromise) {
|
|
88
|
+
return messageToastMountPromise;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Start the lazy mount
|
|
92
|
+
messageToastMountPromise = (async () => {
|
|
93
|
+
// Load the component only when needed
|
|
94
|
+
const { default: MessageToast } = await import('./MessageToast.vue');
|
|
95
|
+
|
|
96
|
+
// Guard against a concurrent mount
|
|
97
|
+
if (document.getElementById(MESSAGE_TOAST_ROOT_ID)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Create the host element and render the component
|
|
102
|
+
const container = document.createElement('div');
|
|
103
|
+
container.id = MESSAGE_TOAST_ROOT_ID;
|
|
104
|
+
document.body.appendChild(container);
|
|
105
|
+
render(h(MessageToast), container);
|
|
106
|
+
})().finally(() => {
|
|
107
|
+
messageToastMountPromise = null;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Let callers await the mount
|
|
111
|
+
return messageToastMountPromise;
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
// Start (or restart) the auto-dismiss countdown of a toast
|
|
115
115
|
const startToastTimer = (id: number) => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
const timer = toastTimers.get(id);
|
|
117
|
+
if (!timer || timer.handle || messageToastState.paused) return;
|
|
118
|
+
timer.startedAt = Date.now();
|
|
119
|
+
timer.handle = setTimeout(() => closeMessageToast(id), timer.remaining);
|
|
120
120
|
};
|
|
121
121
|
|
|
122
122
|
// Stop the countdown of a toast and remember how much time is left
|
|
123
123
|
const stopToastTimer = (id: number) => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
const timer = toastTimers.get(id);
|
|
125
|
+
if (!timer?.handle) return;
|
|
126
|
+
clearTimeout(timer.handle);
|
|
127
|
+
timer.handle = null;
|
|
128
|
+
timer.remaining = Math.max(0, timer.remaining - (Date.now() - timer.startedAt));
|
|
129
129
|
};
|
|
130
130
|
|
|
131
131
|
// Pause every countdown (used while the toaster is hovered or focused)
|
|
132
132
|
export const pauseMessageToasts = () => {
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
messageToastState.paused = true;
|
|
134
|
+
toastTimers.forEach((_, id) => stopToastTimer(id));
|
|
135
135
|
};
|
|
136
136
|
|
|
137
137
|
// Resume every countdown
|
|
138
138
|
export const resumeMessageToasts = () => {
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
messageToastState.paused = false;
|
|
140
|
+
toastTimers.forEach((_, id) => startToastTimer(id));
|
|
141
141
|
};
|
|
142
142
|
|
|
143
143
|
// Dismiss one toast by id, or every toast when no id is passed
|
|
144
144
|
export const closeMessageToast = (id?: number) => {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
145
|
+
const ids = id === undefined ? messageToastState.toasts.map(toast => toast.id) : [id];
|
|
146
|
+
|
|
147
|
+
// Cancel toasts that were requested but are still waiting for the toaster to mount
|
|
148
|
+
if (id === undefined) {
|
|
149
|
+
pendingToastIds.clear();
|
|
150
|
+
} else {
|
|
151
|
+
pendingToastIds.delete(id);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Clear timers and remove the toasts from the stack
|
|
155
|
+
for (const toastId of ids) {
|
|
156
|
+
stopToastTimer(toastId);
|
|
157
|
+
toastTimers.delete(toastId);
|
|
158
|
+
}
|
|
159
|
+
messageToastState.toasts = messageToastState.toasts.filter(toast => !ids.includes(toast.id));
|
|
160
|
+
syncLegacyState();
|
|
161
161
|
};
|
|
162
162
|
|
|
163
163
|
// Show a message toast and optionally auto-dismiss it; returns the toast id
|
|
164
164
|
export const showMessageToast = (options: ShowMessageToastOptions) => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
165
|
+
nextToastId += 1;
|
|
166
|
+
const id = nextToastId;
|
|
167
|
+
const duration = options.duration ?? DEFAULT_TOAST_DURATION_MS;
|
|
168
|
+
|
|
169
|
+
// Build the toast item
|
|
170
|
+
const toast: MessageToastItem = {
|
|
171
|
+
id,
|
|
172
|
+
message: options.message,
|
|
173
|
+
type: options.type ?? 'success',
|
|
174
|
+
title: options.title ?? '',
|
|
175
|
+
icon: options.icon ? markRaw(options.icon) : null,
|
|
176
|
+
action: options.action ?? null,
|
|
177
|
+
dismissible: options.dismissible ?? true,
|
|
178
|
+
duration
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// Toasts are client-only: never store them in the module state shared across server requests
|
|
182
|
+
if (typeof document === 'undefined') {
|
|
183
|
+
return id;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Expose the newest toast synchronously, as the single-toast API always did
|
|
187
|
+
messageToastState.current = { message: toast.message, type: toast.type };
|
|
188
|
+
|
|
189
|
+
// Mount first so the enter transition runs once the renderer exists
|
|
190
|
+
pendingToastIds.add(id);
|
|
191
|
+
void ensureMessageToastMounted().then(() => {
|
|
192
|
+
// Skip toasts that were closed before the toaster finished mounting
|
|
193
|
+
if (!pendingToastIds.delete(id)) {
|
|
194
|
+
syncLegacyState();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Push the newest toast on top and trim the overflow
|
|
199
|
+
messageToastState.toasts = [toast, ...messageToastState.toasts];
|
|
200
|
+
for (const overflow of messageToastState.toasts.slice(MAX_TOASTS)) {
|
|
201
|
+
closeMessageToast(overflow.id);
|
|
202
|
+
}
|
|
203
|
+
syncLegacyState();
|
|
204
|
+
|
|
205
|
+
// Auto-dismiss the toast after the specified duration
|
|
206
|
+
if (duration > 0) {
|
|
207
|
+
toastTimers.set(id, { handle: null, remaining: duration, startedAt: Date.now() });
|
|
208
|
+
startToastTimer(id);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return id;
|
|
213
213
|
};
|
|
@@ -3,53 +3,53 @@ import { Comment, Text, computed, useSlots, type VNode } from 'vue';
|
|
|
3
3
|
|
|
4
4
|
// Props
|
|
5
5
|
const props = withDefaults(defineProps<{
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
title: string;
|
|
7
|
+
headingLevel?: number;
|
|
8
|
+
size?: 'small' | 'medium' | 'large';
|
|
9
9
|
}>(), {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
headingLevel: 1,
|
|
11
|
+
size: 'large'
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
const slots = useSlots();
|
|
15
15
|
|
|
16
16
|
// Check if there is content in the default slot
|
|
17
17
|
const hasSlotContent = computed(() => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
if (!slots.default) return false;
|
|
19
|
+
|
|
20
|
+
// Check if the slot has any non-empty content
|
|
21
|
+
return slots.default({}).some((vnode: VNode) => {
|
|
22
|
+
if (vnode.type === Comment) return false;
|
|
23
|
+
if (vnode.type === Text && typeof vnode.children === 'string' && !vnode.children.trim()) return false;
|
|
24
|
+
return true;
|
|
25
|
+
});
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
// Get size classes based on size prop
|
|
29
29
|
const getSizeClasses = (size: string) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
const baseClasses = 'font-semibold tracking-tight text-foreground';
|
|
31
|
+
|
|
32
|
+
// Return classes based on size
|
|
33
|
+
switch (size) {
|
|
34
|
+
case 'small':
|
|
35
|
+
return `${baseClasses} text-base md:text-xl`;
|
|
36
|
+
case 'medium':
|
|
37
|
+
return `${baseClasses} text-lg md:text-2xl`;
|
|
38
|
+
case 'large':
|
|
39
|
+
default:
|
|
40
|
+
return `${baseClasses} text-xl md:text-3xl`;
|
|
41
|
+
}
|
|
42
42
|
};
|
|
43
43
|
</script>
|
|
44
44
|
|
|
45
45
|
<template>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
<div class="flex flex-col gap-3 mb-6 sm:flex-row sm:justify-between sm:items-center sm:min-h-12 sm:gap-0">
|
|
47
|
+
<!-- Title -->
|
|
48
|
+
<component :is="`h${headingLevel}`" :class="getSizeClasses(props.size)">{{ props.title }}</component>
|
|
49
|
+
|
|
50
|
+
<!-- Right section -->
|
|
51
|
+
<div v-if="hasSlotContent" class="w-full sm:w-auto">
|
|
52
|
+
<slot />
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
55
|
</template>
|
|
@@ -4,14 +4,14 @@ import { Progress } from '../ui/progress';
|
|
|
4
4
|
|
|
5
5
|
// Props
|
|
6
6
|
const props = withDefaults(defineProps<{
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
title: string;
|
|
8
|
+
bottomLeftLabel?: string;
|
|
9
|
+
bottomRightLabel?: string;
|
|
10
|
+
value: number;
|
|
11
|
+
max: number;
|
|
12
12
|
}>(), {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
bottomLeftLabel: '',
|
|
14
|
+
bottomRightLabel: ''
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
// Name the progress bar after the visible title
|
|
@@ -19,34 +19,34 @@ const titleId = useId();
|
|
|
19
19
|
</script>
|
|
20
20
|
|
|
21
21
|
<template>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
<div class="space-y-3">
|
|
23
|
+
<!-- Header with label and value -->
|
|
24
|
+
<div class="flex items-center justify-between">
|
|
25
|
+
<!-- Title of the progress bar -->
|
|
26
|
+
<span :id="titleId" class="text-sm font-semibold text-foreground">
|
|
27
|
+
{{ props.title }}
|
|
28
|
+
</span>
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
<!-- Current value / max -->
|
|
31
|
+
<span class="text-sm font-semibold text-foreground">
|
|
32
|
+
{{ props.value }} / {{ props.max }}
|
|
33
|
+
</span>
|
|
34
|
+
</div>
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
<!-- Progress bar -->
|
|
37
|
+
<Progress :model-value="props.value" :max="props.max" :aria-labelledby="titleId" />
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
<!-- Bottom labels -->
|
|
40
|
+
<div v-if="props.bottomLeftLabel || props.bottomRightLabel" class="flex items-center justify-between text-xs">
|
|
41
|
+
<!-- Bottom left label -->
|
|
42
|
+
<span v-if="props.bottomLeftLabel" class="text-muted-foreground">
|
|
43
|
+
{{ props.bottomLeftLabel }}
|
|
44
|
+
</span>
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
<!-- Bottom right label -->
|
|
47
|
+
<span v-if="props.bottomRightLabel" class="text-muted-foreground">
|
|
48
|
+
{{ props.bottomRightLabel }}
|
|
49
|
+
</span>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
52
|
</template>
|
|
@@ -11,37 +11,37 @@ import type { HugeiconsIconDefinition } from '../../lib/common-types';
|
|
|
11
11
|
|
|
12
12
|
// Props
|
|
13
13
|
const props = withDefaults(defineProps<{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
value: string | number;
|
|
15
|
+
label: string;
|
|
16
|
+
icon: HugeiconsIconDefinition;
|
|
17
|
+
valueColor?: 'default' | 'green' | 'red' | 'gray';
|
|
18
|
+
trend?: number | null;
|
|
19
|
+
trendLabel?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
animate?: boolean;
|
|
22
|
+
formatOptions?: Intl.NumberFormatOptions;
|
|
23
|
+
class?: HTMLAttributes['class'];
|
|
24
24
|
}>(), {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
valueColor: 'default',
|
|
26
|
+
trend: null,
|
|
27
|
+
trendLabel: '',
|
|
28
|
+
description: '',
|
|
29
|
+
animate: true,
|
|
30
|
+
formatOptions: undefined
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
// Value classes
|
|
34
34
|
const valueClass = computed(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
switch (props.valueColor) {
|
|
36
|
+
case 'green':
|
|
37
|
+
return 'text-success';
|
|
38
|
+
case 'red':
|
|
39
|
+
return 'text-destructive';
|
|
40
|
+
case 'gray':
|
|
41
|
+
return 'text-muted-foreground';
|
|
42
|
+
default:
|
|
43
|
+
return 'text-foreground';
|
|
44
|
+
}
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
// Trend badge presentation
|
|
@@ -53,37 +53,37 @@ const staticValue = computed(() => typeof props.value === 'number' && props.form
|
|
|
53
53
|
</script>
|
|
54
54
|
|
|
55
55
|
<template>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
<Card :class="cn('gap-0! p-4!', props.class)">
|
|
57
|
+
<!-- Label and icon -->
|
|
58
|
+
<div class="flex items-start justify-between gap-3">
|
|
59
|
+
<span class="min-w-0 text-[11px] font-semibold leading-5 tracking-wider text-muted-foreground uppercase">
|
|
60
|
+
{{ props.label }}
|
|
61
|
+
</span>
|
|
62
|
+
<span aria-hidden="true" class="flex size-8 shrink-0 items-center justify-center rounded border border-primary/30 text-primary">
|
|
63
|
+
<HugeiconsIcon :icon="props.icon" class="size-4" />
|
|
64
|
+
</span>
|
|
65
|
+
</div>
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
<!-- Value and trend -->
|
|
68
|
+
<div class="mt-1 flex flex-wrap items-baseline gap-x-2 gap-y-1">
|
|
69
|
+
<span :class="['text-2xl font-semibold tracking-tight tabular-nums', valueClass]">
|
|
70
|
+
<AnimatedNumber v-if="props.animate && typeof props.value === 'number'" :value="props.value" :format-options="props.formatOptions" />
|
|
71
|
+
<template v-else>{{ staticValue }}</template>
|
|
72
|
+
</span>
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
<!-- Trend badge -->
|
|
75
|
+
<span v-if="props.trend !== null" :class="cn('inline-flex items-center gap-0.5 rounded-sm px-1 py-0.5 text-[11px] font-semibold tabular-nums', trendIsPositive ? 'bg-success/10 text-success' : 'bg-destructive/10 text-destructive')">
|
|
76
|
+
<HugeiconsIcon :icon="trendIsPositive ? ArrowUpRight01Icon : ArrowDownRight01Icon" class="size-3" />
|
|
77
|
+
{{ formattedTrend }}
|
|
78
|
+
</span>
|
|
79
|
+
<span v-if="props.trend !== null && props.trendLabel" class="text-[11px] text-muted-foreground">
|
|
80
|
+
{{ props.trendLabel }}
|
|
81
|
+
</span>
|
|
82
|
+
</div>
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
</template>
|
|
84
|
+
<!-- Description -->
|
|
85
|
+
<span v-if="props.description" class="mt-1 text-xs leading-5 text-muted-foreground">
|
|
86
|
+
{{ props.description }}
|
|
87
|
+
</span>
|
|
88
|
+
</Card>
|
|
89
|
+
</template>
|