@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,52 @@
|
|
|
1
|
+
import type { Component, InjectionKey, Ref } from 'vue'
|
|
2
|
+
import type { ChartSeriesConfig, ChartBaseProps as SharedChartBaseProps } from '../../variants/lib/chart/types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bộ kiểu dùng chung nằm ở `src/variants/lib/chart/types` — nó mô tả dữ
|
|
6
|
+
* liệu và tuỳ chọn hiển thị, không dính framework nào. Ở đây chỉ còn hai thứ
|
|
7
|
+
* CÓ dính: kiểu của `icon` trong config, và context của `ChartContainer`.
|
|
8
|
+
*/
|
|
9
|
+
export type {
|
|
10
|
+
ChartCurve,
|
|
11
|
+
ChartLabelKey,
|
|
12
|
+
ChartSeriesConfig,
|
|
13
|
+
ChartTickFormatter,
|
|
14
|
+
ChartTooltipOptions,
|
|
15
|
+
ChartTooltipRow,
|
|
16
|
+
ChartValueFormatter,
|
|
17
|
+
} from '../../variants/lib/chart/types'
|
|
18
|
+
|
|
19
|
+
/** `icon` thu hẹp lại thành component của Vue. */
|
|
20
|
+
export interface ChartSeries extends Omit<ChartSeriesConfig, 'icon'> {
|
|
21
|
+
icon?: Component
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Khoá của object là khoá trong dữ liệu. */
|
|
25
|
+
export type ChartConfig = Record<string, ChartSeries>
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Prop dùng chung của mọi biểu đồ trục XY, đã chốt `config` về bản của Vue.
|
|
29
|
+
*
|
|
30
|
+
* `ChartBaseProps` ở tầng chung generic theo kiểu config đúng vì lý do này:
|
|
31
|
+
* `icon` là `unknown` ở đó và là `Component` ở đây, nên không generic thì
|
|
32
|
+
* `ChartConfig` của hai tầng không gán được cho nhau.
|
|
33
|
+
*/
|
|
34
|
+
export type ChartBaseProps<T extends Record<string, unknown>>
|
|
35
|
+
= SharedChartBaseProps<T, ChartConfig>
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Config được ChartContainer đưa xuống cho con.
|
|
39
|
+
*
|
|
40
|
+
* `colorOf` trả về chuỗi `var(--color-<key>)` chứ không phải màu đã resolve:
|
|
41
|
+
* biến đó do chính ChartContainer khai trên thẻ bọc, nên đổi theme hay đổi bảng
|
|
42
|
+
* màu là SVG tự đổi theo cascade, không cần đọc lại bằng JS.
|
|
43
|
+
*/
|
|
44
|
+
export interface ChartContext {
|
|
45
|
+
config: ChartConfig
|
|
46
|
+
colorOf: (key: string) => string
|
|
47
|
+
labelOf: (key: string) => string
|
|
48
|
+
/** Con trỏ đang ở trong khung biểu đồ. Dùng để nhả tooltip đã ghim ra. */
|
|
49
|
+
hovered: Ref<boolean>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const CHART_CONTEXT_KEY: InjectionKey<ChartContext> = Symbol('woodblock-chart')
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { Ref } from 'vue'
|
|
2
|
+
import type { ChartBaseProps, ChartCurve } from './types'
|
|
3
|
+
import { CurveType } from '@unovis/ts'
|
|
4
|
+
import { computed, onMounted, ref } from 'vue'
|
|
5
|
+
|
|
6
|
+
/** Ba lựa chọn rút gọn -> tên đầy đủ trong enum của Unovis. */
|
|
7
|
+
const CURVE_MAP: Record<ChartCurve, CurveType> = {
|
|
8
|
+
monotone: CurveType.MonotoneX,
|
|
9
|
+
natural: CurveType.Natural,
|
|
10
|
+
linear: CurveType.Linear,
|
|
11
|
+
step: CurveType.StepAfter,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function toCurveType(curve: ChartCurve): CurveType {
|
|
15
|
+
return CURVE_MAP[curve]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function toNumber(value: unknown): number {
|
|
19
|
+
const n = Number(value)
|
|
20
|
+
return Number.isFinite(n) ? n : 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Số nhãn tối đa trên trục hoành trước khi bắt đầu bỏ bớt. */
|
|
24
|
+
const MAX_X_TICKS = 12
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Nhận GETTER chứ không nhận giá trị: hook đọc chúng bên trong `computed`, nên
|
|
28
|
+
* truyền giá trị trần sẽ đóng băng ở lần gọi đầu và đổi `stacked` lúc chạy
|
|
29
|
+
* không có tác dụng.
|
|
30
|
+
*/
|
|
31
|
+
export interface UseChartSeriesOptions {
|
|
32
|
+
stacked?: () => boolean
|
|
33
|
+
/** Chuẩn hoá mỗi cột về 100% — dùng cho biểu đồ "stacked expanded". */
|
|
34
|
+
expand?: () => boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Chuẩn bị dữ liệu vẽ dùng chung cho Area và Bar.
|
|
39
|
+
*
|
|
40
|
+
* Xếp chồng được tính SẴN THÀNH CỘT `__base_<key>` / `__value_<key>` / `__top_<key>` thay vì
|
|
41
|
+
* truyền mảng accessor `y` cho Unovis. Lý do: truyền mảng thì mọi lớp nằm chung
|
|
42
|
+
* một component nên chỉ đặt được MỘT `fill`, tức mất khả năng cho từng chuỗi
|
|
43
|
+
* một gradient riêng — mà gradient chính là thứ gallery area của shadcn dựa vào.
|
|
44
|
+
* Tách ra từng lớp rồi neo bằng `baseline` thì vừa chồng đúng, vừa tô riêng được.
|
|
45
|
+
*/
|
|
46
|
+
export function useChartSeries<T extends Record<string, unknown>>(
|
|
47
|
+
props: ChartBaseProps<T>,
|
|
48
|
+
options: UseChartSeriesOptions = {},
|
|
49
|
+
): {
|
|
50
|
+
categories: Ref<string[]>
|
|
51
|
+
plotData: Ref<Record<string, unknown>[]>
|
|
52
|
+
xTicks: Ref<number[]>
|
|
53
|
+
isMounted: Ref<boolean>
|
|
54
|
+
valueOf: (key: string) => (d: Record<string, unknown>) => number
|
|
55
|
+
topOf: (key: string) => (d: Record<string, unknown>) => number
|
|
56
|
+
baseOf: (key: string) => (d: Record<string, unknown>) => number
|
|
57
|
+
} {
|
|
58
|
+
// Bỏ trống `categories` thì lấy đúng các khoá trong `config`, giữ nguyên thứ tự
|
|
59
|
+
// khai báo — nhờ vậy màu, legend và thứ tự chồng lớp luôn khớp nhau.
|
|
60
|
+
const categories = computed(() => props.categories ?? Object.keys(props.config))
|
|
61
|
+
|
|
62
|
+
const plotData = computed<Record<string, unknown>[]>(() => {
|
|
63
|
+
const stacked = options.stacked?.() ?? false
|
|
64
|
+
const expand = options.expand?.() ?? false
|
|
65
|
+
|
|
66
|
+
return props.data.map((row, rowIndex) => {
|
|
67
|
+
// `__i` là chỉ số hàng, ghi sẵn vào dữ liệu.
|
|
68
|
+
// Cần vì `VisXYLabels` gọi accessor `x` KHÔNG truyền chỉ số
|
|
69
|
+
// (`getNumber(data, config.x)`), khác với Area/Line/Bar. Dùng
|
|
70
|
+
// `(_d, i) => i` ở đó sẽ ra NaN và Unovis lặng lẽ loại hết nhãn khỏi
|
|
71
|
+
// vùng vẽ — nhóm `xy-labels` render ra rỗng, không một cảnh báo nào.
|
|
72
|
+
const out: Record<string, unknown> = { ...row, __i: rowIndex }
|
|
73
|
+
|
|
74
|
+
if (!stacked) {
|
|
75
|
+
categories.value.forEach((key) => {
|
|
76
|
+
const raw = toNumber(row[key])
|
|
77
|
+
out[`__base_${key}`] = 0
|
|
78
|
+
out[`__value_${key}`] = raw
|
|
79
|
+
out[`__top_${key}`] = raw
|
|
80
|
+
})
|
|
81
|
+
return out
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const total = expand
|
|
85
|
+
? categories.value.reduce((sum, key) => sum + toNumber(row[key]), 0)
|
|
86
|
+
: 1
|
|
87
|
+
|
|
88
|
+
let acc = 0
|
|
89
|
+
categories.value.forEach((key) => {
|
|
90
|
+
const raw = toNumber(row[key])
|
|
91
|
+
const value = expand ? (total === 0 ? 0 : raw / total) : raw
|
|
92
|
+
out[`__base_${key}`] = acc
|
|
93
|
+
out[`__value_${key}`] = value
|
|
94
|
+
acc += value
|
|
95
|
+
out[`__top_${key}`] = acc
|
|
96
|
+
})
|
|
97
|
+
return out
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Trục hoành chạy theo CHỈ SỐ, nên phải tự khai mốc chia.
|
|
103
|
+
* Để d3 tự chọn thì nó ra mốc lẻ như 0,5 hay 1,5 — mà chỉ số lẻ không ứng với
|
|
104
|
+
* điểm dữ liệu nào, nhãn hiện ra là chuỗi rỗng hoặc lặp lại.
|
|
105
|
+
*/
|
|
106
|
+
const xTicks = computed(() => {
|
|
107
|
+
const n = plotData.value.length
|
|
108
|
+
if (n === 0)
|
|
109
|
+
return []
|
|
110
|
+
const stride = Math.max(1, Math.ceil(n / MAX_X_TICKS))
|
|
111
|
+
const ticks: number[] = []
|
|
112
|
+
for (let i = 0; i < n; i += stride)
|
|
113
|
+
ticks.push(i)
|
|
114
|
+
return ticks
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
// Trước khi mount, `height: 100%` tính theo một thẻ cha cao 0px nên biểu đồ ra
|
|
118
|
+
// 0px rồi không tự vẽ lại. Chờ mount xong mới trao chiều cao thật.
|
|
119
|
+
const isMounted = ref(false)
|
|
120
|
+
onMounted(() => {
|
|
121
|
+
isMounted.value = true
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* `valueOf` là ĐỘ CAO tính từ baseline, không phải toạ độ đỉnh.
|
|
126
|
+
*
|
|
127
|
+
* `getStackedData` của Unovis chồng theo kiểu `[base, base + y]`, nên đưa toạ
|
|
128
|
+
* độ đỉnh vào `y` sẽ cộng dồn thêm một lần nữa: lớp thứ hai vọt lên
|
|
129
|
+
* `base + top` thay vì dừng ở `top`. Lỗi này KHÔNG báo gì cả, biểu đồ vẫn vẽ
|
|
130
|
+
* ra một hình hợp lý — chỉ sai số. Dùng `topOf` cho thứ vẽ theo toạ độ tuyệt
|
|
131
|
+
* đối (đường kẻ), `valueOf` + `baseOf` cho thứ có xếp chồng (vùng).
|
|
132
|
+
*/
|
|
133
|
+
const valueOf = (key: string) => (d: Record<string, unknown>): number => toNumber(d[`__value_${key}`])
|
|
134
|
+
const topOf = (key: string) => (d: Record<string, unknown>): number => toNumber(d[`__top_${key}`])
|
|
135
|
+
const baseOf = (key: string) => (d: Record<string, unknown>): number => toNumber(d[`__base_${key}`])
|
|
136
|
+
|
|
137
|
+
return { categories, plotData, xTicks, isMounted, valueOf, topOf, baseOf }
|
|
138
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { CheckboxRootEmits, CheckboxRootProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import type { CheckboxVariants } from './index'
|
|
5
|
+
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'reka-ui'
|
|
6
|
+
import { computed } from 'vue'
|
|
7
|
+
import { Check, Minus } from '../../lib/icons'
|
|
8
|
+
import { cn } from '../../lib/utils'
|
|
9
|
+
import { checkboxVariants, injectCheckboxGroupContext } from './index'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(
|
|
12
|
+
defineProps<CheckboxRootProps & ClassProps & {
|
|
13
|
+
size?: CheckboxVariants['size']
|
|
14
|
+
tone?: CheckboxVariants['tone']
|
|
15
|
+
/** Viền đỏ + `aria-invalid`. Trong `FormField` thì không cần khai — xem dưới. */
|
|
16
|
+
invalid?: boolean
|
|
17
|
+
}>(),
|
|
18
|
+
// Vue mặc định gán `false` cho prop kiểu Boolean khi không khai. Ở đây phải là
|
|
19
|
+
// `undefined` mới phân biệt được "không khai" với "khai là false" — `false` sẽ
|
|
20
|
+
// chặn mất cả nhóm lẫn `aria-invalid` trong chuỗi `??` bên dưới.
|
|
21
|
+
{ invalid: undefined },
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
const emits = defineEmits<CheckboxRootEmits>()
|
|
25
|
+
|
|
26
|
+
defineSlots<{
|
|
27
|
+
/** Thay glyph trong ô. Nhận trạng thái để tự đổi hình khi indeterminate. */
|
|
28
|
+
indicator?: (props: { state: boolean | 'indeterminate' }) => unknown
|
|
29
|
+
}>()
|
|
30
|
+
|
|
31
|
+
// Nằm trong CheckboxGroup thì kế thừa cỡ, kiểu tô và trạng thái lỗi của nhóm.
|
|
32
|
+
// Prop khai thẳng trên từng ô vẫn thắng.
|
|
33
|
+
const group = injectCheckboxGroupContext(null)
|
|
34
|
+
|
|
35
|
+
const size = computed(() => props.size ?? group?.size.value)
|
|
36
|
+
const tone = computed(() => props.tone ?? group?.tone.value)
|
|
37
|
+
const invalid = computed(() => props.invalid ?? group?.invalid.value)
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* `v-bind="field"` của FormField truyền `aria-invalid` xuống dưới dạng **attr**,
|
|
41
|
+
* nên đọc luôn ở đây: control trong form tự đỏ viền, không phải nối tay `invalid`.
|
|
42
|
+
*
|
|
43
|
+
* Phải gọi trong template chứ không bọc thành computed: `useAttrs()` KHÔNG phản
|
|
44
|
+
* ứng — computed đọc nó sẽ nhớ đúng giá trị lúc render đầu rồi đứng im, nên ô
|
|
45
|
+
* vẫn viền xám sau khi form báo lỗi. Đọc `$attrs` ngay lúc render thì mỗi lần
|
|
46
|
+
* attr đổi, component render lại và giá trị tính lại theo.
|
|
47
|
+
*/
|
|
48
|
+
function resolveInvalid(attrs: Record<string, unknown>): boolean {
|
|
49
|
+
return invalid.value ?? (attrs['aria-invalid'] === true || attrs['aria-invalid'] === 'true')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Tách `class` và ba prop trình bày ra khỏi props trước khi forward: chúng
|
|
53
|
+
// không thuộc CheckboxRoot, để lọt xuống sẽ thành thuộc tính lạ trên DOM.
|
|
54
|
+
const delegated = computed(() => {
|
|
55
|
+
const { class: _class, size: _size, tone: _tone, invalid: _invalid, ...rest } = props
|
|
56
|
+
return rest
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const forwarded = useForwardPropsEmits(delegated, emits)
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<template>
|
|
63
|
+
<CheckboxRoot
|
|
64
|
+
v-slot="{ state }"
|
|
65
|
+
v-bind="forwarded"
|
|
66
|
+
:aria-invalid="resolveInvalid($attrs) || undefined"
|
|
67
|
+
:data-invalid="resolveInvalid($attrs) || undefined"
|
|
68
|
+
:class="cn(checkboxVariants({ size, tone, invalid: resolveInvalid($attrs) }), props.class)"
|
|
69
|
+
>
|
|
70
|
+
<!-- Indicator chỉ tồn tại khi đã chọn, nên animate-in chạy đúng một lần lúc
|
|
71
|
+
nó xuất hiện — không cần transition-group hay forceMount. -->
|
|
72
|
+
<CheckboxIndicator
|
|
73
|
+
class="flex size-full animate-in items-center justify-center text-current zoom-in-50 duration-(--duration-fast) motion-reduce:animate-none"
|
|
74
|
+
>
|
|
75
|
+
<slot name="indicator" :state="state">
|
|
76
|
+
<Minus v-if="state === 'indeterminate'" />
|
|
77
|
+
<Check v-else />
|
|
78
|
+
</slot>
|
|
79
|
+
</CheckboxIndicator>
|
|
80
|
+
</CheckboxRoot>
|
|
81
|
+
</template>
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { CheckboxRootEmits, CheckboxRootProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import type { CheckboxVariants } from './index'
|
|
5
|
+
import { useForwardPropsEmits } from 'reka-ui'
|
|
6
|
+
import { computed, useId } from 'vue'
|
|
7
|
+
import { cn } from '../../lib/utils'
|
|
8
|
+
import Label from '../label/Label.vue'
|
|
9
|
+
import Checkbox from './Checkbox.vue'
|
|
10
|
+
import { injectCheckboxGroupContext } from './index'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Attr rơi tự do phải đi xuống Ô, không đọng lại ở div bọc ngoài: `v-bind="field"`
|
|
14
|
+
* của FormField truyền `aria-invalid` và `aria-describedby` dưới dạng attr, mà
|
|
15
|
+
* cả hai đều tả cái ô chứ không tả cái hàng.
|
|
16
|
+
*/
|
|
17
|
+
defineOptions({ inheritAttrs: false })
|
|
18
|
+
|
|
19
|
+
const props = withDefaults(
|
|
20
|
+
defineProps<CheckboxRootProps & ClassProps & {
|
|
21
|
+
size?: CheckboxVariants['size']
|
|
22
|
+
tone?: CheckboxVariants['tone']
|
|
23
|
+
invalid?: boolean
|
|
24
|
+
/** Nhãn. Cần đánh dấu chữ (ví dụ chèn link) thì dùng slot mặc định. */
|
|
25
|
+
label?: string
|
|
26
|
+
/** Dòng phụ bên dưới nhãn. */
|
|
27
|
+
description?: string
|
|
28
|
+
}>(),
|
|
29
|
+
// Vue mặc định gán `false` cho prop kiểu Boolean khi không khai. Ở đây phải là
|
|
30
|
+
// `undefined` mới phân biệt được "không khai" với "khai là false" — `false` sẽ
|
|
31
|
+
// chặn mất cả nhóm lẫn `aria-invalid` trong chuỗi `??` bên dưới.
|
|
32
|
+
{ invalid: undefined },
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const emits = defineEmits<CheckboxRootEmits>()
|
|
36
|
+
|
|
37
|
+
defineSlots<{
|
|
38
|
+
default?: () => unknown
|
|
39
|
+
description?: () => unknown
|
|
40
|
+
indicator?: (props: { state: boolean | 'indeterminate' }) => unknown
|
|
41
|
+
}>()
|
|
42
|
+
|
|
43
|
+
const group = injectCheckboxGroupContext(null)
|
|
44
|
+
|
|
45
|
+
const fallbackId = useId()
|
|
46
|
+
const controlId = computed(() => props.id ?? fallbackId)
|
|
47
|
+
const descriptionId = computed(() => `${controlId.value}-description`)
|
|
48
|
+
|
|
49
|
+
const hasDescription = computed(() => Boolean(props.description))
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Nối chứ không ghi đè: field trong form đã trỏ sang thông báo lỗi rồi.
|
|
53
|
+
*
|
|
54
|
+
* Nhận `attrs` làm tham số để gọi thẳng trong template — `useAttrs()` không phản
|
|
55
|
+
* ứng, bọc vào computed là giá trị đứng im ở lần render đầu (xem Checkbox.vue).
|
|
56
|
+
*/
|
|
57
|
+
function resolveDescribedBy(attrs: Record<string, unknown>): string | undefined {
|
|
58
|
+
const external = typeof attrs['aria-describedby'] === 'string' ? attrs['aria-describedby'] : undefined
|
|
59
|
+
const ids = [external, hasDescription.value ? descriptionId.value : undefined].filter(Boolean)
|
|
60
|
+
return ids.length > 0 ? ids.join(' ') : undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const size = computed(() => props.size ?? group?.size.value ?? 'md')
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Đẩy ô xuống cho tâm nó thẳng hàng với dòng chữ ĐẦU TIÊN của nhãn, không phải
|
|
67
|
+
* với cả khối. Nhãn là `text-sm` nên dòng cao 20px, tâm ở 10px: ô 14px lệch
|
|
68
|
+
* 3px, ô 16px lệch 2px, ô 20px vừa khít.
|
|
69
|
+
*/
|
|
70
|
+
const alignClass = computed(() => ({
|
|
71
|
+
sm: 'mt-[3px]',
|
|
72
|
+
md: 'mt-0.5',
|
|
73
|
+
lg: 'mt-0',
|
|
74
|
+
}[size.value ?? 'md']))
|
|
75
|
+
|
|
76
|
+
const delegated = computed(() => {
|
|
77
|
+
const {
|
|
78
|
+
class: _class,
|
|
79
|
+
size: _size,
|
|
80
|
+
tone: _tone,
|
|
81
|
+
invalid: _invalid,
|
|
82
|
+
label: _label,
|
|
83
|
+
description: _description,
|
|
84
|
+
...rest
|
|
85
|
+
} = props
|
|
86
|
+
return rest
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const forwarded = useForwardPropsEmits(delegated, emits)
|
|
90
|
+
</script>
|
|
91
|
+
|
|
92
|
+
<template>
|
|
93
|
+
<div :class="cn('flex items-start gap-2.5', props.class)">
|
|
94
|
+
<Checkbox
|
|
95
|
+
v-bind="{ ...forwarded, ...$attrs }"
|
|
96
|
+
:id="controlId"
|
|
97
|
+
:size="props.size"
|
|
98
|
+
:tone="props.tone"
|
|
99
|
+
:invalid="props.invalid"
|
|
100
|
+
:aria-describedby="resolveDescribedBy($attrs)"
|
|
101
|
+
:class="alignClass"
|
|
102
|
+
>
|
|
103
|
+
<template v-if="$slots.indicator" #indicator="slotProps">
|
|
104
|
+
<slot name="indicator" v-bind="slotProps" />
|
|
105
|
+
</template>
|
|
106
|
+
</Checkbox>
|
|
107
|
+
|
|
108
|
+
<div class="flex flex-col gap-1">
|
|
109
|
+
<!-- `for` trỏ vào chính ô: <button> là labelable element nên bấm vào chữ
|
|
110
|
+
cũng bật/tắt được, không phải bọc ô vào trong <label>. -->
|
|
111
|
+
<Label
|
|
112
|
+
:for="controlId"
|
|
113
|
+
:class="props.disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'"
|
|
114
|
+
>
|
|
115
|
+
<slot>{{ props.label }}</slot>
|
|
116
|
+
</Label>
|
|
117
|
+
|
|
118
|
+
<p
|
|
119
|
+
v-if="hasDescription || $slots.description"
|
|
120
|
+
:id="descriptionId"
|
|
121
|
+
:class="cn('text-xs leading-snug text-muted-foreground', props.disabled && 'opacity-50')"
|
|
122
|
+
>
|
|
123
|
+
<slot name="description">
|
|
124
|
+
{{ props.description }}
|
|
125
|
+
</slot>
|
|
126
|
+
</p>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</template>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script setup lang="ts" generic="T extends AcceptableValue = AcceptableValue">
|
|
2
|
+
import type { AcceptableValue, CheckboxGroupRootEmits, CheckboxGroupRootProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import type { CheckboxVariants } from './index'
|
|
5
|
+
import { CheckboxGroupRoot, useForwardPropsEmits } from 'reka-ui'
|
|
6
|
+
import { computed } from 'vue'
|
|
7
|
+
import { cn } from '../../lib/utils'
|
|
8
|
+
import { provideCheckboxGroupContext } from './index'
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(
|
|
11
|
+
defineProps<CheckboxGroupRootProps<T> & ClassProps & {
|
|
12
|
+
size?: CheckboxVariants['size']
|
|
13
|
+
tone?: CheckboxVariants['tone']
|
|
14
|
+
invalid?: boolean
|
|
15
|
+
}>(),
|
|
16
|
+
{
|
|
17
|
+
orientation: 'vertical',
|
|
18
|
+
invalid: false,
|
|
19
|
+
/**
|
|
20
|
+
* Reka mặc định `true` — mỗi nhóm chỉ một điểm dừng Tab, di chuyển bằng
|
|
21
|
+
* phím mũi tên. Đó là khuôn của **radio group**: ở đó chọn cái này là bỏ
|
|
22
|
+
* cái kia nên đi qua cả nhóm bằng mũi tên là hợp lý.
|
|
23
|
+
*
|
|
24
|
+
* Ô đánh dấu thì độc lập với nhau, và WAI-ARIA APG xếp **từng ô** vào chuỗi
|
|
25
|
+
* Tab. Bật roving focus sẽ khiến người dùng bàn phím bỏ sót các ô còn lại
|
|
26
|
+
* nếu họ chỉ nhấn Tab. Cần khuôn kia thì khai `roving-focus` tường minh.
|
|
27
|
+
*/
|
|
28
|
+
rovingFocus: false,
|
|
29
|
+
},
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const emits = defineEmits<CheckboxGroupRootEmits<T>>()
|
|
33
|
+
|
|
34
|
+
const delegated = computed(() => {
|
|
35
|
+
const { class: _class, size: _size, tone: _tone, invalid: _invalid, ...rest } = props
|
|
36
|
+
return rest
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const forwarded = useForwardPropsEmits(delegated, emits)
|
|
40
|
+
|
|
41
|
+
// Cỡ và kiểu tô khai một lần ở nhóm, từng ô con khỏi lặp lại.
|
|
42
|
+
provideCheckboxGroupContext({
|
|
43
|
+
size: computed(() => props.size),
|
|
44
|
+
tone: computed(() => props.tone),
|
|
45
|
+
invalid: computed(() => props.invalid),
|
|
46
|
+
})
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<template>
|
|
50
|
+
<CheckboxGroupRoot
|
|
51
|
+
v-bind="forwarded"
|
|
52
|
+
:class="cn(
|
|
53
|
+
'flex',
|
|
54
|
+
props.orientation === 'horizontal'
|
|
55
|
+
? 'flex-wrap items-center gap-x-5 gap-y-2.5'
|
|
56
|
+
: 'flex-col gap-2.5',
|
|
57
|
+
props.class,
|
|
58
|
+
)"
|
|
59
|
+
>
|
|
60
|
+
<slot />
|
|
61
|
+
</CheckboxGroupRoot>
|
|
62
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ComputedRef } from 'vue'
|
|
2
|
+
import type { CheckboxVariants } from '../../variants/components/checkbox'
|
|
3
|
+
import { createContext } from 'reka-ui'
|
|
4
|
+
|
|
5
|
+
export { checkboxVariants, type CheckboxVariants } from '../../variants/components/checkbox'
|
|
6
|
+
export { default as Checkbox } from './Checkbox.vue'
|
|
7
|
+
export { default as CheckboxField } from './CheckboxField.vue'
|
|
8
|
+
|
|
9
|
+
/** Cỡ/kiểu tô/trạng thái lỗi mà `CheckboxGroup` truyền xuống từng ô con. */
|
|
10
|
+
export interface CheckboxGroupContext {
|
|
11
|
+
size: ComputedRef<CheckboxVariants['size']>
|
|
12
|
+
tone: ComputedRef<CheckboxVariants['tone']>
|
|
13
|
+
invalid: ComputedRef<boolean>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Dùng createContext của reka thay vì tự khai InjectionKey: cùng một cơ chế với
|
|
17
|
+
// mọi context khác đang chạy trong cây component, và báo lỗi kèm tên component.
|
|
18
|
+
export const [injectCheckboxGroupContext, provideCheckboxGroupContext]
|
|
19
|
+
= createContext<CheckboxGroupContext>('CheckboxGroup')
|
|
20
|
+
|
|
21
|
+
export { default as CheckboxGroup } from './CheckboxGroup.vue'
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import type { BadgeVariants } from '../badge'
|
|
4
|
+
import type { ChipVariants } from './index'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { useI18n } from '../../composables/useI18n'
|
|
7
|
+
import { X } from '../../lib/icons'
|
|
8
|
+
import { cn } from '../../lib/utils'
|
|
9
|
+
import { badgeVariants } from '../badge'
|
|
10
|
+
import { chipVariants } from './index'
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(
|
|
13
|
+
defineProps<ClassProps & {
|
|
14
|
+
variant?: BadgeVariants['variant']
|
|
15
|
+
tone?: BadgeVariants['tone']
|
|
16
|
+
size?: ChipVariants['size']
|
|
17
|
+
/** Hiện nút ×. Không bật thì Chip chỉ là một nhãn bo tròn. */
|
|
18
|
+
removable?: boolean
|
|
19
|
+
/**
|
|
20
|
+
* Nhãn cho nút gỡ. Kèm tên chip vào để đọc ra "Gỡ Đang mở".
|
|
21
|
+
*
|
|
22
|
+
* KHÔNG đặt default ở `withDefaults`: default tính một lần lúc dựng
|
|
23
|
+
* component nên nó đóng băng ở ngôn ngữ lúc đó. Rơi lui bằng computed.
|
|
24
|
+
*/
|
|
25
|
+
removeLabel?: string
|
|
26
|
+
disabled?: boolean
|
|
27
|
+
}>(),
|
|
28
|
+
{ removable: false, disabled: false },
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const emit = defineEmits<{ remove: [] }>()
|
|
32
|
+
|
|
33
|
+
const { t } = useI18n()
|
|
34
|
+
|
|
35
|
+
const removeLabel = computed(() => props.removeLabel ?? t('ui.chip.remove'))
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Lấy phần MÀU của badgeVariants rồi bỏ phần hình dáng đi.
|
|
39
|
+
*
|
|
40
|
+
* `badgeVariants` gộp cả hai trong một chuỗi. `cn()` dùng tailwind-merge nên
|
|
41
|
+
* lớp hình dáng của chip viết sau sẽ thắng `rounded-md px-2 py-0.5 text-xs` của
|
|
42
|
+
* badge — không phải cắt chuỗi bằng tay.
|
|
43
|
+
*/
|
|
44
|
+
const classes = computed(() => cn(
|
|
45
|
+
badgeVariants({ variant: props.variant, tone: props.tone }),
|
|
46
|
+
chipVariants({ size: props.size, removable: props.removable }),
|
|
47
|
+
props.disabled && 'pointer-events-none opacity-50',
|
|
48
|
+
props.class,
|
|
49
|
+
))
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template>
|
|
53
|
+
<span :class="classes">
|
|
54
|
+
<slot name="leading" />
|
|
55
|
+
<span class="min-w-0 truncate"><slot /></span>
|
|
56
|
+
|
|
57
|
+
<!--
|
|
58
|
+
Nút gỡ là <button> thật, không phải icon bấm được: nó phải vào được chuỗi
|
|
59
|
+
Tab và nhận Enter/Space. `currentColor` cho nó tự hợp với mọi variant, kể
|
|
60
|
+
cả nền đặc lẫn nền soft.
|
|
61
|
+
-->
|
|
62
|
+
<button
|
|
63
|
+
v-if="props.removable"
|
|
64
|
+
type="button"
|
|
65
|
+
class="grid size-4 shrink-0 place-items-center rounded-full text-current opacity-60 transition-opacity hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current"
|
|
66
|
+
:aria-label="removeLabel"
|
|
67
|
+
:disabled="props.disabled"
|
|
68
|
+
@click.stop="emit('remove')"
|
|
69
|
+
>
|
|
70
|
+
<X class="size-3" />
|
|
71
|
+
</button>
|
|
72
|
+
</span>
|
|
73
|
+
</template>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import type { Language } from '../../variants/lib/highlight'
|
|
4
|
+
import { computed, onUnmounted, ref } from 'vue'
|
|
5
|
+
import { useI18n } from '../../composables/useI18n'
|
|
6
|
+
import { Check, Copy } from '../../lib/icons'
|
|
7
|
+
import { cn } from '../../lib/utils'
|
|
8
|
+
import { codeTokenClass } from '../../variants/components/code-block'
|
|
9
|
+
import { highlightLines } from '../../variants/lib/highlight'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(
|
|
12
|
+
defineProps<ClassProps & {
|
|
13
|
+
code: string
|
|
14
|
+
/** `vue` cho SFC và template, `ts` cho đoạn script thuần. */
|
|
15
|
+
language?: Language
|
|
16
|
+
/** Tắt khi khối chỉ có một hai dòng lệnh shell. */
|
|
17
|
+
lineNumbers?: boolean
|
|
18
|
+
/** Bỏ viền khi khối nằm ngay dưới khung preview (đã có viền ngoài). */
|
|
19
|
+
bordered?: boolean
|
|
20
|
+
/** Quá số dòng này thì khối tự cuộn dọc thay vì kéo dài trang. */
|
|
21
|
+
maxLines?: number
|
|
22
|
+
}>(),
|
|
23
|
+
{ language: 'vue', lineNumbers: true, bordered: true, maxLines: 10 },
|
|
24
|
+
)
|
|
25
|
+
const { t } = useI18n()
|
|
26
|
+
/** Giữ trạng thái "đã chép" bao lâu trước khi cho bấm lại. */
|
|
27
|
+
const RESET_DELAY = 3000
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Chiều cao trần = số dòng × line-height + padding dọc.
|
|
31
|
+
* `em` ăn theo font-size của chính khối code (12.5px) nên đổi cỡ chữ là nó tự
|
|
32
|
+
* theo, không phải chỉnh lại con số.
|
|
33
|
+
*/
|
|
34
|
+
const maxHeight = computed(() => `calc(${props.maxLines} * 1.85em + 2rem)`)
|
|
35
|
+
|
|
36
|
+
/** Cắt theo dòng để số dòng bên trái luôn thẳng hàng với nội dung. */
|
|
37
|
+
const lines = computed(() => highlightLines(props.code, props.language))
|
|
38
|
+
|
|
39
|
+
const copied = ref(false)
|
|
40
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
41
|
+
|
|
42
|
+
onUnmounted(() => clearTimeout(timer))
|
|
43
|
+
|
|
44
|
+
async function copy(): Promise<void> {
|
|
45
|
+
try {
|
|
46
|
+
await navigator.clipboard.writeText(props.code)
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Clipboard API cần ngữ cảnh bảo mật (https hoặc localhost). Không chép
|
|
50
|
+
// được thì thôi, không báo thành công nhầm.
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
copied.value = true
|
|
55
|
+
clearTimeout(timer)
|
|
56
|
+
timer = setTimeout(() => {
|
|
57
|
+
copied.value = false
|
|
58
|
+
}, RESET_DELAY)
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<template>
|
|
63
|
+
<div :class="cn('relative', props.class)">
|
|
64
|
+
<button
|
|
65
|
+
type="button"
|
|
66
|
+
:aria-label="copied ? t('ui.copied') : t('ui.copy.code')"
|
|
67
|
+
class="absolute right-3 top-3 z-(--z-raised) grid size-[26px] place-items-center rounded-md border border-border bg-card text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-[13px]"
|
|
68
|
+
:disabled="copied"
|
|
69
|
+
@click="copy"
|
|
70
|
+
>
|
|
71
|
+
<Check v-if="copied" class="text-success" />
|
|
72
|
+
<Copy v-else />
|
|
73
|
+
</button>
|
|
74
|
+
|
|
75
|
+
<!-- Nền tô ở lớp ngoài, đúng MỘT lần; lớp trong chỉ lo cuộn và mờ dần. -->
|
|
76
|
+
<div
|
|
77
|
+
class="overflow-hidden bg-code-bg"
|
|
78
|
+
:class="bordered ? 'rounded-xl border border-border' : 'border-t border-border'"
|
|
79
|
+
>
|
|
80
|
+
<div
|
|
81
|
+
class="overflow-auto py-4 pl-[18px] pr-[44px] font-mono text-[12.5px] leading-[1.85] text-code-fg"
|
|
82
|
+
:style="{ maxHeight }"
|
|
83
|
+
>
|
|
84
|
+
<div
|
|
85
|
+
v-for="(tokens, row) in lines"
|
|
86
|
+
:key="row"
|
|
87
|
+
class="grid w-max gap-3.5 pr-[42px]"
|
|
88
|
+
:class="lineNumbers ? 'grid-cols-[28px_max-content]' : 'grid-cols-[max-content]'"
|
|
89
|
+
>
|
|
90
|
+
<span v-if="lineNumbers" class="text-right text-muted-foreground opacity-65">{{ row + 1 }}</span>
|
|
91
|
+
<span class="min-w-0 whitespace-pre"><span
|
|
92
|
+
v-for="(token, index) in tokens"
|
|
93
|
+
:key="index"
|
|
94
|
+
:class="codeTokenClass[token.kind]"
|
|
95
|
+
>{{ token.value }}</span></span>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</template>
|