@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,32 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import type { InputGroupAddonVariants } from './index'
|
|
4
|
+
import { cn } from '../../lib/utils'
|
|
5
|
+
import { injectInputGroupContext, inputGroupAddonVariants } from './index'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(
|
|
8
|
+
defineProps<ClassProps & {
|
|
9
|
+
variant?: InputGroupAddonVariants['variant']
|
|
10
|
+
/** Đứng trước hay sau ô nhập — quyết định đường kẻ vẽ ở cạnh nào. */
|
|
11
|
+
align?: InputGroupAddonVariants['align']
|
|
12
|
+
}>(),
|
|
13
|
+
{ variant: 'text', align: 'start' },
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
const group = injectInputGroupContext(null)
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<div
|
|
21
|
+
:class="cn(
|
|
22
|
+
inputGroupAddonVariants({
|
|
23
|
+
variant: props.variant,
|
|
24
|
+
align: props.align,
|
|
25
|
+
size: group?.size.value,
|
|
26
|
+
}),
|
|
27
|
+
props.class,
|
|
28
|
+
)"
|
|
29
|
+
>
|
|
30
|
+
<slot />
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { cn } from '../../lib/utils'
|
|
5
|
+
import { injectInputGroupContext } from './index'
|
|
6
|
+
|
|
7
|
+
const props = defineProps<ClassProps & { disabled?: boolean }>()
|
|
8
|
+
|
|
9
|
+
const model = defineModel<string | number | null>()
|
|
10
|
+
|
|
11
|
+
const group = injectInputGroupContext(null)
|
|
12
|
+
|
|
13
|
+
const padding = computed(() => (group?.size.value === 'sm' ? 'px-2.5' : 'px-3'))
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<!--
|
|
18
|
+
Lột sạch viền, nền, bóng và vòng focus: nhóm bên ngoài đang giữ cả bốn thứ
|
|
19
|
+
đó. Giữ lại trên ô nhập là ra vòng focus lồng trong vòng focus.
|
|
20
|
+
-->
|
|
21
|
+
<input
|
|
22
|
+
v-model="model"
|
|
23
|
+
:disabled="props.disabled"
|
|
24
|
+
:class="cn(
|
|
25
|
+
'min-w-0 flex-1 border-0 bg-transparent text-inherit shadow-none outline-none',
|
|
26
|
+
'placeholder:text-muted-foreground',
|
|
27
|
+
'focus:outline-none focus-visible:outline-none focus-visible:ring-0',
|
|
28
|
+
'disabled:cursor-not-allowed',
|
|
29
|
+
padding,
|
|
30
|
+
props.class,
|
|
31
|
+
)"
|
|
32
|
+
>
|
|
33
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ComputedRef } from 'vue'
|
|
2
|
+
import type { InputGroupVariants } from '../../variants/components/input-group'
|
|
3
|
+
import { createContext } from 'reka-ui'
|
|
4
|
+
|
|
5
|
+
export { inputGroupAddonVariants, type InputGroupAddonVariants, inputGroupVariants, type InputGroupVariants } from '../../variants/components/input-group'
|
|
6
|
+
export { default as InputGroup } from './InputGroup.vue'
|
|
7
|
+
export { default as InputGroupAddon } from './InputGroupAddon.vue'
|
|
8
|
+
|
|
9
|
+
/** Cỡ khai một lần ở nhóm, ô nhập và phần phụ đọc lại. */
|
|
10
|
+
export interface InputGroupContext {
|
|
11
|
+
size: ComputedRef<InputGroupVariants['size']>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const [injectInputGroupContext, provideInputGroupContext]
|
|
15
|
+
= createContext<InputGroupContext>('InputGroup')
|
|
16
|
+
|
|
17
|
+
export { default as InputGroupInput } from './InputGroupInput.vue'
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { NumberFieldRootEmits, NumberFieldRootProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { NumberFieldDecrement, NumberFieldIncrement, NumberFieldInput, NumberFieldRoot, useForwardPropsEmits } from 'reka-ui'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { focusRingWithin, invalidRingWithin } from '../../lib/focus'
|
|
7
|
+
import { Minus, Plus } from '../../lib/icons'
|
|
8
|
+
import { cn } from '../../lib/utils'
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(
|
|
11
|
+
defineProps<NumberFieldRootProps & ClassProps & {
|
|
12
|
+
size?: 'sm' | 'default'
|
|
13
|
+
invalid?: boolean
|
|
14
|
+
/** Ẩn hai nút cộng trừ, chỉ còn ô nhập có định dạng số. */
|
|
15
|
+
hideStepper?: boolean
|
|
16
|
+
}>(),
|
|
17
|
+
{
|
|
18
|
+
size: 'default',
|
|
19
|
+
invalid: false,
|
|
20
|
+
hideStepper: false,
|
|
21
|
+
/**
|
|
22
|
+
* Mặc định `vi-VN`: dấu phân cách nghìn và dấu thập phân của tiếng Việt
|
|
23
|
+
* ngược với tiếng Anh (1.234,5 chứ không phải 1,234.5). Để trình duyệt tự
|
|
24
|
+
* đoán theo ngôn ngữ hệ thống thì cùng một màn hình có thể hiện hai kiểu.
|
|
25
|
+
*/
|
|
26
|
+
locale: 'vi-VN',
|
|
27
|
+
},
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
const emits = defineEmits<NumberFieldRootEmits>()
|
|
31
|
+
|
|
32
|
+
const delegated = computed(() => {
|
|
33
|
+
const { class: _class, size: _size, invalid: _invalid, hideStepper: _hideStepper, ...rest } = props
|
|
34
|
+
return rest
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const forwarded = useForwardPropsEmits(delegated, emits)
|
|
38
|
+
|
|
39
|
+
const height = computed(() => (props.size === 'sm' ? 'h-(--ui-field-height-sm)' : 'h-(--ui-field-height-md)'))
|
|
40
|
+
|
|
41
|
+
const stepper = cn(
|
|
42
|
+
'grid w-8 shrink-0 place-items-center text-muted-foreground transition-colors',
|
|
43
|
+
'hover:bg-accent hover:text-accent-foreground',
|
|
44
|
+
'disabled:pointer-events-none disabled:opacity-40',
|
|
45
|
+
'[&_svg]:size-3.5',
|
|
46
|
+
)
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<template>
|
|
50
|
+
<NumberFieldRoot
|
|
51
|
+
v-bind="forwarded"
|
|
52
|
+
:data-invalid="props.invalid || undefined"
|
|
53
|
+
:class="cn(
|
|
54
|
+
'flex w-full items-stretch overflow-hidden rounded-md border border-input bg-background shadow-xs transition-colors',
|
|
55
|
+
focusRingWithin,
|
|
56
|
+
'has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50',
|
|
57
|
+
invalidRingWithin,
|
|
58
|
+
height,
|
|
59
|
+
props.class,
|
|
60
|
+
)"
|
|
61
|
+
>
|
|
62
|
+
<!-- Nút giảm nằm bên TRÁI, tăng bên PHẢI: cùng thứ tự với trục số, nên
|
|
63
|
+
không phải nghĩ xem nút nào làm gì. -->
|
|
64
|
+
<NumberFieldDecrement :class="cn(stepper, 'border-r border-input', props.hideStepper && 'hidden')">
|
|
65
|
+
<Minus />
|
|
66
|
+
</NumberFieldDecrement>
|
|
67
|
+
|
|
68
|
+
<!-- Căn giữa khi có hai nút hai bên, căn trái khi không: số lơ lửng giữa một
|
|
69
|
+
ô trống trông như bị đặt nhầm chỗ. -->
|
|
70
|
+
<NumberFieldInput
|
|
71
|
+
:class="cn(
|
|
72
|
+
'min-w-0 flex-1 border-0 bg-transparent px-2.5 text-sm tabular-nums outline-none',
|
|
73
|
+
'placeholder:text-muted-foreground disabled:cursor-not-allowed',
|
|
74
|
+
props.hideStepper ? 'text-left' : 'text-center',
|
|
75
|
+
)"
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
<NumberFieldIncrement :class="cn(stepper, 'border-l border-input', props.hideStepper && 'hidden')">
|
|
79
|
+
<Plus />
|
|
80
|
+
</NumberFieldIncrement>
|
|
81
|
+
</NumberFieldRoot>
|
|
82
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as InputNumber } from './InputNumber.vue'
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { PinInputRootEmits, PinInputRootProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { PinInputInput, PinInputRoot, useForwardPropsEmits } from 'reka-ui'
|
|
5
|
+
import { computed, nextTick } from 'vue'
|
|
6
|
+
import { focusRing, invalidRingStatic } from '../../lib/focus'
|
|
7
|
+
import { cn } from '../../lib/utils'
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(
|
|
10
|
+
defineProps<Omit<PinInputRootProps, 'modelValue'> & ClassProps & {
|
|
11
|
+
/** Số ô. */
|
|
12
|
+
length?: number
|
|
13
|
+
/** Chèn dấu ngăn sau mỗi `groupSize` ô. Đặt 0 để không ngăn. */
|
|
14
|
+
groupSize?: number
|
|
15
|
+
invalid?: boolean
|
|
16
|
+
}>(),
|
|
17
|
+
{
|
|
18
|
+
length: 6,
|
|
19
|
+
groupSize: 0,
|
|
20
|
+
/**
|
|
21
|
+
* `otp` bật gợi ý mã một lần của trình duyệt: iOS và Chrome sẽ mời dán mã
|
|
22
|
+
* vừa nhận qua SMS. Đây là lý do chính để dùng component này thay vì sáu ô
|
|
23
|
+
* `<input>` viết tay.
|
|
24
|
+
*/
|
|
25
|
+
otp: true,
|
|
26
|
+
type: 'text',
|
|
27
|
+
invalid: false,
|
|
28
|
+
},
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const emits = defineEmits<PinInputRootEmits>()
|
|
32
|
+
|
|
33
|
+
const model = defineModel<string[]>({ default: () => [] })
|
|
34
|
+
|
|
35
|
+
const delegated = computed(() => {
|
|
36
|
+
const { class: _class, length: _length, groupSize: _groupSize, invalid: _invalid, ...rest } = props
|
|
37
|
+
return rest
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const forwarded = useForwardPropsEmits(delegated, emits)
|
|
41
|
+
|
|
42
|
+
const cells = computed(() => Array.from({ length: props.length }, (_, i) => i))
|
|
43
|
+
|
|
44
|
+
/** Sau ô thứ `groupSize`, `2*groupSize`… thì chèn dấu ngăn — trừ ô cuối cùng. */
|
|
45
|
+
function hasSeparatorAfter(index: number): boolean {
|
|
46
|
+
if (!props.groupSize)
|
|
47
|
+
return false
|
|
48
|
+
return (index + 1) % props.groupSize === 0 && index !== props.length - 1
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* ------------------------------------------------------------------------- *
|
|
52
|
+
* Dãy phải LUÔN DÀY: không có ô trống nằm giữa hai ô có giá trị.
|
|
53
|
+
*
|
|
54
|
+
* reka đá con trỏ về ô trống đầu tiên ở MỌI lần focus (`handleFocus` của
|
|
55
|
+
* `PinInputInput`), kể cả khi ô được bấm vào đang có chữ số. Để nguyên hành vi
|
|
56
|
+
* xoá của reka — xoá tại chỗ, chừa lại một lỗ — thì các ô sau lỗ đó không còn
|
|
57
|
+
* cách nào chạm tới: chuột, mũi tên và Tab đều bị đá ngược. Xem BUG-001.
|
|
58
|
+
*
|
|
59
|
+
* Không tắt được cái đá đó (`otp` gánh luôn `autocomplete="one-time-code"`),
|
|
60
|
+
* nên thay vào đó ta không bao giờ tạo ra lỗ: xoá thì dồn trái, gõ chèn thì dồn
|
|
61
|
+
* phải — coi sáu ô như một chuỗi ký tự, đúng cách `input-otp` của shadcn làm.
|
|
62
|
+
* ------------------------------------------------------------------------- */
|
|
63
|
+
|
|
64
|
+
/** Dãy hiện tại, đủ `length` phần tử, ô thiếu thành chuỗi rỗng. */
|
|
65
|
+
function currentValues(): string[] {
|
|
66
|
+
return Array.from({ length: props.length }, (_, i) => model.value[i] ?? '')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Ghi lại model, cắt đuôi rỗng cho khớp cách reka tự chuẩn hoá. */
|
|
70
|
+
function commit(values: string[]): void {
|
|
71
|
+
const next = values.slice(0, props.length)
|
|
72
|
+
while (next.length > 0 && next[next.length - 1] === '')
|
|
73
|
+
next.pop()
|
|
74
|
+
model.value = next
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function cellIndexOf(target: EventTarget | null): number {
|
|
78
|
+
const raw = (target as HTMLElement | null)?.dataset?.index
|
|
79
|
+
return raw === undefined ? -1 : Number(raw)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function focusCell(root: HTMLElement, index: number): void {
|
|
83
|
+
nextTick(() => {
|
|
84
|
+
root.querySelector<HTMLInputElement>(`input[data-index="${index}"]`)?.focus()
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Backspace / Delete: bỏ hẳn ô đó khỏi dãy rồi dồn phần đuôi lên một bước.
|
|
90
|
+
* Chặn ở pha capture nên handler của reka trên chính ô đó không chạy nữa.
|
|
91
|
+
*/
|
|
92
|
+
function handleKeydownCapture(event: KeyboardEvent): void {
|
|
93
|
+
if (event.key !== 'Backspace' && event.key !== 'Delete')
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
const root = event.currentTarget as HTMLElement
|
|
97
|
+
const index = cellIndexOf(event.target)
|
|
98
|
+
if (index < 0)
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
const values = currentValues()
|
|
102
|
+
let focusIndex = index
|
|
103
|
+
|
|
104
|
+
if (values[index] !== '') {
|
|
105
|
+
values.splice(index, 1)
|
|
106
|
+
}
|
|
107
|
+
else if (event.key === 'Backspace' && index > 0) {
|
|
108
|
+
// Dãy luôn dày nên ô trống chỉ nằm ở đuôi: lùi lại một ô và xoá ô đó.
|
|
109
|
+
values.splice(index - 1, 1)
|
|
110
|
+
focusIndex = index - 1
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Delete trên ô trống, hoặc Backspace ở ô đầu: không có gì để xoá.
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
event.preventDefault()
|
|
118
|
+
event.stopPropagation()
|
|
119
|
+
commit(values)
|
|
120
|
+
|
|
121
|
+
if (focusIndex !== index)
|
|
122
|
+
focusCell(root, focusIndex)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Gõ vào ô đang có chữ số khi dãy CHƯA đầy: chèn vào giữa và dồn phần đuôi
|
|
127
|
+
* sang phải, thay vì đè mất chữ số đang đứng ở đó.
|
|
128
|
+
*
|
|
129
|
+
* Dãy đã đầy thì để nguyên hành vi của reka (gõ đè lên đúng ô đó) — không còn
|
|
130
|
+
* chỗ mà dồn, và đè lên một ô là cách sửa một chữ số sai nhanh nhất.
|
|
131
|
+
*/
|
|
132
|
+
function handleBeforeInputCapture(event: InputEvent): void {
|
|
133
|
+
if (event.inputType !== 'insertText')
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
const char = event.data
|
|
137
|
+
if (!char || char.length !== 1)
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
const root = event.currentTarget as HTMLElement
|
|
141
|
+
const index = cellIndexOf(event.target)
|
|
142
|
+
if (index < 0)
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
const values = currentValues()
|
|
146
|
+
// Ô trống thì không có gì phải dồn; dãy đầy thì không còn chỗ để dồn.
|
|
147
|
+
if (values[index] === '' || values.every(value => value !== ''))
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
event.preventDefault()
|
|
151
|
+
event.stopPropagation()
|
|
152
|
+
values.splice(index, 0, char)
|
|
153
|
+
commit(values)
|
|
154
|
+
focusCell(root, Math.min(index + 1, props.length - 1))
|
|
155
|
+
}
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
<template>
|
|
159
|
+
<PinInputRoot
|
|
160
|
+
v-model="model"
|
|
161
|
+
v-bind="forwarded"
|
|
162
|
+
:class="cn('flex items-center gap-2', props.class)"
|
|
163
|
+
@keydown.capture="handleKeydownCapture"
|
|
164
|
+
@beforeinput.capture="handleBeforeInputCapture"
|
|
165
|
+
>
|
|
166
|
+
<template v-for="index in cells" :key="index">
|
|
167
|
+
<PinInputInput
|
|
168
|
+
:index="index"
|
|
169
|
+
:data-index="index"
|
|
170
|
+
:class="cn(
|
|
171
|
+
'size-10 rounded-md border border-input bg-background text-center text-base font-medium shadow-xs transition-colors',
|
|
172
|
+
// z-(--z-raised) để quầng focus nổi lên trên ô kế bên, không bị nó cắt mất.
|
|
173
|
+
'focus-visible:z-(--z-raised)',
|
|
174
|
+
focusRing,
|
|
175
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
176
|
+
props.invalid && invalidRingStatic,
|
|
177
|
+
)"
|
|
178
|
+
/>
|
|
179
|
+
<span
|
|
180
|
+
v-if="hasSeparatorAfter(index)"
|
|
181
|
+
class="text-muted-foreground"
|
|
182
|
+
aria-hidden="true"
|
|
183
|
+
>–</span>
|
|
184
|
+
</template>
|
|
185
|
+
</PinInputRoot>
|
|
186
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as InputOtp } from './InputOtp.vue'
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import { computed, ref } from 'vue'
|
|
4
|
+
import { useI18n } from '../../composables/useI18n'
|
|
5
|
+
import { Eye, EyeOff } from '../../lib/icons'
|
|
6
|
+
import { cn } from '../../lib/utils'
|
|
7
|
+
import Button from '../button/Button.vue'
|
|
8
|
+
import Input from '../input/Input.vue'
|
|
9
|
+
import { scoreOf, STRENGTH_LEVELS } from './index'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Gốc là một `<div>`, nên thuộc tính rơi tự do phải chuyển tay xuống `Input` —
|
|
13
|
+
* cùng lý do đã ghi ở `Input.vue`: `id` dính lên div thì `<label for>` mất đích.
|
|
14
|
+
*/
|
|
15
|
+
defineOptions({ inheritAttrs: false })
|
|
16
|
+
|
|
17
|
+
const props = withDefaults(
|
|
18
|
+
defineProps<ClassProps & {
|
|
19
|
+
/** Nút con mắt. Tắt đi khi ô nằm trong một luồng không cho phép lộ giá trị. */
|
|
20
|
+
toggle?: boolean
|
|
21
|
+
/** Thanh đo độ mạnh. Chỉ bật cho ô ĐẶT mật khẩu, không bật cho ô đăng nhập. */
|
|
22
|
+
strength?: boolean
|
|
23
|
+
invalid?: boolean
|
|
24
|
+
size?: 'sm' | 'default'
|
|
25
|
+
/**
|
|
26
|
+
* Nhãn trợ năng cho nút con mắt ở hai trạng thái. Bỏ trống thì lấy chuỗi
|
|
27
|
+
* của thư viện — đừng đặt default ở đây, chuỗi trong `withDefaults` tính
|
|
28
|
+
* một lần lúc dựng component nên nó đóng băng ở một ngôn ngữ.
|
|
29
|
+
*/
|
|
30
|
+
showLabel?: string
|
|
31
|
+
hideLabel?: string
|
|
32
|
+
}>(),
|
|
33
|
+
{ toggle: true, strength: false, size: 'default' },
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const model = defineModel<string>({ default: '' })
|
|
37
|
+
|
|
38
|
+
const { t } = useI18n()
|
|
39
|
+
|
|
40
|
+
const revealed = ref(false)
|
|
41
|
+
|
|
42
|
+
const toggleLabel = computed(() =>
|
|
43
|
+
revealed.value
|
|
44
|
+
? props.hideLabel ?? t('ui.inputPassword.hide')
|
|
45
|
+
: props.showLabel ?? t('ui.inputPassword.show'),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
const score = computed(() => (props.strength ? scoreOf(model.value) : 0))
|
|
49
|
+
const level = computed(() => STRENGTH_LEVELS[score.value])
|
|
50
|
+
|
|
51
|
+
/** Chừa chỗ cho nút, nếu không ký tự cuối chui xuống dưới nó. */
|
|
52
|
+
const inputClass = computed(() =>
|
|
53
|
+
props.toggle ? (props.size === 'sm' ? 'pr-8' : 'pr-9') : undefined,
|
|
54
|
+
)
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template>
|
|
58
|
+
<div :class="cn('w-full', props.class)">
|
|
59
|
+
<div class="relative">
|
|
60
|
+
<Input
|
|
61
|
+
v-bind="$attrs"
|
|
62
|
+
v-model="model"
|
|
63
|
+
:type="revealed ? 'text' : 'password'"
|
|
64
|
+
:size="props.size"
|
|
65
|
+
:invalid="props.invalid"
|
|
66
|
+
:class="inputClass"
|
|
67
|
+
/>
|
|
68
|
+
|
|
69
|
+
<!--
|
|
70
|
+
`type="button"` là bắt buộc, không phải cho gọn.
|
|
71
|
+
|
|
72
|
+
Nút trần trong một `<form>` mặc định là `submit`, nên bấm vào con mắt
|
|
73
|
+
để đọc lại thứ mình vừa gõ sẽ GỬI luôn cái form. Không lỗi, không cảnh
|
|
74
|
+
báo — chỉ là biểu mẫu tự nộp.
|
|
75
|
+
-->
|
|
76
|
+
<Button
|
|
77
|
+
v-if="props.toggle"
|
|
78
|
+
type="button"
|
|
79
|
+
variant="ghost"
|
|
80
|
+
:size="props.size === 'sm' ? 'icon-xs' : 'icon-sm'"
|
|
81
|
+
:aria-label="toggleLabel"
|
|
82
|
+
:class="cn(
|
|
83
|
+
'absolute top-1/2 -translate-y-1/2 text-muted-foreground',
|
|
84
|
+
props.size === 'sm' ? 'right-1' : 'right-1',
|
|
85
|
+
)"
|
|
86
|
+
@click="revealed = !revealed"
|
|
87
|
+
>
|
|
88
|
+
<EyeOff v-if="revealed" aria-hidden="true" />
|
|
89
|
+
<Eye v-else aria-hidden="true" />
|
|
90
|
+
</Button>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<!--
|
|
94
|
+
Thanh đo nằm trong vùng live, nhưng đọc ra là CHỮ chứ không phải con số.
|
|
95
|
+
Bốn ô màu thì trình đọc màn hình không thấy gì; và vì nhãn chỉ đổi khi
|
|
96
|
+
qua một nấc nên nó không lải nhải theo từng phím gõ.
|
|
97
|
+
-->
|
|
98
|
+
<div v-if="props.strength" class="mt-2">
|
|
99
|
+
<div class="flex gap-1" aria-hidden="true">
|
|
100
|
+
<span
|
|
101
|
+
v-for="step in STRENGTH_LEVELS.length - 1"
|
|
102
|
+
:key="step"
|
|
103
|
+
:class="cn(
|
|
104
|
+
'h-1 flex-1 rounded-full transition-colors',
|
|
105
|
+
step <= score ? level.bar : 'bg-muted',
|
|
106
|
+
)"
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
<p role="status" aria-live="polite" class="mt-1.5 text-xs text-muted-foreground">
|
|
110
|
+
<template v-if="score > 0">
|
|
111
|
+
{{ t('ui.inputPassword.strength') }}: <span :class="level.text">{{ t(level.key) }}</span>
|
|
112
|
+
</template>
|
|
113
|
+
</p>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</template>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Thang đo lên `src/variants` vì `STRENGTH_LEVELS` là một bảng class, và
|
|
2
|
+
// bản React đọc cùng bảng đó. Re-export để chỗ gọi không phải sửa import.
|
|
3
|
+
export { scoreOf, STRENGTH_LEVELS, type StrengthLevel } from '../../variants/components/input-password'
|
|
4
|
+
|
|
5
|
+
export { default as InputPassword } from './InputPassword.vue'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TagsInputRootEmits, TagsInputRootProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { TagsInputRoot, useForwardPropsEmits } from 'reka-ui'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { focusRingWithin, invalidRingWithin } from '../../lib/focus'
|
|
7
|
+
import { cn } from '../../lib/utils'
|
|
8
|
+
|
|
9
|
+
const props = defineProps<TagsInputRootProps & ClassProps>()
|
|
10
|
+
const emits = defineEmits<TagsInputRootEmits>()
|
|
11
|
+
|
|
12
|
+
const delegated = computed(() => {
|
|
13
|
+
const { class: _class, ...rest } = props
|
|
14
|
+
return rest
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const forwarded = useForwardPropsEmits(delegated, emits)
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<!--
|
|
22
|
+
Vỏ bọc bắt focus bằng `focus-within`, đúng khuôn của InputGroup và
|
|
23
|
+
InputNumber: ô nhập thật nằm bên trong nên chính nó bị lột sạch viền và vòng
|
|
24
|
+
focus, còn cái vỏ mới vẽ ra chúng. Để cả hai thì ra vòng focus lồng vòng focus.
|
|
25
|
+
-->
|
|
26
|
+
<TagsInputRoot
|
|
27
|
+
v-bind="forwarded"
|
|
28
|
+
:class="cn(
|
|
29
|
+
'flex w-full flex-wrap items-center gap-1.5 rounded-md border border-input bg-background px-2 py-1.5 shadow-xs transition-colors',
|
|
30
|
+
focusRingWithin,
|
|
31
|
+
invalidRingWithin,
|
|
32
|
+
'has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50',
|
|
33
|
+
props.class,
|
|
34
|
+
)"
|
|
35
|
+
>
|
|
36
|
+
<slot />
|
|
37
|
+
</TagsInputRoot>
|
|
38
|
+
</template>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TagsInputClearProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { TagsInputClear } from 'reka-ui'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { useI18n } from '../../composables/useI18n'
|
|
7
|
+
import { X } from '../../lib/icons'
|
|
8
|
+
import { cn } from '../../lib/utils'
|
|
9
|
+
|
|
10
|
+
const props = defineProps<TagsInputClearProps & ClassProps & {
|
|
11
|
+
/** Nhãn trợ năng. Bỏ trống thì lấy chuỗi mặc định của thư viện. */
|
|
12
|
+
label?: string
|
|
13
|
+
}>()
|
|
14
|
+
|
|
15
|
+
const { t } = useI18n()
|
|
16
|
+
|
|
17
|
+
const label = computed(() => props.label ?? t('ui.inputTags.clear'))
|
|
18
|
+
|
|
19
|
+
const delegated = computed(() => {
|
|
20
|
+
const { class: _class, label: _label, ...rest } = props
|
|
21
|
+
return rest
|
|
22
|
+
})
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<TagsInputClear
|
|
27
|
+
v-bind="delegated"
|
|
28
|
+
:aria-label="label"
|
|
29
|
+
:class="cn(
|
|
30
|
+
'grid size-5 shrink-0 place-items-center rounded-sm text-muted-foreground transition-colors',
|
|
31
|
+
'hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
32
|
+
props.class,
|
|
33
|
+
)"
|
|
34
|
+
>
|
|
35
|
+
<slot>
|
|
36
|
+
<X class="size-3.5" />
|
|
37
|
+
</slot>
|
|
38
|
+
</TagsInputClear>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TagsInputInputProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { TagsInputInput } from 'reka-ui'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { cn } from '../../lib/utils'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<TagsInputInputProps & ClassProps>()
|
|
9
|
+
|
|
10
|
+
const delegated = computed(() => {
|
|
11
|
+
const { class: _class, ...rest } = props
|
|
12
|
+
return rest
|
|
13
|
+
})
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<!-- Ô nhập bị lột sạch viền và vòng focus: cái vỏ đã vẽ chúng rồi.
|
|
18
|
+
`min-w-24` để nó không co về 0 khi hàng chip đã đầy. -->
|
|
19
|
+
<TagsInputInput
|
|
20
|
+
v-bind="delegated"
|
|
21
|
+
:class="cn(
|
|
22
|
+
'h-6 min-w-24 flex-1 border-0 bg-transparent text-sm outline-none placeholder:text-muted-foreground',
|
|
23
|
+
'disabled:cursor-not-allowed',
|
|
24
|
+
props.class,
|
|
25
|
+
)"
|
|
26
|
+
/>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TagsInputItemProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { TagsInputItem } from 'reka-ui'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { cn } from '../../lib/utils'
|
|
7
|
+
import { badgeVariants } from '../badge'
|
|
8
|
+
import { chipVariants } from '../chip'
|
|
9
|
+
|
|
10
|
+
const props = defineProps<TagsInputItemProps & ClassProps>()
|
|
11
|
+
|
|
12
|
+
const delegated = computed(() => {
|
|
13
|
+
const { class: _class, ...rest } = props
|
|
14
|
+
return rest
|
|
15
|
+
})
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<!--
|
|
20
|
+
Dáng lấy thẳng từ `Chip` — cùng hai bộ variant, không chép lại chuỗi class.
|
|
21
|
+
Không bọc chính `Chip` vào đây vì nút gỡ của nó và `TagsInputItemDelete` của
|
|
22
|
+
reka sẽ tranh nhau làm một việc, mà bản của reka mới là bản biết cách xoá
|
|
23
|
+
khỏi model và trả focus về ô nhập.
|
|
24
|
+
|
|
25
|
+
`data-state=active` là dòng mà bàn phím đang chọn (mũi tên trái/phải) —
|
|
26
|
+
trạng thái reka có còn Chip thì không.
|
|
27
|
+
-->
|
|
28
|
+
<TagsInputItem
|
|
29
|
+
v-bind="delegated"
|
|
30
|
+
:class="cn(
|
|
31
|
+
badgeVariants({ variant: 'secondary' }),
|
|
32
|
+
chipVariants({ size: 'sm', removable: true }),
|
|
33
|
+
'data-[state=active]:ring-2 data-[state=active]:ring-ring',
|
|
34
|
+
'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
35
|
+
props.class,
|
|
36
|
+
)"
|
|
37
|
+
>
|
|
38
|
+
<slot />
|
|
39
|
+
</TagsInputItem>
|
|
40
|
+
</template>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TagsInputItemDeleteProps } from 'reka-ui'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { TagsInputItemDelete } from 'reka-ui'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { X } from '../../lib/icons'
|
|
7
|
+
import { cn } from '../../lib/utils'
|
|
8
|
+
|
|
9
|
+
const props = defineProps<TagsInputItemDeleteProps & ClassProps>()
|
|
10
|
+
|
|
11
|
+
const delegated = computed(() => {
|
|
12
|
+
const { class: _class, ...rest } = props
|
|
13
|
+
return rest
|
|
14
|
+
})
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<!-- `currentColor` để nút hợp với mọi variant của chip, kể cả nền đặc lẫn
|
|
19
|
+
nền soft — cùng cách `Chip` xử lý. -->
|
|
20
|
+
<TagsInputItemDelete
|
|
21
|
+
v-bind="delegated"
|
|
22
|
+
:class="cn(
|
|
23
|
+
'grid size-4 shrink-0 place-items-center rounded-full text-current opacity-60 transition-opacity',
|
|
24
|
+
'hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current',
|
|
25
|
+
props.class,
|
|
26
|
+
)"
|
|
27
|
+
>
|
|
28
|
+
<slot>
|
|
29
|
+
<X class="size-3" />
|
|
30
|
+
</slot>
|
|
31
|
+
</TagsInputItemDelete>
|
|
32
|
+
</template>
|