@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,362 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { CSSProperties } from 'vue'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import type { SpeedDialDirection, SpeedDialItem, SpeedDialType } from './index'
|
|
5
|
+
import { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'
|
|
6
|
+
import { useI18n } from '../../composables/useI18n'
|
|
7
|
+
import { Plus } from '../../lib/icons'
|
|
8
|
+
import { cn } from '../../lib/utils'
|
|
9
|
+
import { Button } from '../button'
|
|
10
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip'
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(
|
|
13
|
+
defineProps<ClassProps & {
|
|
14
|
+
/** Các mục bung ra. Mảng phẳng — SpeedDial không có submenu. */
|
|
15
|
+
model?: SpeedDialItem[]
|
|
16
|
+
type?: SpeedDialType
|
|
17
|
+
direction?: SpeedDialDirection
|
|
18
|
+
/** Bán kính quỹ đạo, tính bằng px. Chỉ dùng cho ba kiểu vòng cung. */
|
|
19
|
+
radius?: number
|
|
20
|
+
/** Độ trễ nối tiếp giữa hai mục, tính bằng ms. Đặt 0 thì bung cùng lúc. */
|
|
21
|
+
transitionDelay?: number
|
|
22
|
+
/** Phủ mờ phần còn lại của trang khi mở. */
|
|
23
|
+
mask?: boolean
|
|
24
|
+
hideOnClickOutside?: boolean
|
|
25
|
+
disabled?: boolean
|
|
26
|
+
/** Nút chính chỉ có icon nên bắt buộc phải có tên. */
|
|
27
|
+
ariaLabel?: string
|
|
28
|
+
}>(),
|
|
29
|
+
{
|
|
30
|
+
model: () => [],
|
|
31
|
+
type: 'linear',
|
|
32
|
+
direction: 'up',
|
|
33
|
+
radius: 80,
|
|
34
|
+
transitionDelay: 30,
|
|
35
|
+
mask: false,
|
|
36
|
+
hideOnClickOutside: true,
|
|
37
|
+
disabled: false,
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
const emit = defineEmits<{ 'update:visible': [value: boolean], 'click': [event: MouseEvent] }>()
|
|
42
|
+
|
|
43
|
+
const { t } = useI18n()
|
|
44
|
+
|
|
45
|
+
// Mặc định đi qua `t()` chứ không nằm trong `withDefaults` — default tính một
|
|
46
|
+
// lần lúc dựng component nên đổi ngôn ngữ xong nó vẫn giữ chuỗi cũ.
|
|
47
|
+
const ariaLabel = computed(() => props.ariaLabel ?? t('ui.speedDial.open'))
|
|
48
|
+
|
|
49
|
+
const visible = defineModel<boolean>('visible', { default: false })
|
|
50
|
+
|
|
51
|
+
const listId = useId()
|
|
52
|
+
const root = ref<HTMLElement>()
|
|
53
|
+
const itemRefs = ref<HTMLElement[]>([])
|
|
54
|
+
/** -1 nghĩa là tiêu điểm đang ở nút chính, không ở mục nào. */
|
|
55
|
+
const focusIndex = ref(-1)
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Kích thước cố định, KHÔNG lấy từ prop `size` của Button.
|
|
59
|
+
*
|
|
60
|
+
* Toạ độ từng mục phải tính bằng số trước khi trình duyệt vẽ, mà đọc kích thước
|
|
61
|
+
* thật thì phải chờ layout xong — chưa kể lúc đóng các mục đang chồng lên nhau.
|
|
62
|
+
* Chốt cứng hai con số rồi ép Button theo là cách duy nhất khớp được.
|
|
63
|
+
*/
|
|
64
|
+
const TRIGGER_SIZE = 36
|
|
65
|
+
const ITEM_SIZE = 32
|
|
66
|
+
/** Lệch để tâm mục trùng tâm nút chính. PrimeVue gọi là `--p-item-diff`. */
|
|
67
|
+
const CENTER = (TRIGGER_SIZE - ITEM_SIZE) / 2
|
|
68
|
+
/** Bước nhảy giữa hai mục ở kiểu `linear`. */
|
|
69
|
+
const LINEAR_STEP = ITEM_SIZE + 8
|
|
70
|
+
|
|
71
|
+
const DEG = Math.PI / 180
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Góc của mục thứ `index`, tính bằng độ.
|
|
75
|
+
*
|
|
76
|
+
* `circle` chia đều 360° nên mục cuối không trùng mục đầu — chia cho `n`. Hai
|
|
77
|
+
* kiểu cung hở thì mục đầu và mục cuối phải nằm đúng hai đầu mút, nên chia cho
|
|
78
|
+
* `n - 1`. Số đo lấy từ chính primevue.dev: 5 mục ⇒ circle 72°, semi 45°,
|
|
79
|
+
* quarter 22.5°.
|
|
80
|
+
*/
|
|
81
|
+
function angleOf(index: number): number {
|
|
82
|
+
const count = props.model.length
|
|
83
|
+
if (count < 2)
|
|
84
|
+
return 0
|
|
85
|
+
|
|
86
|
+
if (props.type === 'circle')
|
|
87
|
+
return (360 / count) * index
|
|
88
|
+
|
|
89
|
+
const sweep = props.type === 'semi-circle' ? 180 : 90
|
|
90
|
+
return (sweep / (count - 1)) * index
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function placeLinear(index: number): [number, number] {
|
|
94
|
+
const d = (index + 1) * LINEAR_STEP
|
|
95
|
+
|
|
96
|
+
switch (props.direction) {
|
|
97
|
+
case 'down': return [0, d]
|
|
98
|
+
case 'left': return [-d, 0]
|
|
99
|
+
case 'right': return [d, 0]
|
|
100
|
+
default: return [0, -d]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Độ lệch của mục thứ `index` so với tâm nút chính, tính bằng px.
|
|
106
|
+
*
|
|
107
|
+
* Bốn hướng KHÔNG phải bốn phép xoay của cùng một công thức: hướng dọc lấy cos
|
|
108
|
+
* cho trục ngang, hướng ngang thì hai trục **đổi vai** cos/sin — nếu không, một
|
|
109
|
+
* nửa số hướng sẽ quét ngược. Số đo đối chiếu từng mục với primevue.dev.
|
|
110
|
+
*/
|
|
111
|
+
function place(index: number): [number, number] {
|
|
112
|
+
if (props.type === 'linear')
|
|
113
|
+
return placeLinear(index)
|
|
114
|
+
|
|
115
|
+
const angle = angleOf(index) * DEG
|
|
116
|
+
const cos = props.radius * Math.cos(angle)
|
|
117
|
+
const sin = props.radius * Math.sin(angle)
|
|
118
|
+
|
|
119
|
+
if (props.type === 'circle')
|
|
120
|
+
return [cos, sin]
|
|
121
|
+
|
|
122
|
+
if (props.type === 'semi-circle') {
|
|
123
|
+
switch (props.direction) {
|
|
124
|
+
case 'down': return [cos, sin]
|
|
125
|
+
case 'left': return [-sin, cos]
|
|
126
|
+
case 'right': return [sin, cos]
|
|
127
|
+
default: return [cos, -sin]
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
switch (props.direction) {
|
|
132
|
+
case 'up-left': return [-cos, -sin]
|
|
133
|
+
case 'down-left': return [-sin, cos]
|
|
134
|
+
case 'down-right': return [sin, cos]
|
|
135
|
+
default: return [cos, -sin]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Lúc mở thì mục gần nút chính hiện trước; lúc đóng thì thu về theo thứ tự
|
|
141
|
+
* ngược lại. Chạy xuôi cả hai chiều trông như cái đuôi bị đứt: mục xa biến mất
|
|
142
|
+
* trước rồi mới tới mục gần, ngược với hướng chuyển động.
|
|
143
|
+
*/
|
|
144
|
+
function delayOf(index: number): string {
|
|
145
|
+
const step = props.transitionDelay
|
|
146
|
+
const order = visible.value ? index : props.model.length - 1 - index
|
|
147
|
+
return `${order * step}ms`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Vị trí bằng `transform`, KHÔNG bằng `left/right/top/bottom`.
|
|
152
|
+
*
|
|
153
|
+
* Neo theo `bottom` hay `right` thì lúc đóng thuộc tính đối diện phải về
|
|
154
|
+
* `auto`, mà `auto` không nội suy được — mục **nhảy** thẳng tới đích thay vì
|
|
155
|
+
* trượt, và chỉ nhảy ở những hướng neo kiểu đó. Đã đo: hướng `up`/`left` giật,
|
|
156
|
+
* `down`/`right` mượt, cùng một component. Dịch bằng `transform` thì cả tám
|
|
157
|
+
* hướng chạy giống hệt nhau, lại không bắt trình duyệt tính lại layout.
|
|
158
|
+
*/
|
|
159
|
+
function styleOf(index: number): CSSProperties {
|
|
160
|
+
const [x, y] = visible.value ? place(index) : [0, 0]
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
transform: `translate(${x.toFixed(3)}px, ${y.toFixed(3)}px) scale(${visible.value ? 1 : 0.5})`,
|
|
164
|
+
transitionDelay: delayOf(index),
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function close(): void {
|
|
169
|
+
if (!visible.value)
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
visible.value = false
|
|
173
|
+
focusIndex.value = -1
|
|
174
|
+
emit('update:visible', false)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function toggle(event: MouseEvent): void {
|
|
178
|
+
emit('click', event)
|
|
179
|
+
|
|
180
|
+
if (props.disabled)
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
visible.value = !visible.value
|
|
184
|
+
focusIndex.value = -1
|
|
185
|
+
emit('update:visible', visible.value)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function run(item: SpeedDialItem): void {
|
|
189
|
+
if (item.disabled)
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
if (!item.url)
|
|
193
|
+
item.command?.(item)
|
|
194
|
+
|
|
195
|
+
close()
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Đưa tiêu điểm tới mục thứ `next`, quay vòng ở hai đầu. */
|
|
199
|
+
async function focusItem(next: number): Promise<void> {
|
|
200
|
+
const count = props.model.length
|
|
201
|
+
if (!count)
|
|
202
|
+
return
|
|
203
|
+
|
|
204
|
+
focusIndex.value = (next + count) % count
|
|
205
|
+
await nextTick()
|
|
206
|
+
itemRefs.value[focusIndex.value]?.focus()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function onKeydown(event: KeyboardEvent): void {
|
|
210
|
+
if (!visible.value)
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
switch (event.key) {
|
|
214
|
+
case 'Escape':
|
|
215
|
+
close()
|
|
216
|
+
// Trả tiêu điểm về nút chính, nếu không nó rơi ra ngoài body.
|
|
217
|
+
root.value?.querySelector<HTMLElement>('[data-speed-dial-trigger]')?.focus()
|
|
218
|
+
break
|
|
219
|
+
case 'ArrowDown':
|
|
220
|
+
case 'ArrowRight':
|
|
221
|
+
event.preventDefault()
|
|
222
|
+
focusItem(focusIndex.value + 1)
|
|
223
|
+
break
|
|
224
|
+
case 'ArrowUp':
|
|
225
|
+
case 'ArrowLeft':
|
|
226
|
+
event.preventDefault()
|
|
227
|
+
focusItem(focusIndex.value <= 0 ? props.model.length - 1 : focusIndex.value - 1)
|
|
228
|
+
break
|
|
229
|
+
case 'Home':
|
|
230
|
+
event.preventDefault()
|
|
231
|
+
focusItem(0)
|
|
232
|
+
break
|
|
233
|
+
case 'End':
|
|
234
|
+
event.preventDefault()
|
|
235
|
+
focusItem(props.model.length - 1)
|
|
236
|
+
break
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function onPointerDown(event: PointerEvent): void {
|
|
241
|
+
if (props.hideOnClickOutside && !root.value?.contains(event.target as Node))
|
|
242
|
+
close()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
onMounted(() => {
|
|
246
|
+
document.addEventListener('pointerdown', onPointerDown)
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
onBeforeUnmount(() => {
|
|
250
|
+
document.removeEventListener('pointerdown', onPointerDown)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
// Danh sách đổi thì mảng ref cũ còn giữ node đã gỡ — dọn để phím mũi tên không
|
|
254
|
+
// focus vào phần tử mồ côi.
|
|
255
|
+
watch(() => props.model, () => {
|
|
256
|
+
itemRefs.value = []
|
|
257
|
+
focusIndex.value = -1
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
const rootStyle = computed<CSSProperties>(() => ({
|
|
261
|
+
width: `${TRIGGER_SIZE}px`,
|
|
262
|
+
height: `${TRIGGER_SIZE}px`,
|
|
263
|
+
}))
|
|
264
|
+
</script>
|
|
265
|
+
|
|
266
|
+
<template>
|
|
267
|
+
<div
|
|
268
|
+
ref="root"
|
|
269
|
+
:class="cn('relative', props.class)"
|
|
270
|
+
:style="rootStyle"
|
|
271
|
+
@keydown="onKeydown"
|
|
272
|
+
>
|
|
273
|
+
<!--
|
|
274
|
+
Lớp phủ nằm trong chính component chứ không Teleport ra body: đặt z thấp
|
|
275
|
+
hơn nút và các mục là đủ, mà lại không phải lo portal khi component nằm
|
|
276
|
+
trong dialog.
|
|
277
|
+
-->
|
|
278
|
+
<div
|
|
279
|
+
v-if="mask"
|
|
280
|
+
class="fixed inset-0 -z-10 bg-foreground/40 backdrop-blur-[2px] transition-opacity duration-(--duration-base) motion-reduce:transition-none"
|
|
281
|
+
:class="visible ? 'opacity-100' : 'pointer-events-none opacity-0'"
|
|
282
|
+
aria-hidden="true"
|
|
283
|
+
/>
|
|
284
|
+
|
|
285
|
+
<ul
|
|
286
|
+
:id="listId"
|
|
287
|
+
class="pointer-events-none absolute inset-0 m-0 list-none p-0"
|
|
288
|
+
role="menu"
|
|
289
|
+
:aria-label="ariaLabel"
|
|
290
|
+
>
|
|
291
|
+
<li
|
|
292
|
+
v-for="(item, index) in model"
|
|
293
|
+
:key="index"
|
|
294
|
+
class="absolute transition-[transform,opacity] duration-(--duration-base) ease-out motion-reduce:transition-none"
|
|
295
|
+
:class="visible ? 'opacity-100' : 'pointer-events-none opacity-0'"
|
|
296
|
+
:style="[{ left: `${CENTER}px`, top: `${CENTER}px` }, styleOf(index)]"
|
|
297
|
+
role="none"
|
|
298
|
+
>
|
|
299
|
+
<Tooltip v-if="item.label" :delay-duration="200">
|
|
300
|
+
<TooltipTrigger as-child>
|
|
301
|
+
<Button
|
|
302
|
+
:ref="el => { if (el) itemRefs[index] = (el as { $el: HTMLElement }).$el }"
|
|
303
|
+
:as="item.url ? 'a' : 'button'"
|
|
304
|
+
:href="item.url"
|
|
305
|
+
:target="item.target"
|
|
306
|
+
variant="secondary"
|
|
307
|
+
size="icon"
|
|
308
|
+
role="menuitem"
|
|
309
|
+
class="pointer-events-auto rounded-full shadow-md"
|
|
310
|
+
:tabindex="visible ? 0 : -1"
|
|
311
|
+
:disabled="item.disabled"
|
|
312
|
+
:aria-label="item.label"
|
|
313
|
+
@click="run(item)"
|
|
314
|
+
>
|
|
315
|
+
<component :is="item.icon" v-if="item.icon" />
|
|
316
|
+
</Button>
|
|
317
|
+
</TooltipTrigger>
|
|
318
|
+
<TooltipContent :side="type === 'linear' && (direction === 'up' || direction === 'down') ? 'right' : 'top'">
|
|
319
|
+
{{ item.label }}
|
|
320
|
+
</TooltipContent>
|
|
321
|
+
</Tooltip>
|
|
322
|
+
|
|
323
|
+
<Button
|
|
324
|
+
v-else
|
|
325
|
+
:ref="el => { if (el) itemRefs[index] = (el as { $el: HTMLElement }).$el }"
|
|
326
|
+
:as="item.url ? 'a' : 'button'"
|
|
327
|
+
:href="item.url"
|
|
328
|
+
:target="item.target"
|
|
329
|
+
variant="secondary"
|
|
330
|
+
size="icon"
|
|
331
|
+
role="menuitem"
|
|
332
|
+
class="pointer-events-auto rounded-full shadow-md"
|
|
333
|
+
:tabindex="visible ? 0 : -1"
|
|
334
|
+
:disabled="item.disabled"
|
|
335
|
+
@click="run(item)"
|
|
336
|
+
>
|
|
337
|
+
<component :is="item.icon" v-if="item.icon" />
|
|
338
|
+
</Button>
|
|
339
|
+
</li>
|
|
340
|
+
</ul>
|
|
341
|
+
|
|
342
|
+
<Button
|
|
343
|
+
data-speed-dial-trigger
|
|
344
|
+
size="icon-lg"
|
|
345
|
+
class="relative rounded-full shadow-lg"
|
|
346
|
+
:disabled="disabled"
|
|
347
|
+
:aria-label="ariaLabel"
|
|
348
|
+
:aria-expanded="visible"
|
|
349
|
+
:aria-controls="listId"
|
|
350
|
+
aria-haspopup="menu"
|
|
351
|
+
@click="toggle"
|
|
352
|
+
>
|
|
353
|
+
<slot name="icon" :visible="visible">
|
|
354
|
+
<!-- Dấu cộng xoay 135° thành dấu nhân — cùng một icon, không đổi thẻ. -->
|
|
355
|
+
<Plus
|
|
356
|
+
class="transition-transform duration-(--duration-base) motion-reduce:transition-none"
|
|
357
|
+
:class="visible && 'rotate-[135deg]'"
|
|
358
|
+
/>
|
|
359
|
+
</slot>
|
|
360
|
+
</Button>
|
|
361
|
+
</div>
|
|
362
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MenuActionItem } from '../../lib/menu'
|
|
2
|
+
|
|
3
|
+
export { default as SpeedDial } from './SpeedDial.vue'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SpeedDial chỉ nhận mục **phẳng**: không đường kẻ, không submenu. Mỗi mục là
|
|
7
|
+
* một nút tròn nằm trên quỹ đạo quanh nút chính, không có chỗ cho cấp hai.
|
|
8
|
+
*/
|
|
9
|
+
export type SpeedDialItem = MenuActionItem
|
|
10
|
+
|
|
11
|
+
/** Kiểu quỹ đạo các mục bung ra. */
|
|
12
|
+
export type SpeedDialType = 'linear' | 'circle' | 'semi-circle' | 'quarter-circle'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Hướng bung.
|
|
16
|
+
*
|
|
17
|
+
* `linear` và `semi-circle` dùng bốn hướng thẳng; `quarter-circle` dùng bốn
|
|
18
|
+
* hướng chéo. `circle` bỏ qua prop này vì nó bung kín 360°.
|
|
19
|
+
*/
|
|
20
|
+
export type SpeedDialDirection
|
|
21
|
+
= | 'up'
|
|
22
|
+
| 'down'
|
|
23
|
+
| 'left'
|
|
24
|
+
| 'right'
|
|
25
|
+
| 'up-left'
|
|
26
|
+
| 'up-right'
|
|
27
|
+
| 'down-left'
|
|
28
|
+
| 'down-right'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { useI18n } from '../../composables/useI18n'
|
|
5
|
+
import { LoaderCircle } from '../../lib/icons'
|
|
6
|
+
import { cn } from '../../lib/utils'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<ClassProps & { label?: string }>()
|
|
9
|
+
|
|
10
|
+
const { t } = useI18n()
|
|
11
|
+
|
|
12
|
+
// Bỏ qua `withDefaults`: chuỗi mặc định phải đi qua `t()` mới đổi theo ngôn ngữ.
|
|
13
|
+
const label = computed(() => props.label ?? t('ui.spinner.label'))
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<span role="status" :aria-label="label" :class="cn('inline-flex items-center', props.class)">
|
|
18
|
+
<LoaderCircle class="size-4 animate-spin" aria-hidden="true" />
|
|
19
|
+
</span>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Spinner } from './Spinner.vue'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { SplitterGroupEmits, SplitterGroupProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { SplitterGroup, useForwardPropsEmits } from 'reka-ui'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { cn } from '../../lib/utils'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<SplitterGroupProps & ClassProps>()
|
|
9
|
+
const emits = defineEmits<SplitterGroupEmits>()
|
|
10
|
+
|
|
11
|
+
const delegated = computed(() => {
|
|
12
|
+
const { class: _class, ...rest } = props
|
|
13
|
+
return rest
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const forwarded = useForwardPropsEmits(delegated, emits)
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<!-- Chiều xếp do reka đặt inline theo `direction`; ở đây chỉ lo kích thước. -->
|
|
21
|
+
<SplitterGroup v-bind="forwarded" :class="cn('flex h-full w-full', props.class)">
|
|
22
|
+
<slot />
|
|
23
|
+
</SplitterGroup>
|
|
24
|
+
</template>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { SplitterResizeHandleEmits, SplitterResizeHandleProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { SplitterResizeHandle, useForwardPropsEmits } from 'reka-ui'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { GripVertical } from '../../lib/icons'
|
|
7
|
+
import { cn } from '../../lib/utils'
|
|
8
|
+
|
|
9
|
+
const props = defineProps<SplitterResizeHandleProps & ClassProps & {
|
|
10
|
+
/** Hiện ô chấm ở giữa. Bật khi tay cầm nằm giữa hai mặt phẳng cùng màu. */
|
|
11
|
+
grip?: boolean
|
|
12
|
+
}>()
|
|
13
|
+
const emits = defineEmits<SplitterResizeHandleEmits>()
|
|
14
|
+
|
|
15
|
+
const delegated = computed(() => {
|
|
16
|
+
const { class: _class, grip: _grip, ...rest } = props
|
|
17
|
+
return rest
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const forwarded = useForwardPropsEmits(delegated, emits)
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<SplitterResizeHandle
|
|
25
|
+
v-bind="forwarded"
|
|
26
|
+
:class="cn(
|
|
27
|
+
'relative flex w-px shrink-0 items-center justify-center bg-border',
|
|
28
|
+
// Đường kẻ dày 1px, nhưng vùng bắt chuột dày 9px — kéo được một đường
|
|
29
|
+
// tóc là bài kiểm tra độ chính xác, không phải một giao diện.
|
|
30
|
+
'after:absolute after:inset-y-0 after:left-1/2 after:w-[9px] after:-translate-x-1/2',
|
|
31
|
+
// Vòng focus kiểu ô lịch, không phải `focusRing`: tay cầm không có viền
|
|
32
|
+
// để đổi màu, mà `focusRing` thì làm đúng việc đó.
|
|
33
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
34
|
+
'data-[state=drag]:bg-ring',
|
|
35
|
+
// Nhóm dọc: kẻ nằm ngang, mọi thứ xoay 90°.
|
|
36
|
+
'data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full',
|
|
37
|
+
'data-[orientation=vertical]:after:inset-x-0 data-[orientation=vertical]:after:left-0',
|
|
38
|
+
'data-[orientation=vertical]:after:h-[9px] data-[orientation=vertical]:after:w-full',
|
|
39
|
+
'data-[orientation=vertical]:after:-translate-y-1/2 data-[orientation=vertical]:after:translate-x-0',
|
|
40
|
+
props.class,
|
|
41
|
+
)"
|
|
42
|
+
>
|
|
43
|
+
<span
|
|
44
|
+
v-if="props.grip"
|
|
45
|
+
class="z-(--z-raised) grid h-4 w-3 place-items-center rounded-xs border border-border bg-border text-muted-foreground data-[orientation=vertical]:rotate-90"
|
|
46
|
+
>
|
|
47
|
+
<GripVertical class="size-2.5" />
|
|
48
|
+
</span>
|
|
49
|
+
<slot />
|
|
50
|
+
</SplitterResizeHandle>
|
|
51
|
+
</template>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { SplitterPanelEmits, SplitterPanelProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { SplitterPanel, useForwardPropsEmits } from 'reka-ui'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { cn } from '../../lib/utils'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<SplitterPanelProps & ClassProps>()
|
|
9
|
+
const emits = defineEmits<SplitterPanelEmits>()
|
|
10
|
+
|
|
11
|
+
const delegated = computed(() => {
|
|
12
|
+
const { class: _class, ...rest } = props
|
|
13
|
+
return rest
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const forwarded = useForwardPropsEmits(delegated, emits)
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<!--
|
|
21
|
+
`overflow-hidden` là bắt buộc, không phải cho đẹp.
|
|
22
|
+
|
|
23
|
+
reka đặt kích thước panel bằng `flex-grow`, nên nội dung rộng hơn phần đã
|
|
24
|
+
chia sẽ **đẩy panel giãn ra** thay vì bị cắt — kéo tay cầm về một phía là
|
|
25
|
+
panel bên kia không chịu co lại, trông như thanh chia bị kẹt.
|
|
26
|
+
-->
|
|
27
|
+
<SplitterPanel v-bind="forwarded" :class="cn('overflow-hidden', props.class)">
|
|
28
|
+
<slot />
|
|
29
|
+
</SplitterPanel>
|
|
30
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vùng chia được, kéo tay cầm để đổi tỉ lệ.
|
|
3
|
+
*
|
|
4
|
+
* PrimeVue gọi là `Splitter`, shadcn gọi là `Resizable`. Lấy tên PrimeVue vì
|
|
5
|
+
* danh mục component của repo xếp theo phân loại bên đó.
|
|
6
|
+
*
|
|
7
|
+
* Tay cầm phải khai TƯỜNG MINH giữa hai panel — reka không tự chèn. Đổi lại,
|
|
8
|
+
* `Splitter` lồng trong `SplitterPanel` là ra lưới hai chiều mà không cần API
|
|
9
|
+
* riêng nào.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export { default as Splitter } from './Splitter.vue'
|
|
13
|
+
export { default as SplitterHandle } from './SplitterHandle.vue'
|
|
14
|
+
export { default as SplitterPanel } from './SplitterPanel.vue'
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { StepperRootEmits, StepperRootProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { StepperRoot, useForwardPropsEmits } from 'reka-ui'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { cn } from '../../lib/utils'
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(
|
|
9
|
+
defineProps<StepperRootProps & ClassProps>(),
|
|
10
|
+
{ orientation: 'horizontal' },
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
const emits = defineEmits<StepperRootEmits>()
|
|
14
|
+
|
|
15
|
+
const delegated = computed(() => {
|
|
16
|
+
const { class: _class, ...rest } = props
|
|
17
|
+
return rest
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const forwarded = useForwardPropsEmits(delegated, emits)
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<StepperRoot
|
|
25
|
+
v-bind="forwarded"
|
|
26
|
+
:class="cn(
|
|
27
|
+
'flex w-full',
|
|
28
|
+
props.orientation === 'vertical' ? 'flex-col' : 'items-start',
|
|
29
|
+
props.class,
|
|
30
|
+
)"
|
|
31
|
+
>
|
|
32
|
+
<slot />
|
|
33
|
+
</StepperRoot>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { StepperItemProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import {
|
|
5
|
+
StepperDescription,
|
|
6
|
+
StepperItem,
|
|
7
|
+
StepperSeparator,
|
|
8
|
+
StepperTitle,
|
|
9
|
+
StepperTrigger,
|
|
10
|
+
} from 'reka-ui'
|
|
11
|
+
import { computed } from 'vue'
|
|
12
|
+
import { Check } from '../../lib/icons'
|
|
13
|
+
import { cn } from '../../lib/utils'
|
|
14
|
+
|
|
15
|
+
const props = withDefaults(
|
|
16
|
+
defineProps<StepperItemProps & ClassProps & {
|
|
17
|
+
title?: string
|
|
18
|
+
description?: string
|
|
19
|
+
/** Bước cuối không vẽ đoạn kẻ nối. */
|
|
20
|
+
last?: boolean
|
|
21
|
+
orientation?: 'horizontal' | 'vertical'
|
|
22
|
+
}>(),
|
|
23
|
+
{ last: false, orientation: 'horizontal' },
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
const delegated = computed(() => {
|
|
27
|
+
const {
|
|
28
|
+
class: _class,
|
|
29
|
+
title: _title,
|
|
30
|
+
description: _description,
|
|
31
|
+
last: _last,
|
|
32
|
+
orientation: _orientation,
|
|
33
|
+
...rest
|
|
34
|
+
} = props
|
|
35
|
+
return rest
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Bước đã xong hiện dấu tick thay cho số.
|
|
40
|
+
*
|
|
41
|
+
* Reka phơi `data-state` là `completed` / `active` / `inactive`. Đổi số thành
|
|
42
|
+
* tick là cách duy nhất phân biệt được "đã xong" với "đang làm" mà không cần
|
|
43
|
+
* đến màu — người không phân biệt được màu vẫn đọc ra.
|
|
44
|
+
*/
|
|
45
|
+
const indicator = cn(
|
|
46
|
+
'grid size-8 shrink-0 place-items-center rounded-full border text-sm font-medium transition-colors',
|
|
47
|
+
'group-data-[state=inactive]/step:border-border group-data-[state=inactive]/step:text-muted-foreground',
|
|
48
|
+
'group-data-[state=active]/step:border-primary group-data-[state=active]/step:bg-primary group-data-[state=active]/step:text-primary-foreground',
|
|
49
|
+
'group-data-[state=completed]/step:border-primary group-data-[state=completed]/step:bg-primary group-data-[state=completed]/step:text-primary-foreground',
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const separator = cn(
|
|
53
|
+
'bg-border transition-colors',
|
|
54
|
+
'group-data-[state=completed]/step:bg-primary',
|
|
55
|
+
)
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<StepperItem
|
|
60
|
+
v-bind="delegated"
|
|
61
|
+
:class="cn(
|
|
62
|
+
'group/step relative flex',
|
|
63
|
+
props.orientation === 'vertical' ? 'gap-3 pb-6 last:pb-0' : 'flex-1 flex-col items-center gap-2 last:flex-none',
|
|
64
|
+
props.class,
|
|
65
|
+
)"
|
|
66
|
+
>
|
|
67
|
+
<!--
|
|
68
|
+
Ngang: đoạn kẻ nằm NGANG HÀNG với vòng tròn, không nằm dưới nhãn — nên nó
|
|
69
|
+
phải tuyệt đối, vì nhãn bên dưới có thể dài ngắn khác nhau và sẽ đẩy đoạn
|
|
70
|
+
kẻ lệch nếu để nó chảy theo dòng.
|
|
71
|
+
-->
|
|
72
|
+
<StepperSeparator
|
|
73
|
+
v-if="!props.last"
|
|
74
|
+
:class="props.orientation === 'vertical'
|
|
75
|
+
? cn('absolute left-4 top-9 bottom-2 w-px -translate-x-1/2', separator)
|
|
76
|
+
: cn('absolute left-[calc(50%+1.5rem)] right-[calc(-50%+1.5rem)] top-4 h-px -translate-y-1/2', separator)"
|
|
77
|
+
/>
|
|
78
|
+
|
|
79
|
+
<StepperTrigger
|
|
80
|
+
:class="cn(
|
|
81
|
+
'relative z-(--z-raised) flex rounded-full outline-none',
|
|
82
|
+
'focus-visible:ring-2 focus-visible:ring-ring',
|
|
83
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
84
|
+
)"
|
|
85
|
+
>
|
|
86
|
+
<span :class="indicator">
|
|
87
|
+
<Check v-if="props.step !== undefined" class="hidden size-4 group-data-[state=completed]/step:block" />
|
|
88
|
+
<span class="group-data-[state=completed]/step:hidden"><slot name="indicator">{{ props.step }}</slot></span>
|
|
89
|
+
</span>
|
|
90
|
+
</StepperTrigger>
|
|
91
|
+
|
|
92
|
+
<div
|
|
93
|
+
:class="cn(
|
|
94
|
+
'flex min-w-0 flex-col gap-0.5',
|
|
95
|
+
props.orientation === 'vertical' ? 'pt-1' : 'items-center text-center',
|
|
96
|
+
)"
|
|
97
|
+
>
|
|
98
|
+
<StepperTitle class="text-sm font-medium group-data-[state=inactive]/step:text-muted-foreground">
|
|
99
|
+
<slot name="title">
|
|
100
|
+
{{ props.title }}
|
|
101
|
+
</slot>
|
|
102
|
+
</StepperTitle>
|
|
103
|
+
<StepperDescription
|
|
104
|
+
v-if="props.description || $slots.description"
|
|
105
|
+
class="text-xs leading-relaxed text-muted-foreground"
|
|
106
|
+
>
|
|
107
|
+
<slot name="description">
|
|
108
|
+
{{ props.description }}
|
|
109
|
+
</slot>
|
|
110
|
+
</StepperDescription>
|
|
111
|
+
</div>
|
|
112
|
+
</StepperItem>
|
|
113
|
+
</template>
|