@vestow/vue 0.1.0
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/README.md +233 -0
- package/index.ts +8 -0
- package/package.json +68 -0
- package/src/components/accordion/Accordion.vue +45 -0
- package/src/components/accordion/AccordionContent.vue +31 -0
- package/src/components/accordion/AccordionItem.vue +26 -0
- package/src/components/accordion/AccordionTrigger.vue +39 -0
- package/src/components/accordion/index.ts +18 -0
- package/src/components/alert/Alert.vue +17 -0
- package/src/components/alert/AlertDescription.vue +12 -0
- package/src/components/alert/AlertTitle.vue +12 -0
- package/src/components/alert/index.ts +5 -0
- package/src/components/avatar/Avatar.vue +32 -0
- package/src/components/avatar/AvatarFallback.vue +23 -0
- package/src/components/avatar/AvatarImage.vue +18 -0
- package/src/components/avatar/index.ts +3 -0
- package/src/components/badge/Badge.vue +25 -0
- package/src/components/badge/index.ts +3 -0
- package/src/components/breadcrumb/Breadcrumb.vue +14 -0
- package/src/components/breadcrumb/BreadcrumbItem.vue +12 -0
- package/src/components/breadcrumb/BreadcrumbLink.vue +19 -0
- package/src/components/breadcrumb/BreadcrumbPage.vue +12 -0
- package/src/components/breadcrumb/BreadcrumbSeparator.vue +15 -0
- package/src/components/breadcrumb/index.ts +5 -0
- package/src/components/button/Button.vue +55 -0
- package/src/components/button/index.ts +3 -0
- package/src/components/button-group/ButtonGroup.vue +29 -0
- package/src/components/button-group/ButtonGroupSeparator.vue +30 -0
- package/src/components/button-group/ButtonGroupText.vue +29 -0
- package/src/components/button-group/SplitButton.vue +103 -0
- package/src/components/button-group/SplitButtonItems.vue +50 -0
- package/src/components/button-group/index.ts +34 -0
- package/src/components/calendar/Calendar.vue +120 -0
- package/src/components/calendar/RangeCalendar.vue +127 -0
- package/src/components/calendar/index.ts +2 -0
- package/src/components/card/Card.vue +12 -0
- package/src/components/card/CardAction.vue +12 -0
- package/src/components/card/CardContent.vue +12 -0
- package/src/components/card/CardDescription.vue +12 -0
- package/src/components/card/CardFooter.vue +12 -0
- package/src/components/card/CardHeader.vue +15 -0
- package/src/components/card/CardTitle.vue +12 -0
- package/src/components/card/index.ts +7 -0
- package/src/components/carousel/Carousel.vue +164 -0
- package/src/components/carousel/CarouselContent.vue +43 -0
- package/src/components/carousel/CarouselDots.vue +42 -0
- package/src/components/carousel/CarouselItem.vue +58 -0
- package/src/components/carousel/CarouselNext.vue +45 -0
- package/src/components/carousel/CarouselPrevious.vue +45 -0
- package/src/components/carousel/index.ts +44 -0
- package/src/components/cascade-select/CascadeSelect.vue +314 -0
- package/src/components/cascade-select/index.ts +11 -0
- package/src/components/chart/AreaChart.vue +201 -0
- package/src/components/chart/BarChart.vue +389 -0
- package/src/components/chart/ChartContainer.vue +127 -0
- package/src/components/chart/ChartCrosshair.vue +92 -0
- package/src/components/chart/ChartLegend.vue +63 -0
- package/src/components/chart/ChartTooltipCard.vue +49 -0
- package/src/components/chart/LineChart.vue +214 -0
- package/src/components/chart/PieChart.vue +280 -0
- package/src/components/chart/RadarChart.vue +291 -0
- package/src/components/chart/RadialChart.vue +354 -0
- package/src/components/chart/index.ts +30 -0
- package/src/components/chart/polar.ts +5 -0
- package/src/components/chart/tooltip.ts +5 -0
- package/src/components/chart/types.ts +52 -0
- package/src/components/chart/useChartSeries.ts +138 -0
- package/src/components/chart/xy-label.ts +5 -0
- package/src/components/checkbox/Checkbox.vue +81 -0
- package/src/components/checkbox/CheckboxField.vue +129 -0
- package/src/components/checkbox/CheckboxGroup.vue +62 -0
- package/src/components/checkbox/index.ts +21 -0
- package/src/components/chip/Chip.vue +73 -0
- package/src/components/chip/index.ts +3 -0
- package/src/components/code-block/CodeBlock.vue +100 -0
- package/src/components/code-block/index.ts +4 -0
- package/src/components/collapsible/Collapsible.vue +15 -0
- package/src/components/collapsible/CollapsibleContent.vue +26 -0
- package/src/components/collapsible/CollapsibleTrigger.vue +12 -0
- package/src/components/collapsible/index.ts +3 -0
- package/src/components/combobox/Combobox.vue +23 -0
- package/src/components/combobox/ComboboxAnchor.vue +20 -0
- package/src/components/combobox/ComboboxEmpty.vue +21 -0
- package/src/components/combobox/ComboboxGroup.vue +23 -0
- package/src/components/combobox/ComboboxInput.vue +32 -0
- package/src/components/combobox/ComboboxItem.vue +38 -0
- package/src/components/combobox/ComboboxList.vue +43 -0
- package/src/components/combobox/ComboboxSeparator.vue +18 -0
- package/src/components/combobox/ComboboxTrigger.vue +12 -0
- package/src/components/combobox/index.ts +9 -0
- package/src/components/command/Command.vue +54 -0
- package/src/components/command/CommandDialog.vue +41 -0
- package/src/components/command/CommandEmpty.vue +19 -0
- package/src/components/command/CommandGroup.vue +28 -0
- package/src/components/command/CommandInput.vue +25 -0
- package/src/components/command/CommandItem.vue +40 -0
- package/src/components/command/CommandList.vue +13 -0
- package/src/components/command/CommandSeparator.vue +11 -0
- package/src/components/command/CommandShortcut.vue +13 -0
- package/src/components/command/index.ts +34 -0
- package/src/components/command-block/CommandBlock.vue +90 -0
- package/src/components/command-block/index.ts +2 -0
- package/src/components/confirm-dialog/ConfirmDialog.vue +37 -0
- package/src/components/confirm-dialog/index.ts +1 -0
- package/src/components/confirm-popup/ConfirmPopup.vue +123 -0
- package/src/components/confirm-popup/index.ts +1 -0
- package/src/components/context-menu/ContextMenu.vue +15 -0
- package/src/components/context-menu/ContextMenuCheckboxItem.vue +30 -0
- package/src/components/context-menu/ContextMenuContent.vue +36 -0
- package/src/components/context-menu/ContextMenuGroup.vue +12 -0
- package/src/components/context-menu/ContextMenuItem.vue +32 -0
- package/src/components/context-menu/ContextMenuLabel.vue +21 -0
- package/src/components/context-menu/ContextMenuRadioGroup.vue +15 -0
- package/src/components/context-menu/ContextMenuRadioItem.vue +30 -0
- package/src/components/context-menu/ContextMenuSeparator.vue +19 -0
- package/src/components/context-menu/ContextMenuShortcut.vue +13 -0
- package/src/components/context-menu/ContextMenuSub.vue +15 -0
- package/src/components/context-menu/ContextMenuSubContent.vue +26 -0
- package/src/components/context-menu/ContextMenuSubTrigger.vue +26 -0
- package/src/components/context-menu/ContextMenuTrigger.vue +12 -0
- package/src/components/context-menu/index.ts +14 -0
- package/src/components/data-table/DataTable.vue +458 -0
- package/src/components/data-table/DataTableFacetedFilter.vue +97 -0
- package/src/components/data-table/DataTablePagination.vue +103 -0
- package/src/components/data-table/DataTableViewOptions.vue +69 -0
- package/src/components/data-table/features.ts +87 -0
- package/src/components/data-table/index.ts +27 -0
- package/src/components/data-table/pagination-adapter.ts +67 -0
- package/src/components/data-table/selection-column.ts +48 -0
- package/src/components/data-table/types.ts +36 -0
- package/src/components/data-view/DataView.vue +136 -0
- package/src/components/data-view/index.ts +1 -0
- package/src/components/date-picker/DatePicker.vue +117 -0
- package/src/components/date-picker/DateRangePicker.vue +122 -0
- package/src/components/date-picker/index.ts +2 -0
- package/src/components/deferred/Deferred.vue +80 -0
- package/src/components/deferred/index.ts +1 -0
- package/src/components/dialog/Dialog.vue +15 -0
- package/src/components/dialog/DialogClose.vue +12 -0
- package/src/components/dialog/DialogContent.vue +46 -0
- package/src/components/dialog/DialogDescription.vue +19 -0
- package/src/components/dialog/DialogFooter.vue +12 -0
- package/src/components/dialog/DialogHeader.vue +12 -0
- package/src/components/dialog/DialogTitle.vue +19 -0
- package/src/components/dialog/DialogTrigger.vue +12 -0
- package/src/components/dialog/index.ts +10 -0
- package/src/components/doc-example/DocExample.vue +41 -0
- package/src/components/doc-example/index.ts +1 -0
- package/src/components/doc-text/DocText.vue +64 -0
- package/src/components/doc-text/index.ts +1 -0
- package/src/components/dropdown-menu/DropdownMenu.vue +15 -0
- package/src/components/dropdown-menu/DropdownMenuCheckboxItem.vue +33 -0
- package/src/components/dropdown-menu/DropdownMenuContent.vue +36 -0
- package/src/components/dropdown-menu/DropdownMenuGroup.vue +12 -0
- package/src/components/dropdown-menu/DropdownMenuItem.vue +32 -0
- package/src/components/dropdown-menu/DropdownMenuLabel.vue +24 -0
- package/src/components/dropdown-menu/DropdownMenuRadioGroup.vue +15 -0
- package/src/components/dropdown-menu/DropdownMenuRadioItem.vue +33 -0
- package/src/components/dropdown-menu/DropdownMenuSeparator.vue +19 -0
- package/src/components/dropdown-menu/DropdownMenuShortcut.vue +13 -0
- package/src/components/dropdown-menu/DropdownMenuSub.vue +15 -0
- package/src/components/dropdown-menu/DropdownMenuSubContent.vue +29 -0
- package/src/components/dropdown-menu/DropdownMenuSubTrigger.vue +26 -0
- package/src/components/dropdown-menu/DropdownMenuTrigger.vue +12 -0
- package/src/components/dropdown-menu/index.ts +14 -0
- package/src/components/editable/Editable.vue +41 -0
- package/src/components/editable/EditableArea.vue +22 -0
- package/src/components/editable/EditableCancelTrigger.vue +40 -0
- package/src/components/editable/EditableEditTrigger.vue +40 -0
- package/src/components/editable/EditableInput.vue +28 -0
- package/src/components/editable/EditablePreview.vue +34 -0
- package/src/components/editable/EditableSubmitTrigger.vue +40 -0
- package/src/components/editable/index.ts +17 -0
- package/src/components/empty/Empty.vue +17 -0
- package/src/components/empty/EmptyActions.vue +14 -0
- package/src/components/empty/EmptyDescription.vue +12 -0
- package/src/components/empty/EmptyMedia.vue +16 -0
- package/src/components/empty/EmptyTitle.vue +17 -0
- package/src/components/empty/index.ts +7 -0
- package/src/components/field/Field.vue +22 -0
- package/src/components/field/FieldDescription.vue +20 -0
- package/src/components/field/FieldGroup.vue +22 -0
- package/src/components/field/FieldLabel.vue +27 -0
- package/src/components/field/FieldSeparator.vue +28 -0
- package/src/components/field/index.ts +7 -0
- package/src/components/fieldset/Fieldset.vue +126 -0
- package/src/components/fieldset/index.ts +1 -0
- package/src/components/file-upload/FileUpload.vue +149 -0
- package/src/components/file-upload/index.ts +8 -0
- package/src/components/float-label/FloatLabel.vue +26 -0
- package/src/components/float-label/index.ts +3 -0
- package/src/components/form/Form.vue +126 -0
- package/src/components/form/FormDescription.vue +12 -0
- package/src/components/form/FormField.vue +127 -0
- package/src/components/form/FormItem.vue +12 -0
- package/src/components/form/FormLabel.vue +14 -0
- package/src/components/form/FormMessage.vue +31 -0
- package/src/components/form/context.ts +54 -0
- package/src/components/form/errors.ts +6 -0
- package/src/components/form/index.ts +7 -0
- package/src/components/form/types.ts +55 -0
- package/src/components/hover-card/HoverCard.vue +27 -0
- package/src/components/hover-card/HoverCardContent.vue +36 -0
- package/src/components/hover-card/HoverCardTrigger.vue +12 -0
- package/src/components/hover-card/index.ts +3 -0
- package/src/components/ifta-label/IftaLabel.vue +26 -0
- package/src/components/ifta-label/index.ts +1 -0
- package/src/components/index.ts +89 -0
- package/src/components/input/Input.vue +87 -0
- package/src/components/input/index.ts +1 -0
- package/src/components/input-color/ColorArea.vue +36 -0
- package/src/components/input-color/ColorField.vue +32 -0
- package/src/components/input-color/ColorSlider.vue +31 -0
- package/src/components/input-color/InputColor.vue +65 -0
- package/src/components/input-color/index.ts +16 -0
- package/src/components/input-group/InputGroup.vue +26 -0
- package/src/components/input-group/InputGroupAddon.vue +32 -0
- package/src/components/input-group/InputGroupInput.vue +33 -0
- package/src/components/input-group/index.ts +17 -0
- package/src/components/input-number/InputNumber.vue +82 -0
- package/src/components/input-number/index.ts +1 -0
- package/src/components/input-otp/InputOtp.vue +186 -0
- package/src/components/input-otp/index.ts +1 -0
- package/src/components/input-password/InputPassword.vue +116 -0
- package/src/components/input-password/index.ts +5 -0
- package/src/components/input-tags/InputTags.vue +38 -0
- package/src/components/input-tags/InputTagsClear.vue +39 -0
- package/src/components/input-tags/InputTagsInput.vue +27 -0
- package/src/components/input-tags/InputTagsItem.vue +40 -0
- package/src/components/input-tags/InputTagsItemDelete.vue +32 -0
- package/src/components/input-tags/InputTagsItemText.vue +18 -0
- package/src/components/input-tags/index.ts +16 -0
- package/src/components/kbd/Kbd.vue +17 -0
- package/src/components/kbd/KbdGroup.vue +15 -0
- package/src/components/kbd/index.ts +4 -0
- package/src/components/label/Label.vue +23 -0
- package/src/components/label/index.ts +1 -0
- package/src/components/listbox/Listbox.vue +42 -0
- package/src/components/listbox/ListboxContent.vue +22 -0
- package/src/components/listbox/ListboxFilter.vue +34 -0
- package/src/components/listbox/ListboxGroup.vue +31 -0
- package/src/components/listbox/ListboxItem.vue +46 -0
- package/src/components/listbox/index.ts +19 -0
- package/src/components/mask/Mask.vue +205 -0
- package/src/components/mask/index.ts +15 -0
- package/src/components/menubar/Menubar.vue +26 -0
- package/src/components/menubar/MenubarCheckboxItem.vue +30 -0
- package/src/components/menubar/MenubarContent.vue +52 -0
- package/src/components/menubar/MenubarGroup.vue +12 -0
- package/src/components/menubar/MenubarItem.vue +32 -0
- package/src/components/menubar/MenubarLabel.vue +21 -0
- package/src/components/menubar/MenubarMenu.vue +12 -0
- package/src/components/menubar/MenubarRadioGroup.vue +15 -0
- package/src/components/menubar/MenubarRadioItem.vue +30 -0
- package/src/components/menubar/MenubarSeparator.vue +12 -0
- package/src/components/menubar/MenubarShortcut.vue +13 -0
- package/src/components/menubar/MenubarSub.vue +15 -0
- package/src/components/menubar/MenubarSubContent.vue +26 -0
- package/src/components/menubar/MenubarSubTrigger.vue +26 -0
- package/src/components/menubar/MenubarTrigger.vue +30 -0
- package/src/components/menubar/index.ts +29 -0
- package/src/components/navigation-menu/NavigationMenu.vue +43 -0
- package/src/components/navigation-menu/NavigationMenuContent.vue +63 -0
- package/src/components/navigation-menu/NavigationMenuIndicator.vue +31 -0
- package/src/components/navigation-menu/NavigationMenuItem.vue +20 -0
- package/src/components/navigation-menu/NavigationMenuLink.vue +34 -0
- package/src/components/navigation-menu/NavigationMenuList.vue +23 -0
- package/src/components/navigation-menu/NavigationMenuTrigger.vue +38 -0
- package/src/components/navigation-menu/NavigationMenuViewport.vue +34 -0
- package/src/components/navigation-menu/index.ts +18 -0
- package/src/components/order-list/OrderList.vue +168 -0
- package/src/components/order-list/SelectableList.vue +200 -0
- package/src/components/order-list/index.ts +1 -0
- package/src/components/pagination/Pagination.vue +133 -0
- package/src/components/pagination/index.ts +1 -0
- package/src/components/panel/Panel.vue +69 -0
- package/src/components/panel/index.ts +1 -0
- package/src/components/pick-list/PickList.vue +211 -0
- package/src/components/pick-list/index.ts +1 -0
- package/src/components/popover/Popover.vue +15 -0
- package/src/components/popover/PopoverAnchor.vue +12 -0
- package/src/components/popover/PopoverContent.vue +39 -0
- package/src/components/popover/PopoverTrigger.vue +12 -0
- package/src/components/popover/index.ts +4 -0
- package/src/components/progress/Progress.vue +39 -0
- package/src/components/progress/index.ts +1 -0
- package/src/components/props-table/PropsTable.vue +62 -0
- package/src/components/props-table/index.ts +2 -0
- package/src/components/radio-group/RadioGroup.vue +52 -0
- package/src/components/radio-group/RadioGroupItem.vue +80 -0
- package/src/components/radio-group/RadioGroupItemField.vue +128 -0
- package/src/components/radio-group/index.ts +21 -0
- package/src/components/rating/Rating.vue +70 -0
- package/src/components/rating/RatingItem.vue +26 -0
- package/src/components/rating/RatingItemIndicator.vue +28 -0
- package/src/components/rating/RatingStar.vue +16 -0
- package/src/components/rating/index.ts +13 -0
- package/src/components/scroll-area/ScrollArea.vue +52 -0
- package/src/components/scroll-area/index.ts +1 -0
- package/src/components/search-input/SearchInput.vue +46 -0
- package/src/components/search-input/index.ts +1 -0
- package/src/components/select/Select.vue +15 -0
- package/src/components/select/SelectContent.vue +67 -0
- package/src/components/select/SelectGroup.vue +20 -0
- package/src/components/select/SelectItem.vue +59 -0
- package/src/components/select/SelectLabel.vue +20 -0
- package/src/components/select/SelectSeparator.vue +18 -0
- package/src/components/select/SelectTrigger.vue +101 -0
- package/src/components/select/SelectValue.vue +46 -0
- package/src/components/select/index.ts +8 -0
- package/src/components/separator/Separator.vue +25 -0
- package/src/components/separator/index.ts +1 -0
- package/src/components/sheet/Sheet.vue +15 -0
- package/src/components/sheet/SheetClose.vue +12 -0
- package/src/components/sheet/SheetContent.vue +45 -0
- package/src/components/sheet/SheetDescription.vue +19 -0
- package/src/components/sheet/SheetFooter.vue +12 -0
- package/src/components/sheet/SheetHeader.vue +12 -0
- package/src/components/sheet/SheetTitle.vue +19 -0
- package/src/components/sheet/SheetTrigger.vue +12 -0
- package/src/components/sheet/index.ts +10 -0
- package/src/components/sidebar/Sidebar.vue +108 -0
- package/src/components/sidebar/SidebarContent.vue +19 -0
- package/src/components/sidebar/SidebarFooter.vue +19 -0
- package/src/components/sidebar/SidebarGroup.vue +19 -0
- package/src/components/sidebar/SidebarGroupAction.vue +28 -0
- package/src/components/sidebar/SidebarGroupContent.vue +19 -0
- package/src/components/sidebar/SidebarGroupLabel.vue +25 -0
- package/src/components/sidebar/SidebarHeader.vue +19 -0
- package/src/components/sidebar/SidebarInput.vue +15 -0
- package/src/components/sidebar/SidebarInset.vue +22 -0
- package/src/components/sidebar/SidebarMenu.vue +19 -0
- package/src/components/sidebar/SidebarMenuAction.vue +41 -0
- package/src/components/sidebar/SidebarMenuBadge.vue +24 -0
- package/src/components/sidebar/SidebarMenuButton.vue +94 -0
- package/src/components/sidebar/SidebarMenuItem.vue +19 -0
- package/src/components/sidebar/SidebarMenuSkeleton.vue +36 -0
- package/src/components/sidebar/SidebarMenuSub.vue +22 -0
- package/src/components/sidebar/SidebarMenuSubButton.vue +39 -0
- package/src/components/sidebar/SidebarMenuSubItem.vue +19 -0
- package/src/components/sidebar/SidebarProvider.vue +114 -0
- package/src/components/sidebar/SidebarRail.vue +37 -0
- package/src/components/sidebar/SidebarSeparator.vue +15 -0
- package/src/components/sidebar/SidebarTrigger.vue +27 -0
- package/src/components/sidebar/context.ts +39 -0
- package/src/components/sidebar/index.ts +34 -0
- package/src/components/skeleton/Skeleton.vue +10 -0
- package/src/components/skeleton/index.ts +1 -0
- package/src/components/skip-link/SkipLink.vue +45 -0
- package/src/components/skip-link/index.ts +1 -0
- package/src/components/slider/Slider.vue +57 -0
- package/src/components/slider/index.ts +3 -0
- package/src/components/speed-dial/SpeedDial.vue +362 -0
- package/src/components/speed-dial/index.ts +28 -0
- package/src/components/spinner/Spinner.vue +20 -0
- package/src/components/spinner/index.ts +1 -0
- package/src/components/splitter/Splitter.vue +24 -0
- package/src/components/splitter/SplitterHandle.vue +51 -0
- package/src/components/splitter/SplitterPanel.vue +30 -0
- package/src/components/splitter/index.ts +14 -0
- package/src/components/stepper/Stepper.vue +34 -0
- package/src/components/stepper/StepperItem.vue +113 -0
- package/src/components/stepper/index.ts +2 -0
- package/src/components/switch/Switch.vue +35 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/table/Table.vue +24 -0
- package/src/components/table/TableBody.vue +12 -0
- package/src/components/table/TableCell.vue +12 -0
- package/src/components/table/TableEmpty.vue +20 -0
- package/src/components/table/TableHead.vue +17 -0
- package/src/components/table/TableHeader.vue +12 -0
- package/src/components/table/TableRow.vue +19 -0
- package/src/components/table/index.ts +7 -0
- package/src/components/tabs/Tabs.vue +23 -0
- package/src/components/tabs/TabsContent.vue +23 -0
- package/src/components/tabs/TabsList.vue +26 -0
- package/src/components/tabs/TabsTrigger.vue +30 -0
- package/src/components/tabs/index.ts +4 -0
- package/src/components/textarea/Textarea.vue +25 -0
- package/src/components/textarea/index.ts +1 -0
- package/src/components/theme-controls/LocaleMenu.vue +95 -0
- package/src/components/theme-controls/PaletteMenu.vue +155 -0
- package/src/components/theme-controls/ThemeToggle.vue +21 -0
- package/src/components/theme-controls/index.ts +15 -0
- package/src/components/timeline/Timeline.vue +20 -0
- package/src/components/timeline/TimelineItem.vue +68 -0
- package/src/components/timeline/index.ts +15 -0
- package/src/components/toast/Toaster.vue +62 -0
- package/src/components/toast/index.ts +4 -0
- package/src/components/toast/toast.ts +57 -0
- package/src/components/toggle/Toggle.vue +32 -0
- package/src/components/toggle/ToggleGroup.vue +56 -0
- package/src/components/toggle/ToggleGroupItem.vue +33 -0
- package/src/components/toggle/index.ts +18 -0
- package/src/components/token-table/TokenTable.vue +67 -0
- package/src/components/token-table/index.ts +2 -0
- package/src/components/toolbar/Toolbar.vue +34 -0
- package/src/components/toolbar/ToolbarButton.vue +33 -0
- package/src/components/toolbar/ToolbarSeparator.vue +21 -0
- package/src/components/toolbar/index.ts +3 -0
- package/src/components/tooltip/Tooltip.vue +15 -0
- package/src/components/tooltip/TooltipContent.vue +38 -0
- package/src/components/tooltip/TooltipProvider.vue +16 -0
- package/src/components/tooltip/TooltipTrigger.vue +12 -0
- package/src/components/tooltip/index.ts +4 -0
- package/src/components/tree/Tree.vue +53 -0
- package/src/components/tree/TreeItem.vue +59 -0
- package/src/components/tree/index.ts +21 -0
- package/src/components/tree-select/TreeSelect.vue +141 -0
- package/src/components/tree-select/index.ts +1 -0
- package/src/components/tree-table/TreeTable.vue +302 -0
- package/src/components/tree-table/features.ts +49 -0
- package/src/components/tree-table/index.ts +3 -0
- package/src/components/video-player/VideoPlayer.vue +472 -0
- package/src/components/video-player/index.ts +20 -0
- package/src/composables/index.ts +15 -0
- package/src/composables/useBase.ts +32 -0
- package/src/composables/useChartPalette.ts +33 -0
- package/src/composables/useColorMode.ts +29 -0
- package/src/composables/useConfirm.ts +81 -0
- package/src/composables/useDelayedFlag.ts +62 -0
- package/src/composables/useI18n.ts +107 -0
- package/src/composables/useIsMobile.ts +34 -0
- package/src/composables/useMenuAccent.ts +32 -0
- package/src/composables/useMenuAppearance.ts +31 -0
- package/src/composables/useMenuColor.ts +31 -0
- package/src/composables/usePalette.ts +30 -0
- package/src/composables/useStyle.ts +35 -0
- package/src/composables/useSurface.ts +30 -0
- package/src/composables/useTableQuery.ts +73 -0
- package/src/composables/useThemeController.ts +48 -0
- package/src/i18n/README.md +68 -0
- package/src/i18n/controller.ts +233 -0
- package/src/i18n/dictionary.ts +43 -0
- package/src/i18n/index.ts +34 -0
- package/src/i18n/registry.ts +64 -0
- package/src/i18n/ui/en/ui.json +114 -0
- package/src/i18n/ui/en/zod.json +16 -0
- package/src/i18n/ui/index.ts +48 -0
- package/src/i18n/ui/vi/ui.json +114 -0
- package/src/i18n/ui/vi/zod.json +16 -0
- package/src/lib/date.ts +24 -0
- package/src/lib/focus.ts +12 -0
- package/src/lib/icons.ts +345 -0
- package/src/lib/menu-styles.ts +16 -0
- package/src/lib/menu.ts +20 -0
- package/src/lib/platform.ts +5 -0
- package/src/lib/types.ts +6 -0
- package/src/lib/utils.ts +9 -0
- package/src/lib/zod-messages.ts +127 -0
- package/src/styles/chart.css +30 -0
- package/src/styles/index.css +18 -0
- package/src/variants/README.md +55 -0
- package/src/variants/components/accordion.ts +71 -0
- package/src/variants/components/alert.ts +20 -0
- package/src/variants/components/badge.ts +76 -0
- package/src/variants/components/button-group.ts +136 -0
- package/src/variants/components/button.ts +160 -0
- package/src/variants/components/checkbox.ts +67 -0
- package/src/variants/components/chip.ts +51 -0
- package/src/variants/components/code-block.ts +22 -0
- package/src/variants/components/dialog.ts +28 -0
- package/src/variants/components/empty.ts +60 -0
- package/src/variants/components/field.ts +35 -0
- package/src/variants/components/float-label.ts +95 -0
- package/src/variants/components/input-group.ts +73 -0
- package/src/variants/components/input-password.ts +68 -0
- package/src/variants/components/kbd.ts +47 -0
- package/src/variants/components/picker-trigger.ts +30 -0
- package/src/variants/components/radio-group.ts +63 -0
- package/src/variants/components/sheet.ts +23 -0
- package/src/variants/components/sidebar.ts +33 -0
- package/src/variants/components/slider.ts +49 -0
- package/src/variants/components/timeline.ts +45 -0
- package/src/variants/components/toggle.ts +58 -0
- package/src/variants/index.ts +35 -0
- package/src/variants/lib/chart/polar.ts +139 -0
- package/src/variants/lib/chart/tooltip.ts +101 -0
- package/src/variants/lib/chart/types.ts +172 -0
- package/src/variants/lib/chart/xy-label.ts +42 -0
- package/src/variants/lib/focus.ts +66 -0
- package/src/variants/lib/form-errors.ts +59 -0
- package/src/variants/lib/highlight.ts +548 -0
- package/src/variants/lib/list-move.ts +63 -0
- package/src/variants/lib/mask.ts +106 -0
- package/src/variants/lib/media.ts +56 -0
- package/src/variants/lib/menu-styles.ts +71 -0
- package/src/variants/lib/platform.ts +68 -0
- package/src/variants/lib/tree.ts +119 -0
- package/src/variants/lib/upload.ts +58 -0
- package/src/variants/lib/utils.ts +11 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ChartTooltipOptions, ChartTooltipRow } from './types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bản COMPONENT của hộp tooltip, dùng cho các biểu đồ tự vẽ (Pie, Radial, Radar).
|
|
6
|
+
*
|
|
7
|
+
* Vì sao có hai bản cùng một hộp: Unovis chỉ nhận `template` trả về CHUỖI HTML
|
|
8
|
+
* nên phần XY phải đi qua `renderTooltip()`; còn ở đây ta tự vẽ SVG nên dựng
|
|
9
|
+
* bằng Vue được — và bắt buộc phải vậy, repo bật `vue/no-v-html` ở mức error.
|
|
10
|
+
*
|
|
11
|
+
* Hai bản phải giữ CÙNG markup. Sửa một bên thì sửa cả bên kia.
|
|
12
|
+
*/
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
title?: string
|
|
15
|
+
rows: ChartTooltipRow[]
|
|
16
|
+
options?: ChartTooltipOptions
|
|
17
|
+
}>()
|
|
18
|
+
|
|
19
|
+
/** Ba kiểu chỉ báo, khớp `INDICATOR_CLASS` trong `tooltip.ts`. */
|
|
20
|
+
const INDICATOR = {
|
|
21
|
+
dot: 'size-2.5 rounded-[2px]',
|
|
22
|
+
line: 'h-3 w-1 rounded-[2px]',
|
|
23
|
+
dashed: 'h-3 w-0 border-[1.5px] border-dashed',
|
|
24
|
+
} as const
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<div class="grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-popover px-2.5 py-1.5 text-xs shadow-xl">
|
|
29
|
+
<div v-if="props.title && !props.options?.hideLabel" class="font-medium text-popover-foreground">
|
|
30
|
+
{{ props.title }}
|
|
31
|
+
</div>
|
|
32
|
+
<div class="grid gap-1.5">
|
|
33
|
+
<div v-for="row in props.rows" :key="row.key" class="flex w-full items-center gap-2">
|
|
34
|
+
<div
|
|
35
|
+
v-if="!props.options?.hideIndicator"
|
|
36
|
+
class="shrink-0"
|
|
37
|
+
:class="INDICATOR[props.options?.indicator ?? 'dot']"
|
|
38
|
+
:style="(props.options?.indicator ?? 'dot') === 'dashed'
|
|
39
|
+
? { borderColor: row.color }
|
|
40
|
+
: { background: row.color }"
|
|
41
|
+
/>
|
|
42
|
+
<div class="flex flex-1 items-center justify-between gap-4 leading-none">
|
|
43
|
+
<span class="text-muted-foreground">{{ row.label }}</span>
|
|
44
|
+
<span class="font-mono font-medium tabular-nums text-foreground">{{ row.value }}</span>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
<script setup lang="ts" generic="T extends Record<string, unknown>">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import type { ChartBaseProps, ChartCurve, ChartLabelKey } from './types'
|
|
4
|
+
import { VisAxis, VisLine, VisScatter, VisXYContainer, VisXYLabels } from '@unovis/vue'
|
|
5
|
+
import { computed, ref } from 'vue'
|
|
6
|
+
import { cn } from '../../lib/utils'
|
|
7
|
+
import ChartContainer from './ChartContainer.vue'
|
|
8
|
+
import ChartCrosshair from './ChartCrosshair.vue'
|
|
9
|
+
import ChartLegend from './ChartLegend.vue'
|
|
10
|
+
import { toCurveType, useChartSeries } from './useChartSeries'
|
|
11
|
+
import { xyLabelHtml } from './xy-label'
|
|
12
|
+
|
|
13
|
+
const props = withDefaults(defineProps<ClassProps & ChartBaseProps<T> & {
|
|
14
|
+
/** @default 'monotone' */
|
|
15
|
+
curve?: ChartCurve
|
|
16
|
+
/** Vẽ chấm tại từng điểm dữ liệu. @default false */
|
|
17
|
+
showDots?: boolean
|
|
18
|
+
/** Bán kính chấm, pixel. @default 4 */
|
|
19
|
+
dotSize?: number
|
|
20
|
+
/** Chấm rỗng ruột — viền theo màu chuỗi, ruột theo màu nền. @default false */
|
|
21
|
+
hollowDots?: boolean
|
|
22
|
+
/** In nhãn phía trên từng điểm. @default false */
|
|
23
|
+
showLabels?: boolean
|
|
24
|
+
/** Nhãn đó in giá trị hay tên hạng mục. @default 'value' */
|
|
25
|
+
labelKey?: ChartLabelKey
|
|
26
|
+
/** Bấm vào legend để ẩn/hiện chuỗi. @default false */
|
|
27
|
+
interactiveLegend?: boolean
|
|
28
|
+
/**
|
|
29
|
+
* Thời lượng animation vào trang, mili giây. Mặc định **0**: 68 biểu đồ cùng
|
|
30
|
+
* mount một lúc, animation từ 0 làm cả trang trông như cột bị nén dí xuống
|
|
31
|
+
* đáy trong ~0,7 giây đầu — đã đo, và đã khiến chính tôi lẫn người dùng tưởng
|
|
32
|
+
* là lỗi hình học. Đặt 400–600 nếu muốn hiệu ứng ở màn dashboard chỉ có vài
|
|
33
|
+
* biểu đồ.
|
|
34
|
+
* @default 0
|
|
35
|
+
*/
|
|
36
|
+
duration?: number
|
|
37
|
+
}>(), {
|
|
38
|
+
duration: 0,
|
|
39
|
+
curve: 'monotone',
|
|
40
|
+
showDots: false,
|
|
41
|
+
dotSize: 4,
|
|
42
|
+
hollowDots: false,
|
|
43
|
+
showLabels: false,
|
|
44
|
+
labelKey: 'value',
|
|
45
|
+
showXAxis: true,
|
|
46
|
+
showYAxis: true,
|
|
47
|
+
showTooltip: true,
|
|
48
|
+
showLegend: false,
|
|
49
|
+
showGridLine: true,
|
|
50
|
+
showCursor: true,
|
|
51
|
+
interactiveLegend: false,
|
|
52
|
+
margin: () => ({ top: 8, right: 8, bottom: 0, left: 0 }),
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const hidden = ref<string[]>([])
|
|
56
|
+
|
|
57
|
+
const { categories, plotData, xTicks, isMounted, topOf } = useChartSeries(props)
|
|
58
|
+
|
|
59
|
+
const curveType = computed(() => toCurveType(props.curve))
|
|
60
|
+
|
|
61
|
+
function colorOf(key: string): string {
|
|
62
|
+
return `var(--color-${key})`
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function labelOf(key: string): string {
|
|
66
|
+
return props.config[key]?.label ?? key
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Chuỗi bị tắt thì làm mờ bằng chính MÀU, không dùng `stroke-opacity`:
|
|
71
|
+
* `_renderLines` của Unovis đặt cứng `stroke-opacity: 1` ở mỗi lần vẽ nên ghi
|
|
72
|
+
* đè thuộc tính đó sẽ bị xoá ngay lần render kế tiếp.
|
|
73
|
+
*/
|
|
74
|
+
function strokeOf(key: string): string {
|
|
75
|
+
return hidden.value.includes(key)
|
|
76
|
+
? `color-mix(in oklab, var(--color-${key}) 12%, transparent)`
|
|
77
|
+
: `var(--color-${key})`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Kẻ lưới không đi kèm việc có hiện trục tung hay không — xem ghi chú cùng tên
|
|
82
|
+
* trong `BarChart.vue`. Trục vẫn dựng nhưng nhãn để rỗng.
|
|
83
|
+
*/
|
|
84
|
+
const showY = computed(() => props.showYAxis || props.showGridLine)
|
|
85
|
+
|
|
86
|
+
/** Nhãn rỗng cho trục chỉ tồn tại để mang kẻ lưới. */
|
|
87
|
+
function blankTick(): string {
|
|
88
|
+
return ''
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function formatX(tick: number | Date, i: number, ticks: number[] | Date[]): string {
|
|
92
|
+
if (props.xFormatter)
|
|
93
|
+
return props.xFormatter(tick, i, ticks)
|
|
94
|
+
return String(props.data[Number(tick)]?.[props.index] ?? '')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Nhãn phía trên điểm, cách 12px — đúng `position="top" offset={12}` của shadcn.
|
|
99
|
+
* Lệch theo pixel nên phải đi qua `xyLabelHtml`; xem ghi chú ở `xy-label.ts`.
|
|
100
|
+
*
|
|
101
|
+
* Cộng thêm nửa chiều cao chữ vì `offset` của Recharts đo mép chữ, còn `dy` đo tâm.
|
|
102
|
+
*/
|
|
103
|
+
const LABEL_GAP_Y = 12 + 12 / 2
|
|
104
|
+
function labelAt(key: string) {
|
|
105
|
+
return (d: Record<string, unknown>): string => {
|
|
106
|
+
if (props.labelKey === 'name') {
|
|
107
|
+
const raw = String(d[props.index] ?? '')
|
|
108
|
+
return xyLabelHtml(props.config[raw]?.label ?? raw, { dy: -LABEL_GAP_Y })
|
|
109
|
+
}
|
|
110
|
+
const raw = Number(d[key]) || 0
|
|
111
|
+
const text = props.valueFormatter ? props.valueFormatter(raw, key) : String(raw)
|
|
112
|
+
return xyLabelHtml(text, { dy: -LABEL_GAP_Y })
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function toggle(key: string): void {
|
|
117
|
+
hidden.value = hidden.value.includes(key)
|
|
118
|
+
? hidden.value.filter(k => k !== key)
|
|
119
|
+
: [...hidden.value, key]
|
|
120
|
+
}
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<template>
|
|
124
|
+
<ChartContainer :config="props.config" :show-cursor="props.showCursor" :class="cn(props.class)">
|
|
125
|
+
<VisXYContainer
|
|
126
|
+
:data="plotData"
|
|
127
|
+
:margin="props.margin"
|
|
128
|
+
:duration="props.duration"
|
|
129
|
+
:style="{ height: isMounted ? '100%' : 'auto' }"
|
|
130
|
+
>
|
|
131
|
+
<VisLine
|
|
132
|
+
v-for="key in categories"
|
|
133
|
+
:key="key"
|
|
134
|
+
:x="(_d: Record<string, unknown>, i: number) => i"
|
|
135
|
+
:y="topOf(key)"
|
|
136
|
+
:color="strokeOf(key)"
|
|
137
|
+
:curve-type="curveType"
|
|
138
|
+
:line-width="2"
|
|
139
|
+
/>
|
|
140
|
+
|
|
141
|
+
<!--
|
|
142
|
+
Chấm vẽ bằng VisScatter chồng lên đường: `VisLine` của Unovis không có
|
|
143
|
+
tuỳ chọn chấm nào. Đặt SAU đường để chấm nằm trên.
|
|
144
|
+
-->
|
|
145
|
+
<template v-if="props.showDots">
|
|
146
|
+
<VisScatter
|
|
147
|
+
v-for="key in categories"
|
|
148
|
+
:key="`dot-${key}`"
|
|
149
|
+
:x="(_d: Record<string, unknown>, i: number) => i"
|
|
150
|
+
:y="topOf(key)"
|
|
151
|
+
:color="props.hollowDots ? 'var(--background)' : strokeOf(key)"
|
|
152
|
+
:stroke-color="strokeOf(key)"
|
|
153
|
+
:stroke-width="2"
|
|
154
|
+
:size="props.dotSize * 2"
|
|
155
|
+
/>
|
|
156
|
+
</template>
|
|
157
|
+
|
|
158
|
+
<template v-if="props.showLabels">
|
|
159
|
+
<VisXYLabels
|
|
160
|
+
v-for="key in categories"
|
|
161
|
+
:key="`label-${key}`"
|
|
162
|
+
:x="(d: Record<string, unknown>) => Number(d.__i)"
|
|
163
|
+
:y="topOf(key)"
|
|
164
|
+
:label="labelAt(key)"
|
|
165
|
+
:label-font-size="12"
|
|
166
|
+
:clustering="false"
|
|
167
|
+
background-color="none"
|
|
168
|
+
color="var(--foreground)"
|
|
169
|
+
/>
|
|
170
|
+
</template>
|
|
171
|
+
|
|
172
|
+
<ChartCrosshair
|
|
173
|
+
v-if="props.showTooltip"
|
|
174
|
+
:index="props.index"
|
|
175
|
+
:categories="categories as Extract<keyof T, string>[]"
|
|
176
|
+
:hidden="hidden"
|
|
177
|
+
:color-of="colorOf"
|
|
178
|
+
:label-of="labelOf"
|
|
179
|
+
:value-formatter="props.valueFormatter"
|
|
180
|
+
:options="props.tooltip"
|
|
181
|
+
:default-index="props.defaultIndex"
|
|
182
|
+
:show-cursor="props.showCursor"
|
|
183
|
+
/>
|
|
184
|
+
|
|
185
|
+
<VisAxis
|
|
186
|
+
v-if="props.showXAxis"
|
|
187
|
+
type="x"
|
|
188
|
+
:tick-values="xTicks"
|
|
189
|
+
:tick-format="formatX"
|
|
190
|
+
:grid-line="false"
|
|
191
|
+
:tick-line="false"
|
|
192
|
+
:domain-line="false"
|
|
193
|
+
/>
|
|
194
|
+
<VisAxis
|
|
195
|
+
v-if="showY"
|
|
196
|
+
type="y"
|
|
197
|
+
:tick-format="props.showYAxis ? props.yFormatter : blankTick"
|
|
198
|
+
:grid-line="props.showGridLine"
|
|
199
|
+
:tick-line="false"
|
|
200
|
+
:domain-line="false"
|
|
201
|
+
/>
|
|
202
|
+
|
|
203
|
+
<slot />
|
|
204
|
+
</VisXYContainer>
|
|
205
|
+
|
|
206
|
+
<ChartLegend
|
|
207
|
+
v-if="props.showLegend"
|
|
208
|
+
:categories="categories"
|
|
209
|
+
:hidden="hidden"
|
|
210
|
+
:interactive="props.interactiveLegend"
|
|
211
|
+
@toggle="toggle"
|
|
212
|
+
/>
|
|
213
|
+
</ChartContainer>
|
|
214
|
+
</template>
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
<script setup lang="ts" generic="T extends Record<string, unknown>">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import type { ChartConfig, ChartLabelKey, ChartTooltipOptions, ChartTooltipRow, ChartValueFormatter } from './types'
|
|
4
|
+
import { computed, ref } from 'vue'
|
|
5
|
+
import { cn } from '../../lib/utils'
|
|
6
|
+
import ChartContainer from './ChartContainer.vue'
|
|
7
|
+
import ChartLegend from './ChartLegend.vue'
|
|
8
|
+
import ChartTooltipCard from './ChartTooltipCard.vue'
|
|
9
|
+
import { annularArcPath, polarToCartesian } from './polar'
|
|
10
|
+
import { formatChartValue } from './tooltip'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Pie và Donut là CÙNG một component, khác đúng một con số: `arcWidth`.
|
|
14
|
+
*
|
|
15
|
+
* VẼ TAY chứ không dùng `VisDonut`. Lý do cụ thể: Unovis Donut không có tuỳ chọn
|
|
16
|
+
* nhãn nào, mà nhãn trên lát là thứ một nửa số biến thể cần (nhãn giữa lát, danh
|
|
17
|
+
* sách nhãn ngoài rìa, lát được làm nổi). Tự vẽ thì `annularArcPath` đã có sẵn
|
|
18
|
+
* cho Radial dùng lại được, và ba biểu đồ tròn trong bộ này cùng một cách dựng.
|
|
19
|
+
*/
|
|
20
|
+
const props = withDefaults(defineProps<ClassProps & {
|
|
21
|
+
data: T[]
|
|
22
|
+
/** Khoá chứa TÊN của lát cắt. Tên đó cũng là khoá tra vào `config`. */
|
|
23
|
+
index: Extract<keyof T, string>
|
|
24
|
+
/** Khoá chứa giá trị số của lát cắt. */
|
|
25
|
+
value: Extract<keyof T, string>
|
|
26
|
+
/** Nhãn, màu và icon, khoá theo giá trị của `index`. */
|
|
27
|
+
config: ChartConfig
|
|
28
|
+
/** Bề dày vành, pixel. `0` cho ra hình quạt đặc. @default 0 */
|
|
29
|
+
arcWidth?: number
|
|
30
|
+
/**
|
|
31
|
+
* Bề rộng đường tách giữa hai lát, pixel. Đặt `0` thì các lát dính liền.
|
|
32
|
+
*
|
|
33
|
+
* Là VIỀN chứ không phải khe hở theo góc — đây là chỗ bản trước sai. Khe hở
|
|
34
|
+
* theo góc rộng ở vành và thu về 0 ở tâm, nên vệt trắng có hình cái nêm và
|
|
35
|
+
* nhìn lệch; viền thì rộng đều suốt chiều dài mép, đúng cách shadcn (Recharts
|
|
36
|
+
* tách lát bằng `stroke`, và `separator-none` chỉ là `stroke="0"`).
|
|
37
|
+
* @default 2
|
|
38
|
+
*/
|
|
39
|
+
separatorWidth?: number
|
|
40
|
+
/** In nhãn cho từng lát. @default false */
|
|
41
|
+
showLabels?: boolean
|
|
42
|
+
/** Nội dung nhãn: giá trị hay tên hạng mục. @default 'value' */
|
|
43
|
+
labelKey?: ChartLabelKey
|
|
44
|
+
/**
|
|
45
|
+
* Nhãn nằm TRONG lát hay NGOÀI vành.
|
|
46
|
+
*
|
|
47
|
+
* Hai chỗ này không thay thế nhau được: nhãn trong lát vẽ trên nền màu chuỗi
|
|
48
|
+
* nên lấy màu chữ dẫn xuất của chính chuỗi đó, còn nhãn ngoài vành nằm trên
|
|
49
|
+
* nền card nên lấy `--foreground`. Chọn nhầm là chữ chìm mất.
|
|
50
|
+
* @default 'inside'
|
|
51
|
+
*/
|
|
52
|
+
labelPosition?: 'inside' | 'outside'
|
|
53
|
+
/** Kẻ đường dẫn từ lát ra nhãn ngoài. Chỉ có nghĩa với `labelPosition="outside"`. @default true */
|
|
54
|
+
labelLine?: boolean
|
|
55
|
+
/** Chỉ số lát được làm nổi (bán kính lớn hơn). */
|
|
56
|
+
activeIndex?: number
|
|
57
|
+
/** Chữ lớn ở giữa. Chỉ vẽ khi `arcWidth > 0`. */
|
|
58
|
+
centralLabel?: string
|
|
59
|
+
/** Dòng nhỏ dưới `centralLabel`. */
|
|
60
|
+
centralSubLabel?: string
|
|
61
|
+
/** @default true */
|
|
62
|
+
showTooltip?: boolean
|
|
63
|
+
/** @default false */
|
|
64
|
+
showLegend?: boolean
|
|
65
|
+
valueFormatter?: ChartValueFormatter
|
|
66
|
+
tooltip?: ChartTooltipOptions
|
|
67
|
+
}>(), {
|
|
68
|
+
arcWidth: 0,
|
|
69
|
+
separatorWidth: 2,
|
|
70
|
+
showLabels: false,
|
|
71
|
+
labelKey: 'value',
|
|
72
|
+
labelPosition: 'inside',
|
|
73
|
+
labelLine: true,
|
|
74
|
+
showTooltip: true,
|
|
75
|
+
showLegend: false,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Khung vẽ. Nhãn ngoài vành thì phải NỚI NGANG: nhãn nằm ngang nên chỗ thiếu là
|
|
80
|
+
* chiều ngang, đúng như ở RadarChart — khung vuông xén mất "Chrome" và "Edge" ở
|
|
81
|
+
* hai mép.
|
|
82
|
+
*/
|
|
83
|
+
const VIEW_H = 260
|
|
84
|
+
const CENTER = VIEW_H / 2
|
|
85
|
+
/** Nhãn nằm ngoài vành. */
|
|
86
|
+
const outside = computed(() => props.showLabels && props.labelPosition === 'outside')
|
|
87
|
+
/** Chừa 10px cho lát `activeIndex` phình ra; nhãn ngoài thì chừa 30px. */
|
|
88
|
+
const outerRadius = computed(() => CENTER - (outside.value ? 30 : 10))
|
|
89
|
+
const viewWidth = computed(() => (outside.value ? 380 : VIEW_H))
|
|
90
|
+
/** Tâm lệch sang giữa khung rộng, nhưng bán kính vẫn tính theo chiều cao. */
|
|
91
|
+
const cx = computed(() => viewWidth.value / 2)
|
|
92
|
+
/** Lát được làm nổi phình thêm chừng này. */
|
|
93
|
+
const ACTIVE_GROW = 8
|
|
94
|
+
|
|
95
|
+
const hidden = ref<string[]>([])
|
|
96
|
+
const hoverIndex = ref<number | null>(null)
|
|
97
|
+
const pointer = ref({ x: 0, y: 0 })
|
|
98
|
+
|
|
99
|
+
const names = computed(() => props.data.map(row => String(row[props.index])))
|
|
100
|
+
|
|
101
|
+
const visibleRows = computed(() =>
|
|
102
|
+
props.data.filter(row => !hidden.value.includes(String(row[props.index]))))
|
|
103
|
+
|
|
104
|
+
const total = computed(() =>
|
|
105
|
+
visibleRows.value.reduce((sum, row) => sum + (Number(row[props.value]) || 0), 0))
|
|
106
|
+
|
|
107
|
+
function colorOf(name: string): string {
|
|
108
|
+
return `var(--color-${name})`
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function labelOf(name: string): string {
|
|
112
|
+
return props.config[name]?.label ?? name
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function formatValue(raw: number, name: string): string {
|
|
116
|
+
return props.valueFormatter ? props.valueFormatter(raw, name) : formatChartValue(raw)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Các lát, kèm sẵn góc và path.
|
|
121
|
+
*
|
|
122
|
+
* Khe hở tính bằng ĐỘ rồi trừ vào hai đầu mỗi lát, chứ không cộng thêm vào tổng:
|
|
123
|
+
* cộng thêm sẽ làm tổng vượt 360° và lát cuối tràn qua lát đầu.
|
|
124
|
+
*/
|
|
125
|
+
const slices = computed(() => {
|
|
126
|
+
const rows = visibleRows.value
|
|
127
|
+
const sum = total.value
|
|
128
|
+
let angle = 0
|
|
129
|
+
|
|
130
|
+
return rows.map((row, i) => {
|
|
131
|
+
const name = String(row[props.index])
|
|
132
|
+
const raw = Number(row[props.value]) || 0
|
|
133
|
+
const span = sum === 0 ? 0 : (raw / sum) * 360
|
|
134
|
+
const start = angle
|
|
135
|
+
angle += span
|
|
136
|
+
|
|
137
|
+
const active = props.activeIndex === i
|
|
138
|
+
const outer = outerRadius.value + (active ? ACTIVE_GROW : 0)
|
|
139
|
+
const inner = props.arcWidth > 0 ? Math.max(0, outer - props.arcWidth - (active ? ACTIVE_GROW : 0)) : 0
|
|
140
|
+
const mid = start + span / 2
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
key: name,
|
|
144
|
+
name,
|
|
145
|
+
label: labelOf(name),
|
|
146
|
+
color: colorOf(name),
|
|
147
|
+
raw,
|
|
148
|
+
value: formatValue(raw, name),
|
|
149
|
+
share: sum === 0 ? 0 : raw / sum,
|
|
150
|
+
path: annularArcPath(cx.value, CENTER, outer, inner, start, start + span),
|
|
151
|
+
// Tâm lát: giữa bán kính trong và ngoài, để nhãn không đè lên mép.
|
|
152
|
+
centroid: polarToCartesian(cx.value, CENTER, (outer + inner) / 2, mid),
|
|
153
|
+
outerAt: polarToCartesian(cx.value, CENTER, outer + 6, mid),
|
|
154
|
+
leaderAt: polarToCartesian(cx.value, CENTER, outer + 18, mid),
|
|
155
|
+
mid,
|
|
156
|
+
span,
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
const hovered = computed(() => (hoverIndex.value === null ? null : slices.value[hoverIndex.value] ?? null))
|
|
162
|
+
|
|
163
|
+
const tooltipRows = computed<ChartTooltipRow[]>(() => {
|
|
164
|
+
const s = hovered.value
|
|
165
|
+
return s ? [{ key: s.key, label: s.label, color: s.color, value: s.value }] : []
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
function onMove(event: MouseEvent): void {
|
|
169
|
+
const host = (event.currentTarget as HTMLElement).getBoundingClientRect()
|
|
170
|
+
pointer.value = { x: event.clientX - host.left, y: event.clientY - host.top }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function textOf(slice: { value: string, label: string }): string {
|
|
174
|
+
return props.labelKey === 'name' ? slice.label : slice.value
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function toggle(name: string): void {
|
|
178
|
+
hidden.value = hidden.value.includes(name)
|
|
179
|
+
? hidden.value.filter(n => n !== name)
|
|
180
|
+
: [...hidden.value, name]
|
|
181
|
+
}
|
|
182
|
+
</script>
|
|
183
|
+
|
|
184
|
+
<template>
|
|
185
|
+
<ChartContainer :config="props.config" :class="cn(props.class)">
|
|
186
|
+
<div class="relative flex min-h-0 flex-1 items-center justify-center" @mousemove="onMove">
|
|
187
|
+
<svg
|
|
188
|
+
:viewBox="`0 0 ${viewWidth} ${VIEW_H}`"
|
|
189
|
+
class="h-full max-h-full w-auto max-w-full"
|
|
190
|
+
role="img"
|
|
191
|
+
aria-label="Pie chart"
|
|
192
|
+
>
|
|
193
|
+
<g
|
|
194
|
+
v-for="(slice, i) in slices"
|
|
195
|
+
:key="slice.key"
|
|
196
|
+
@mouseenter="hoverIndex = i"
|
|
197
|
+
@mouseleave="hoverIndex = null"
|
|
198
|
+
>
|
|
199
|
+
<path
|
|
200
|
+
:d="slice.path"
|
|
201
|
+
:fill="slice.color"
|
|
202
|
+
:stroke="props.separatorWidth > 0 ? 'var(--card)' : 'none'"
|
|
203
|
+
:stroke-width="props.separatorWidth"
|
|
204
|
+
stroke-linejoin="round"
|
|
205
|
+
:opacity="hoverIndex === null || hoverIndex === i ? 1 : 0.72"
|
|
206
|
+
class="transition-opacity"
|
|
207
|
+
>
|
|
208
|
+
<title>{{ slice.label }}: {{ slice.value }}</title>
|
|
209
|
+
</path>
|
|
210
|
+
|
|
211
|
+
<!-- Nhãn giữa lát. Bỏ qua lát quá mỏng, chữ sẽ tràn ra ngoài. -->
|
|
212
|
+
<text
|
|
213
|
+
v-if="props.showLabels && !outside && slice.span > 18"
|
|
214
|
+
:x="slice.centroid.x"
|
|
215
|
+
:y="slice.centroid.y"
|
|
216
|
+
text-anchor="middle"
|
|
217
|
+
dominant-baseline="middle"
|
|
218
|
+
class="pointer-events-none text-[11px] font-medium"
|
|
219
|
+
:fill="`var(--color-${slice.name}-foreground)`"
|
|
220
|
+
>{{ textOf(slice) }}</text>
|
|
221
|
+
|
|
222
|
+
<!-- Nhãn ngoài vành, đường dẫn tuỳ chọn. -->
|
|
223
|
+
<template v-if="outside && slice.span > 10">
|
|
224
|
+
<line
|
|
225
|
+
v-if="props.labelLine"
|
|
226
|
+
:x1="slice.outerAt.x"
|
|
227
|
+
:y1="slice.outerAt.y"
|
|
228
|
+
:x2="slice.leaderAt.x"
|
|
229
|
+
:y2="slice.leaderAt.y"
|
|
230
|
+
stroke="var(--border)"
|
|
231
|
+
stroke-width="1"
|
|
232
|
+
/>
|
|
233
|
+
<text
|
|
234
|
+
:x="slice.leaderAt.x + (slice.mid % 360 < 180 ? 3 : -3)"
|
|
235
|
+
:y="slice.leaderAt.y"
|
|
236
|
+
:text-anchor="slice.mid % 360 < 180 ? 'start' : 'end'"
|
|
237
|
+
dominant-baseline="middle"
|
|
238
|
+
class="pointer-events-none fill-foreground text-[12px]"
|
|
239
|
+
>{{ textOf(slice) }}</text>
|
|
240
|
+
</template>
|
|
241
|
+
</g>
|
|
242
|
+
|
|
243
|
+
<template v-if="props.arcWidth > 0 && props.centralLabel">
|
|
244
|
+
<text
|
|
245
|
+
:x="cx"
|
|
246
|
+
:y="props.centralSubLabel ? CENTER - 4 : CENTER"
|
|
247
|
+
text-anchor="middle"
|
|
248
|
+
dominant-baseline="middle"
|
|
249
|
+
class="fill-foreground text-[24px] font-semibold tabular-nums"
|
|
250
|
+
>{{ props.centralLabel }}</text>
|
|
251
|
+
<text
|
|
252
|
+
v-if="props.centralSubLabel"
|
|
253
|
+
:x="cx"
|
|
254
|
+
:y="CENTER + 18"
|
|
255
|
+
text-anchor="middle"
|
|
256
|
+
dominant-baseline="middle"
|
|
257
|
+
class="fill-muted-foreground text-[12px]"
|
|
258
|
+
>{{ props.centralSubLabel }}</text>
|
|
259
|
+
</template>
|
|
260
|
+
</svg>
|
|
261
|
+
|
|
262
|
+
<!-- Tooltip bám con trỏ. `pointer-events-none` để nó không tự cướp hover. -->
|
|
263
|
+
<div
|
|
264
|
+
v-if="props.showTooltip && hovered"
|
|
265
|
+
class="pointer-events-none absolute z-(--z-raised)"
|
|
266
|
+
:style="{ left: `${pointer.x + 14}px`, top: `${pointer.y + 14}px` }"
|
|
267
|
+
>
|
|
268
|
+
<ChartTooltipCard :rows="tooltipRows" :options="{ hideLabel: true, ...props.tooltip }" />
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
<ChartLegend
|
|
273
|
+
v-if="props.showLegend"
|
|
274
|
+
:categories="names"
|
|
275
|
+
:hidden="hidden"
|
|
276
|
+
interactive
|
|
277
|
+
@toggle="toggle"
|
|
278
|
+
/>
|
|
279
|
+
</ChartContainer>
|
|
280
|
+
</template>
|