@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,45 @@
|
|
|
1
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
2
|
+
import { cva } from 'class-variance-authority'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Dòng thời gian: nhật ký hoạt động, lịch sử đơn hàng, các bước đã chạy.
|
|
6
|
+
*
|
|
7
|
+
* P cho đặt nội dung ở **cả hai** phía đường kẻ (`align="alternate"`). Ở đây bỏ
|
|
8
|
+
* hẳn kiểu đó: nội dung so le hai bên đọc theo hình zíc zắc, mà nhật ký quản trị
|
|
9
|
+
* thì người ta quét dọc bằng mắt. Chỉ giữ một cột nội dung.
|
|
10
|
+
*/
|
|
11
|
+
export const timelineVariants = cva('flex w-full flex-col', {
|
|
12
|
+
variants: {
|
|
13
|
+
size: {
|
|
14
|
+
sm: 'gap-0',
|
|
15
|
+
default: 'gap-0',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: { size: 'default' },
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
/** Chấm mốc. Màu mang NGHĨA nên đi theo bộ variant chung của repo. */
|
|
22
|
+
export const timelineMarkerVariants = cva(
|
|
23
|
+
'grid shrink-0 place-items-center rounded-full border-2 bg-background [&_svg]:size-3',
|
|
24
|
+
{
|
|
25
|
+
variants: {
|
|
26
|
+
variant: {
|
|
27
|
+
default: 'border-border text-muted-foreground',
|
|
28
|
+
primary: 'border-primary bg-primary text-primary-foreground',
|
|
29
|
+
success: 'border-success bg-success text-success-foreground',
|
|
30
|
+
warning: 'border-warning bg-warning text-warning-foreground',
|
|
31
|
+
destructive: 'border-destructive bg-destructive text-destructive-foreground',
|
|
32
|
+
info: 'border-info bg-info text-info-foreground',
|
|
33
|
+
},
|
|
34
|
+
size: {
|
|
35
|
+
sm: 'size-4',
|
|
36
|
+
default: 'size-6',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
defaultVariants: { variant: 'default', size: 'default' },
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
export type TimelineVariants = VariantProps<typeof timelineVariants>
|
|
44
|
+
|
|
45
|
+
export type TimelineMarkerVariants = VariantProps<typeof timelineMarkerVariants>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
2
|
+
import { cva } from 'class-variance-authority'
|
|
3
|
+
import { focusRing } from '../lib/focus'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Nút giữ trạng thái bật/tắt. Thang cỡ lấy **đúng** của Button (24 / 28 / 32 /
|
|
7
|
+
* 36px, kèm bốn cỡ vuông) chứ không khai riêng: toggle với button hay đứng cùng
|
|
8
|
+
* một thanh công cụ, lệch chiều cao là thấy ngay.
|
|
9
|
+
*
|
|
10
|
+
* Hai `variant`:
|
|
11
|
+
* - `ghost` — không viền, chỉ đổi nền khi bật. Dùng trong thanh công cụ dày đặc.
|
|
12
|
+
* - `outline` — có viền, đọc ra như một nút thật. Dùng khi nó đứng một mình.
|
|
13
|
+
*
|
|
14
|
+
* Trạng thái bật đọc từ `data-state=on` của reka, không phải từ prop.
|
|
15
|
+
*/
|
|
16
|
+
export const toggleVariants = cva(
|
|
17
|
+
[
|
|
18
|
+
'ui-button inline-flex items-center justify-center gap-1.5 whitespace-nowrap rounded-lg',
|
|
19
|
+
'text-sm font-medium',
|
|
20
|
+
'transition-[color,background-color,border-color,opacity,transform] duration-(--ui-control-duration)',
|
|
21
|
+
'active:scale-[0.97] active:duration-(--ui-control-duration-press) motion-reduce:active:scale-100',
|
|
22
|
+
focusRing,
|
|
23
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
24
|
+
'[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
|
25
|
+
],
|
|
26
|
+
{
|
|
27
|
+
variants: {
|
|
28
|
+
variant: {
|
|
29
|
+
ghost: [
|
|
30
|
+
'bg-transparent hover:bg-accent hover:text-accent-foreground',
|
|
31
|
+
'data-[state=on]:bg-accent data-[state=on]:text-accent-foreground',
|
|
32
|
+
],
|
|
33
|
+
outline: [
|
|
34
|
+
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
35
|
+
'data-[state=on]:bg-accent data-[state=on]:text-accent-foreground',
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
size: {
|
|
40
|
+
'xs': 'h-(--ui-control-height-xs) gap-1 rounded-md px-(--ui-control-padding-x-xs) text-xs [&_svg]:size-3',
|
|
41
|
+
'sm': 'h-(--ui-control-height-sm) gap-1 rounded-md px-(--ui-control-padding-x) text-[0.8rem]',
|
|
42
|
+
'default': 'h-(--ui-control-height-md) px-(--ui-control-padding-x)',
|
|
43
|
+
'lg': 'h-(--ui-control-height-lg) px-(--ui-control-padding-x)',
|
|
44
|
+
'icon-xs': 'size-(--ui-control-height-xs) rounded-md [&_svg]:size-3',
|
|
45
|
+
'icon-sm': 'size-(--ui-control-height-sm) rounded-md',
|
|
46
|
+
'icon': 'size-(--ui-control-height-md)',
|
|
47
|
+
'icon-lg': 'size-(--ui-control-height-lg)',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
defaultVariants: {
|
|
52
|
+
variant: 'ghost',
|
|
53
|
+
size: 'default',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
export type ToggleVariants = VariantProps<typeof toggleVariants>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel cho bên nào muốn lấy cả bộ một lần.
|
|
3
|
+
*
|
|
4
|
+
* Bên trong repo thì nhập theo subpath (`src/variants/components/button`): bộ dò
|
|
5
|
+
* token của trang docs đi theo import để biết component nào đọc bảng nào, và
|
|
6
|
+
* một file barrel sẽ làm cả 18 bảng dính vào nhau — trang Select mượn
|
|
7
|
+
* `badgeVariants` cho một cái chip sẽ kéo luôn năm màu trạng thái vào bảng.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export * from './components/accordion'
|
|
11
|
+
export * from './components/alert'
|
|
12
|
+
export * from './components/badge'
|
|
13
|
+
export * from './components/button'
|
|
14
|
+
export * from './components/button-group'
|
|
15
|
+
export * from './components/checkbox'
|
|
16
|
+
export * from './components/code-block'
|
|
17
|
+
export * from './components/dialog'
|
|
18
|
+
export * from './components/empty'
|
|
19
|
+
export * from './components/field'
|
|
20
|
+
export * from './components/float-label'
|
|
21
|
+
export * from './components/input-group'
|
|
22
|
+
export * from './components/input-password'
|
|
23
|
+
export * from './components/kbd'
|
|
24
|
+
export * from './components/picker-trigger'
|
|
25
|
+
export * from './components/radio-group'
|
|
26
|
+
export * from './components/sheet'
|
|
27
|
+
export * from './components/sidebar'
|
|
28
|
+
export * from './components/slider'
|
|
29
|
+
export * from './components/timeline'
|
|
30
|
+
export * from './components/toggle'
|
|
31
|
+
export * from './lib/focus'
|
|
32
|
+
export * from './lib/highlight'
|
|
33
|
+
export * from './lib/menu-styles'
|
|
34
|
+
export * from './lib/platform'
|
|
35
|
+
export * from './lib/utils'
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hình học cực, dùng chung cho RadialChart và RadarChart.
|
|
3
|
+
*
|
|
4
|
+
* Hai loại này Unovis KHÔNG có (`nested-donut` là sunburst phân cấp, không phải
|
|
5
|
+
* vòng đồng tâm; radar thì không có gì cả), nên vẽ tay bằng SVG. Đổi lại được
|
|
6
|
+
* một thứ: mọi màu là `var(--color-…)` đặt thẳng vào thuộc tính SVG, nên vẫn đi
|
|
7
|
+
* theo token y hệt phần còn lại — không phải uốn theo API của thư viện.
|
|
8
|
+
*
|
|
9
|
+
* Quy ước góc: 0 ở đỉnh (12 giờ), chạy THEO chiều kim đồng hồ, đơn vị độ.
|
|
10
|
+
* Chọn vậy vì cả radial lẫn radar đều đọc từ đỉnh xuống, còn `Math.cos/sin`
|
|
11
|
+
* mặc định lấy 0 ở hướng 3 giờ và chạy ngược kim đồng hồ.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export interface Point {
|
|
15
|
+
x: number
|
|
16
|
+
y: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Đổi (góc, bán kính) sang toạ độ Descartes quanh tâm. */
|
|
20
|
+
export function polarToCartesian(cx: number, cy: number, radius: number, angleDeg: number): Point {
|
|
21
|
+
const rad = ((angleDeg - 90) * Math.PI) / 180
|
|
22
|
+
return { x: cx + radius * Math.cos(rad), y: cy + radius * Math.sin(rad) }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Path của một cung VÀNH (có bề dày), tức hình quạt bị khoét ruột.
|
|
27
|
+
*
|
|
28
|
+
* Cung tròn 360° phải tách làm HAI cung 180°: cờ `large-arc-flag` của SVG chỉ
|
|
29
|
+
* phân biệt được cung lớn hay nhỏ, nên một cung đúng 360° có điểm đầu trùng
|
|
30
|
+
* điểm cuối và trình duyệt vẽ ra… không gì cả.
|
|
31
|
+
*/
|
|
32
|
+
export function annularArcPath(
|
|
33
|
+
cx: number,
|
|
34
|
+
cy: number,
|
|
35
|
+
outerRadius: number,
|
|
36
|
+
innerRadius: number,
|
|
37
|
+
startAngle: number,
|
|
38
|
+
endAngle: number,
|
|
39
|
+
): string {
|
|
40
|
+
const sweep = Math.abs(endAngle - startAngle)
|
|
41
|
+
if (sweep < 0.01)
|
|
42
|
+
return ''
|
|
43
|
+
|
|
44
|
+
if (sweep >= 359.99) {
|
|
45
|
+
const mid = startAngle + 180
|
|
46
|
+
return `${annularArcPath(cx, cy, outerRadius, innerRadius, startAngle, mid)} ${annularArcPath(cx, cy, outerRadius, innerRadius, mid, startAngle + 359.98)}`
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const largeArc = sweep > 180 ? 1 : 0
|
|
50
|
+
const o1 = polarToCartesian(cx, cy, outerRadius, startAngle)
|
|
51
|
+
const o2 = polarToCartesian(cx, cy, outerRadius, endAngle)
|
|
52
|
+
const i2 = polarToCartesian(cx, cy, innerRadius, endAngle)
|
|
53
|
+
const i1 = polarToCartesian(cx, cy, innerRadius, startAngle)
|
|
54
|
+
|
|
55
|
+
return [
|
|
56
|
+
`M ${o1.x} ${o1.y}`,
|
|
57
|
+
`A ${outerRadius} ${outerRadius} 0 ${largeArc} 1 ${o2.x} ${o2.y}`,
|
|
58
|
+
`L ${i2.x} ${i2.y}`,
|
|
59
|
+
`A ${innerRadius} ${innerRadius} 0 ${largeArc} 0 ${i1.x} ${i1.y}`,
|
|
60
|
+
'Z',
|
|
61
|
+
].join(' ')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Các đỉnh của một đa giác đều `sides` cạnh, đỉnh đầu ở 12 giờ. */
|
|
65
|
+
export function polygonPoints(cx: number, cy: number, radius: number, sides: number): Point[] {
|
|
66
|
+
return Array.from({ length: sides }, (_, i) => polarToCartesian(cx, cy, radius, (360 / sides) * i))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Chuỗi `points` cho `<polygon>`. */
|
|
70
|
+
export function toPointsAttr(points: Point[]): string {
|
|
71
|
+
return points.map(p => `${p.x.toFixed(2)},${p.y.toFixed(2)}`).join(' ')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Khung bao của một cung vành, tính đúng chứ không lấy cả hình vuông.
|
|
76
|
+
*
|
|
77
|
+
* Cần nó vì một cung nửa vòng chỉ chiếm nửa khung: để nguyên khung vuông thì
|
|
78
|
+
* biểu đồ bán nguyệt bị dạt lên trên và bỏ trống nửa dưới. Điểm cực trị của
|
|
79
|
+
* cung tròn chỉ rơi vào hai đầu mút HOẶC các mốc 0/90/180/270 nằm trong khoảng,
|
|
80
|
+
* nên chỉ cần thử chừng ấy điểm.
|
|
81
|
+
*/
|
|
82
|
+
export function arcBounds(
|
|
83
|
+
cx: number,
|
|
84
|
+
cy: number,
|
|
85
|
+
outerRadius: number,
|
|
86
|
+
innerRadius: number,
|
|
87
|
+
startAngle: number,
|
|
88
|
+
endAngle: number,
|
|
89
|
+
includeCenter = false,
|
|
90
|
+
): { x: number, y: number, width: number, height: number } {
|
|
91
|
+
const lo = Math.min(startAngle, endAngle)
|
|
92
|
+
const hi = Math.max(startAngle, endAngle)
|
|
93
|
+
|
|
94
|
+
const angles = [lo, hi]
|
|
95
|
+
for (let a = Math.ceil(lo / 90) * 90; a <= hi; a += 90)
|
|
96
|
+
angles.push(a)
|
|
97
|
+
|
|
98
|
+
const points: Point[] = []
|
|
99
|
+
for (const a of angles) {
|
|
100
|
+
points.push(polarToCartesian(cx, cy, outerRadius, a))
|
|
101
|
+
points.push(polarToCartesian(cx, cy, innerRadius, a))
|
|
102
|
+
}
|
|
103
|
+
if (includeCenter)
|
|
104
|
+
points.push({ x: cx, y: cy })
|
|
105
|
+
|
|
106
|
+
const xs = points.map(p => p.x)
|
|
107
|
+
const ys = points.map(p => p.y)
|
|
108
|
+
const minX = Math.min(...xs)
|
|
109
|
+
const minY = Math.min(...ys)
|
|
110
|
+
return { x: minX, y: minY, width: Math.max(...xs) - minX, height: Math.max(...ys) - minY }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Path của một cung MỞ (chỉ đường, không khép kín) chạy trên bán kính `radius`.
|
|
115
|
+
*
|
|
116
|
+
* Dùng cho radial bar: thay vì tô một hình quạt vành, ta VẼ NÉT dày bằng đúng
|
|
117
|
+
* bề dày vành dọc theo đường tâm của nó. Nhờ vậy `stroke-linecap="round"` cho ra
|
|
118
|
+
* hai đầu cung bo tròn miễn phí — thứ mà tô hình quạt phải dựng path phức tạp
|
|
119
|
+
* mới có (Recharts gọi là `cornerRadius`).
|
|
120
|
+
*
|
|
121
|
+
* Cung 360° vẫn phải tách đôi, cùng lý do như `annularArcPath`.
|
|
122
|
+
*/
|
|
123
|
+
export function arcPath(cx: number, cy: number, radius: number, startAngle: number, endAngle: number): string {
|
|
124
|
+
const sweep = Math.abs(endAngle - startAngle)
|
|
125
|
+
if (sweep < 0.01)
|
|
126
|
+
return ''
|
|
127
|
+
|
|
128
|
+
if (sweep >= 359.99) {
|
|
129
|
+
const mid = startAngle + 180
|
|
130
|
+
const a = polarToCartesian(cx, cy, radius, startAngle)
|
|
131
|
+
const b = polarToCartesian(cx, cy, radius, mid)
|
|
132
|
+
return `M ${a.x} ${a.y} A ${radius} ${radius} 0 0 1 ${b.x} ${b.y} A ${radius} ${radius} 0 0 1 ${a.x} ${a.y}`
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const largeArc = sweep > 180 ? 1 : 0
|
|
136
|
+
const from = polarToCartesian(cx, cy, radius, startAngle)
|
|
137
|
+
const to = polarToCartesian(cx, cy, radius, endAngle)
|
|
138
|
+
return `M ${from.x} ${from.y} A ${radius} ${radius} 0 ${largeArc} 1 ${to.x} ${to.y}`
|
|
139
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { ChartTooltipOptions, ChartTooltipRow } from './types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unovis nhận `template` trả về CHUỖI HTML, không nhận component của framework
|
|
5
|
+
* nào — nên hàm này dùng chung được cho cả hai cây, và ở đây là chỗ của nó.
|
|
6
|
+
*
|
|
7
|
+
* shadcn-vue giải bài này bằng `createApp(ChartTooltip).mount(div)` cho từng
|
|
8
|
+
* điểm dữ liệu rồi cache `innerHTML` vào một WeakMap. Cách đó có hai chỗ hỏng:
|
|
9
|
+
* mount hẳn một app mỗi lần rê chuột là quá nặng, và HTML đã cache thì KHÔNG
|
|
10
|
+
* đổi theo theme nữa — đổi sang chế độ tối xong tooltip vẫn giữ màu cũ.
|
|
11
|
+
*
|
|
12
|
+
* Ở đây chỉ ghép chuỗi. Vì nó là DOM thật trong trang nên utility Tailwind và
|
|
13
|
+
* token vẫn áp bình thường, và màu lấy qua `var(--color-<key>)` nên đổi theme
|
|
14
|
+
* hay đổi bảng màu là tooltip đổi theo ngay, không có gì để mà cũ.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Chặn nhãn do người dùng truyền vào phá cấu trúc HTML. */
|
|
18
|
+
export function escapeHtml(value: string): string {
|
|
19
|
+
return value
|
|
20
|
+
.replace(/&/g, '&')
|
|
21
|
+
.replace(/</g, '<')
|
|
22
|
+
.replace(/>/g, '>')
|
|
23
|
+
.replace(/"/g, '"')
|
|
24
|
+
.replace(/'/g, ''')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Ba kiểu chỉ báo của shadcn. `dashed` cố ý KHÔNG có nền — nó là hai nét gạch
|
|
29
|
+
* nên phải để trong suốt, nếu tô nền thì nhìn hệt `line` bị dày lên.
|
|
30
|
+
*/
|
|
31
|
+
const INDICATOR_CLASS: Record<NonNullable<ChartTooltipOptions['indicator']>, string> = {
|
|
32
|
+
dot: 'size-2.5 rounded-[2px]',
|
|
33
|
+
line: 'h-3 w-1 rounded-[2px]',
|
|
34
|
+
dashed: 'h-3 w-0 border-[1.5px] border-dashed',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function indicatorHtml(color: string, options: ChartTooltipOptions): string {
|
|
38
|
+
if (options.hideIndicator)
|
|
39
|
+
return ''
|
|
40
|
+
const kind = options.indicator ?? 'dot'
|
|
41
|
+
const style = kind === 'dashed'
|
|
42
|
+
? `border-color:${color}`
|
|
43
|
+
: `background:${color}`
|
|
44
|
+
return `<div class="shrink-0 ${INDICATOR_CLASS[kind]}" style="${style}"></div>`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Ghép hộp tooltip theo đúng bố cục của shadcn: dòng tiêu đề đậm, dưới là từng
|
|
49
|
+
* chuỗi với chỉ báo màu, tên mờ bên trái và số bên phải.
|
|
50
|
+
*
|
|
51
|
+
* Số dùng `tabular-nums` + `font-mono` để cột giá trị không nhảy khi rê chuột
|
|
52
|
+
* qua các điểm có độ dài chữ số khác nhau.
|
|
53
|
+
*/
|
|
54
|
+
export function renderTooltip(
|
|
55
|
+
title: string,
|
|
56
|
+
rows: ChartTooltipRow[],
|
|
57
|
+
options: ChartTooltipOptions = {},
|
|
58
|
+
): string {
|
|
59
|
+
const heading = options.labelFormatter ? options.labelFormatter(title) : title
|
|
60
|
+
const head = heading && !options.hideLabel
|
|
61
|
+
? `<div class="font-medium text-popover-foreground">${escapeHtml(heading)}</div>`
|
|
62
|
+
: ''
|
|
63
|
+
|
|
64
|
+
const body = rows.map(row => `
|
|
65
|
+
<div class="flex w-full items-center gap-2">
|
|
66
|
+
${indicatorHtml(row.color, options)}
|
|
67
|
+
<div class="flex flex-1 items-center justify-between gap-4 leading-none">
|
|
68
|
+
<span class="text-muted-foreground">${escapeHtml(row.label)}</span>
|
|
69
|
+
<span class="font-mono font-medium tabular-nums text-foreground">${escapeHtml(row.value)}</span>
|
|
70
|
+
</div>
|
|
71
|
+
</div>`).join('')
|
|
72
|
+
|
|
73
|
+
// Dòng tổng ngăn bằng một đường kẻ, và KHÔNG có chỉ báo màu: nó không phải
|
|
74
|
+
// một chuỗi, gắn ô màu vào là đọc nhầm thành chuỗi thứ n+1.
|
|
75
|
+
const totalRow = options.showTotal && rows.length > 1
|
|
76
|
+
? `<div class="mt-1.5 flex items-center justify-between gap-4 border-t border-border/50 pt-1.5">
|
|
77
|
+
<span class="text-muted-foreground">${escapeHtml(options.totalLabel ?? 'Total')}</span>
|
|
78
|
+
<span class="font-mono font-medium tabular-nums text-foreground">${escapeHtml(sumOf(rows))}</span>
|
|
79
|
+
</div>`
|
|
80
|
+
: ''
|
|
81
|
+
|
|
82
|
+
return `<div class="grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-popover px-2.5 py-1.5 text-xs shadow-xl">
|
|
83
|
+
${head}
|
|
84
|
+
<div class="grid gap-1.5">${body}</div>
|
|
85
|
+
${totalRow}
|
|
86
|
+
</div>`
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Tổng của các dòng. Cộng lại từ CHUỖI đã định dạng thì sai, nên mỗi dòng giữ
|
|
91
|
+
* thêm `raw` để cộng; thiếu `raw` thì bỏ qua dòng đó.
|
|
92
|
+
*/
|
|
93
|
+
function sumOf(rows: ChartTooltipRow[]): string {
|
|
94
|
+
const total = rows.reduce((sum, r) => sum + (r.raw ?? 0), 0)
|
|
95
|
+
return formatChartValue(total)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Định dạng số mặc định: phân nhóm hàng nghìn theo locale của trình duyệt. */
|
|
99
|
+
export function formatChartValue(value: number): string {
|
|
100
|
+
return Number.isFinite(value) ? value.toLocaleString() : '—'
|
|
101
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kiểu dùng chung cho cả bộ biểu đồ — KHÔNG dính framework nào.
|
|
3
|
+
*
|
|
4
|
+
* Nằm ở đây vì nó mô tả DỮ LIỆU và các tuỳ chọn hiển thị, mà cả hai cây đọc
|
|
5
|
+
* đúng một hình dạng: `ChartConfig` của shadcn, cùng prop của Recharts mà bộ
|
|
6
|
+
* này soi theo. Hai bản là hai cơ hội để một bên thêm prop rồi bên kia không
|
|
7
|
+
* biết.
|
|
8
|
+
*
|
|
9
|
+
* Phần CÓ dính framework — icon của legend, và context mà `ChartContainer`
|
|
10
|
+
* chuyền xuống — nằm ở `types.ts` của từng cây.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Mô tả một chuỗi số liệu: nhãn, màu, icon.
|
|
15
|
+
*
|
|
16
|
+
* Đây là `ChartConfig` của shadcn, giữ nguyên hình dạng để người đã quen bên đó
|
|
17
|
+
* không phải học lại. Khoá của object chính là khoá trong dữ liệu, nên chỉ khai
|
|
18
|
+
* một lần là legend, tooltip và màu vẽ đều dùng chung.
|
|
19
|
+
*/
|
|
20
|
+
export interface ChartSeriesConfig {
|
|
21
|
+
/** Nhãn người dùng nhìn thấy trong legend và tooltip. */
|
|
22
|
+
label: string
|
|
23
|
+
/**
|
|
24
|
+
* Màu của chuỗi. Bỏ trống thì lấy `--chart-1`…`--chart-5` theo đúng thứ tự
|
|
25
|
+
* khai báo trong config, quay vòng khi có hơn năm chuỗi.
|
|
26
|
+
*/
|
|
27
|
+
color?: string
|
|
28
|
+
/**
|
|
29
|
+
* Icon đứng trước nhãn trong legend.
|
|
30
|
+
*
|
|
31
|
+
* Kiểu để `unknown` ở tầng dùng chung: bên Vue nó là một `Component`, bên
|
|
32
|
+
* React là một `ComponentType` — mỗi cây thu hẹp lại trong `ChartConfig` của
|
|
33
|
+
* chính nó. Đây là trường DUY NHẤT của bộ type này dính tới framework.
|
|
34
|
+
*/
|
|
35
|
+
icon?: unknown
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Khoá của object là khoá trong dữ liệu. */
|
|
39
|
+
export type ChartConfig = Record<string, ChartSeriesConfig>
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Lề trong của khung vẽ.
|
|
43
|
+
*
|
|
44
|
+
* Khai lại ở đây chứ không import `Spacing` của `@unovis/ts`: `src/variants`
|
|
45
|
+
* không được phụ thuộc một thư viện biểu đồ — nó là tầng không biết framework
|
|
46
|
+
* VÀ không biết thư viện vẽ. Hình dạng trùng khít nên vẫn truyền thẳng xuống
|
|
47
|
+
* Unovis được nhờ structural typing.
|
|
48
|
+
*/
|
|
49
|
+
export interface ChartSpacing {
|
|
50
|
+
top?: number
|
|
51
|
+
right?: number
|
|
52
|
+
bottom?: number
|
|
53
|
+
left?: number
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Hàm định dạng nhãn trục, nhận đúng chữ ký `tickFormat` của Unovis. */
|
|
57
|
+
export type ChartTickFormatter = (tick: number | Date, i: number, ticks: number[] | Date[]) => string
|
|
58
|
+
|
|
59
|
+
/** Định dạng giá trị hiện trong tooltip và legend. */
|
|
60
|
+
export type ChartValueFormatter = (value: number, key: string) => string
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Kiểu nối điểm. Rút gọn từ `CurveType` của Unovis xuống bốn lựa chọn mà gallery
|
|
64
|
+
* của shadcn thật sự dùng — bày cả mười tám kiểu chỉ tổ làm người đọc phân vân.
|
|
65
|
+
*
|
|
66
|
+
* `natural` là kiểu shadcn dùng nhiều nhất (8 ô area, 6 ô line); `monotone` chỉ
|
|
67
|
+
* ở `line-multiple` và `line-interactive`. Hai kiểu này nhìn gần giống nhau —
|
|
68
|
+
* khác ở chỗ `natural` (spline tự nhiên) được phép vọt qua giá trị điểm dữ liệu,
|
|
69
|
+
* còn `monotone` thì không bao giờ.
|
|
70
|
+
*/
|
|
71
|
+
export type ChartCurve = 'monotone' | 'natural' | 'linear' | 'step'
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Nhãn in trên cột / lát / điểm lấy nội dung ở đâu: con SỐ, hay TÊN hạng mục.
|
|
75
|
+
*
|
|
76
|
+
* `'name'` tra qua `config` trước — nhờ vậy dữ liệu ghi `chrome` mà nhãn hiện
|
|
77
|
+
* ra `Chrome`, đúng cách shadcn dùng `formatter` của `LabelList`. Không có
|
|
78
|
+
* trong config thì in nguyên giá trị.
|
|
79
|
+
*/
|
|
80
|
+
export type ChartLabelKey = 'value' | 'name'
|
|
81
|
+
|
|
82
|
+
/** Prop dùng chung của mọi biểu đồ trục XY trong bộ này. */
|
|
83
|
+
export interface ChartBaseProps<
|
|
84
|
+
T extends Record<string, unknown>,
|
|
85
|
+
TConfig extends Record<string, ChartSeriesConfig> = ChartConfig,
|
|
86
|
+
> {
|
|
87
|
+
/** Dữ liệu nguồn, mỗi phần tử là một điểm trên trục hoành. */
|
|
88
|
+
data: T[]
|
|
89
|
+
/** Khoá dùng làm nhãn trục hoành, ví dụ `'month'`. */
|
|
90
|
+
index: Extract<keyof T, string>
|
|
91
|
+
/**
|
|
92
|
+
* Các chuỗi cần vẽ. Bỏ trống thì vẽ đúng những khoá có trong `config`, theo
|
|
93
|
+
* thứ tự khai báo — nên thường không phải truyền.
|
|
94
|
+
*/
|
|
95
|
+
categories?: Extract<keyof T, string>[]
|
|
96
|
+
/** Nhãn, màu và icon của từng chuỗi. */
|
|
97
|
+
config: TConfig
|
|
98
|
+
/** Lề trong của khung vẽ. */
|
|
99
|
+
margin?: ChartSpacing
|
|
100
|
+
/** Định dạng nhãn trục hoành. */
|
|
101
|
+
xFormatter?: ChartTickFormatter
|
|
102
|
+
/** Định dạng nhãn trục tung. */
|
|
103
|
+
yFormatter?: ChartTickFormatter
|
|
104
|
+
/** Định dạng giá trị trong tooltip. Mặc định là `toLocaleString('vi-VN')`. */
|
|
105
|
+
valueFormatter?: ChartValueFormatter
|
|
106
|
+
/** Tuỳ chọn hiển thị của tooltip: kiểu chỉ báo, ẩn nhãn, ẩn chỉ báo. */
|
|
107
|
+
tooltip?: ChartTooltipOptions
|
|
108
|
+
/** @default true */
|
|
109
|
+
showXAxis?: boolean
|
|
110
|
+
/** @default true */
|
|
111
|
+
showYAxis?: boolean
|
|
112
|
+
/** @default true */
|
|
113
|
+
showTooltip?: boolean
|
|
114
|
+
/** @default false — shadcn để legend tắt sẵn, bật khi có từ hai chuỗi trở lên. */
|
|
115
|
+
showLegend?: boolean
|
|
116
|
+
/** Đường kẻ ngang nền. @default true */
|
|
117
|
+
showGridLine?: boolean
|
|
118
|
+
/**
|
|
119
|
+
* Vạch dóng dọc và chấm bám trên chuỗi khi rê chuột.
|
|
120
|
+
*
|
|
121
|
+
* shadcn tắt nó ở 58/65 ô (`cursor={false}`) — nhưng đó là quy ước TRÌNH BÀY của
|
|
122
|
+
* trang gallery, không phải mặc định hợp lý cho một component: mặc định của
|
|
123
|
+
* Recharts cũng là bật, họ phải khai tắt từng ô. Giữ `true`, ai cần thì tắt.
|
|
124
|
+
* @default true
|
|
125
|
+
*/
|
|
126
|
+
showCursor?: boolean
|
|
127
|
+
/**
|
|
128
|
+
* Ghim tooltip mở sẵn ở điểm dữ liệu thứ mấy, không cần rê chuột.
|
|
129
|
+
*
|
|
130
|
+
* Dành cho trang tài liệu: cả chín biến thể ở trang Tooltips của shadcn đều
|
|
131
|
+
* khai `defaultIndex={1}`, vì tooltip là THỨ ĐANG ĐƯỢC TRƯNG BÀY — bắt người
|
|
132
|
+
* đọc rê chuột lên từng ô mới thấy thì xem như không trưng bày gì.
|
|
133
|
+
*
|
|
134
|
+
* Rê chuột lên vẫn đè được; bỏ chuột ra thì nó quay về điểm đã ghim.
|
|
135
|
+
*/
|
|
136
|
+
defaultIndex?: number
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Tuỳ chọn hiển thị của tooltip, theo đúng bộ prop của shadcn. */
|
|
140
|
+
export interface ChartTooltipOptions {
|
|
141
|
+
/** Kiểu chỉ báo màu đứng trước tên chuỗi. @default 'dot' */
|
|
142
|
+
indicator?: 'dot' | 'line' | 'dashed'
|
|
143
|
+
/** Bỏ dòng tiêu đề (nhãn trục hoành). */
|
|
144
|
+
hideLabel?: boolean
|
|
145
|
+
/** Bỏ hẳn ô/vạch màu, chỉ còn tên và số. */
|
|
146
|
+
hideIndicator?: boolean
|
|
147
|
+
/**
|
|
148
|
+
* Lấy dòng tiêu đề từ `config[labelKey].label` thay vì từ nhãn trục hoành.
|
|
149
|
+
*
|
|
150
|
+
* Khoá này KHÔNG phải một chuỗi số liệu — nó chỉ có `label`. Vì `categories`
|
|
151
|
+
* mặc định là mọi khoá của `config`, biểu đồ dùng nó phải khai `categories`
|
|
152
|
+
* tường minh, nếu không sẽ mọc thêm một chuỗi rỗng.
|
|
153
|
+
*/
|
|
154
|
+
labelKey?: string
|
|
155
|
+
/** Đổi cách viết dòng tiêu đề, ví dụ đổi 'January' thành 'Tháng 1'. */
|
|
156
|
+
labelFormatter?: (label: string) => string
|
|
157
|
+
/** Thêm một dòng tổng ở cuối. Chỉ có nghĩa khi tooltip liệt kê nhiều chuỗi. */
|
|
158
|
+
showTotal?: boolean
|
|
159
|
+
/** Nhãn của dòng tổng. @default 'Total' */
|
|
160
|
+
totalLabel?: string
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Một dòng trong tooltip, đã resolve xong nhãn và màu. */
|
|
164
|
+
export interface ChartTooltipRow {
|
|
165
|
+
key: string
|
|
166
|
+
label: string
|
|
167
|
+
color: string
|
|
168
|
+
/** Đã định dạng, dùng để hiển thị. */
|
|
169
|
+
value: string
|
|
170
|
+
/** Số thô, dùng để cộng dòng tổng. */
|
|
171
|
+
raw?: number
|
|
172
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { escapeHtml } from './tooltip'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Đẩy nhãn của `VisXYLabels` LỆCH khỏi điểm dữ liệu, bằng `<tspan>`.
|
|
5
|
+
*
|
|
6
|
+
* `VisXYLabels` KHÔNG có tuỳ chọn lệch theo pixel: `x`/`y` là toạ độ trong
|
|
7
|
+
* không gian dữ liệu, và chữ luôn nằm ĐÚNG TÂM điểm đó
|
|
8
|
+
* (`text-anchor: middle; dominant-baseline: central`, đặt bằng inline style nên
|
|
9
|
+
* CSS thường không đè được). Nhưng shadcn thì đặt nhãn cách cột 12px phía trên,
|
|
10
|
+
* hoặc 8px ngoài đầu cột ngang — tức phải lệch theo PIXEL, thứ không quy được
|
|
11
|
+
* về đơn vị dữ liệu nếu không biết thang đo.
|
|
12
|
+
*
|
|
13
|
+
* Lối ra nằm ở chỗ Unovis gán nội dung nhãn bằng `text.html(labelText)`, tức
|
|
14
|
+
* chuỗi trả về được nhét thẳng vào `<text>` dưới dạng markup. Ba điều khiến
|
|
15
|
+
* cách này chạy đúng, cả ba đều là SVG chuẩn chứ không phải mẹo:
|
|
16
|
+
*
|
|
17
|
+
* 1. `<tspan y="-12">` dời cả chữ lên 12px — đã ĐO: tâm chữ lệch đúng 12px, và
|
|
18
|
+
* `dominant-baseline: central` của thẻ cha vẫn được kế thừa.
|
|
19
|
+
* 2. `text-anchor` khai TRÊN CHÍNH tspan thắng giá trị kế thừa từ inline style
|
|
20
|
+
* của `<text>` — thuộc tính khai trực tiếp luôn hơn kế thừa.
|
|
21
|
+
* 3. Nhưng `text-anchor` chỉ có hiệu lực ở ĐẦU một text chunk, mà chunk mới chỉ
|
|
22
|
+
* mở ra khi có toạ độ TUYỆT ĐỐI. Vì vậy luôn phát cả `x` lẫn `y`, kể cả khi
|
|
23
|
+
* bằng 0 — thiếu `x` là anchor bị bỏ qua và chữ vẫn nằm giữa.
|
|
24
|
+
*
|
|
25
|
+
* Kèm một hệ quả phải nhớ: `getLabelRenderProps` ước lượng bề rộng nền theo
|
|
26
|
+
* `labelText.length`, mà chuỗi giờ có cả markup nên con số đó vô nghĩa. Không
|
|
27
|
+
* sao vì nền để `none`, NHƯNG nó làm phép gom cụm (`clustering`) tính sai —
|
|
28
|
+
* luôn đặt `:clustering="false"` ở mọi chỗ dùng hàm này.
|
|
29
|
+
*/
|
|
30
|
+
export interface XYLabelOffset {
|
|
31
|
+
/** Lệch ngang, pixel. Dương là sang phải. */
|
|
32
|
+
dx?: number
|
|
33
|
+
/** Lệch dọc, pixel. Âm là lên trên. */
|
|
34
|
+
dy?: number
|
|
35
|
+
/** Căn chữ so với điểm neo sau khi đã lệch. @default 'middle' */
|
|
36
|
+
anchor?: 'start' | 'middle' | 'end'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function xyLabelHtml(text: string, offset: XYLabelOffset = {}): string {
|
|
40
|
+
const { dx = 0, dy = 0, anchor = 'middle' } = offset
|
|
41
|
+
return `<tspan x="${dx}" y="${dy}" text-anchor="${anchor}">${escapeHtml(text)}</tspan>`
|
|
42
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vòng focus dùng chung cho control NHẬP LIỆU.
|
|
3
|
+
*
|
|
4
|
+
* Tách ra một chỗ vì trước đây mười ba file cùng chép một chuỗi class, và
|
|
5
|
+
* chúng đã trôi khỏi nhau: có chỗ `ring-offset-1`, có chỗ `ring-offset-2`, có
|
|
6
|
+
* chỗ quên hẳn. Sửa một kiểu focus mà phải mở mười ba file thì kiểu gì cũng sót.
|
|
7
|
+
*
|
|
8
|
+
* Hình dạng (xem `--focus-ring` trong `tokens.css` để biết vì sao hai lớp):
|
|
9
|
+
*
|
|
10
|
+
* | trạng thái | viền | bóng |
|
|
11
|
+
* |---|---|---|
|
|
12
|
+
* | thường | `--input` | `shadow-xs` |
|
|
13
|
+
* | focus | `--ring` | hai lớp, sắc `--ring` |
|
|
14
|
+
* | không hợp lệ | `--destructive` | **không** — chỉ đổi viền |
|
|
15
|
+
* | không hợp lệ + focus | `--destructive` | hai lớp, sắc `--destructive` |
|
|
16
|
+
*
|
|
17
|
+
* Dòng cuối bảng là chỗ dễ làm sai nhất. Người dùng chỉ yêu cầu "invalid thì
|
|
18
|
+
* đổi viền thôi", nhưng đó là nói về trạng thái **tĩnh**; bỏ luôn cả quầng lúc
|
|
19
|
+
* đang focus thì field sai lại thành field khó thấy con trỏ đang ở đâu nhất.
|
|
20
|
+
*
|
|
21
|
+
* Vì sao `data-[invalid]:focus-visible:*` chắc chắn thắng `focus-visible:*`:
|
|
22
|
+
* biến thể ghép sinh ra selector có thêm một attribute selector, tức
|
|
23
|
+
* specificity (0,3,0) so với (0,2,0). Nó thắng nhờ **specificity**, không phải
|
|
24
|
+
* nhờ thứ tự trong file — nên không phụ thuộc vào cách Tailwind sắp xếp.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/** Bỏ outline mặc định của trình duyệt. Luôn đi kèm một trong hai bộ dưới. */
|
|
28
|
+
const noOutline = 'focus-visible:outline-none'
|
|
29
|
+
|
|
30
|
+
/** Control mà chính nó nhận focus: input, textarea, trigger của select… */
|
|
31
|
+
export const focusRing = [
|
|
32
|
+
noOutline,
|
|
33
|
+
'focus-visible:border-ring focus-visible:shadow-focus',
|
|
34
|
+
].join(' ')
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Vỏ bọc quanh control thật: InputGroup, InputNumber, vùng thả của FileUpload.
|
|
38
|
+
* Focus nằm ở `<input>` bên trong nên vỏ phải bắt bằng `focus-within`, còn ô
|
|
39
|
+
* nhập bên trong thì bị lột sạch viền và vòng focus của nó.
|
|
40
|
+
*/
|
|
41
|
+
export const focusRingWithin = [
|
|
42
|
+
'focus-within:outline-none',
|
|
43
|
+
'focus-within:border-ring focus-within:shadow-focus',
|
|
44
|
+
].join(' ')
|
|
45
|
+
|
|
46
|
+
/** Trạng thái không hợp lệ, đọc từ `data-invalid` trên chính element. */
|
|
47
|
+
export const invalidRing = [
|
|
48
|
+
'data-[invalid]:border-destructive',
|
|
49
|
+
'data-[invalid]:focus-visible:border-destructive data-[invalid]:focus-visible:shadow-focus-invalid',
|
|
50
|
+
].join(' ')
|
|
51
|
+
|
|
52
|
+
/** Bản `focus-within` của `invalidRing`, cho vỏ bọc. */
|
|
53
|
+
export const invalidRingWithin = [
|
|
54
|
+
'data-[invalid]:border-destructive',
|
|
55
|
+
'data-[invalid]:focus-within:border-destructive data-[invalid]:focus-within:shadow-focus-invalid',
|
|
56
|
+
].join(' ')
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Cho control không đọc được `data-invalid` từ DOM mà phải nhận qua prop —
|
|
60
|
+
* chủ yếu là các wrapper của reka, nơi thuộc tính không xuống tới element
|
|
61
|
+
* đang vẽ viền.
|
|
62
|
+
*/
|
|
63
|
+
export const invalidRingStatic = [
|
|
64
|
+
'border-destructive',
|
|
65
|
+
'focus-visible:border-destructive focus-visible:shadow-focus-invalid',
|
|
66
|
+
].join(' ')
|