@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,233 @@
|
|
|
1
|
+
import type { LocaleId, LocaleState } from './registry.js'
|
|
2
|
+
import {
|
|
3
|
+
defaults,
|
|
4
|
+
domTargets,
|
|
5
|
+
isLocaleId,
|
|
6
|
+
localeTag,
|
|
7
|
+
storageKeys,
|
|
8
|
+
} from './registry.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Locale controller thuần TypeScript — không Vue, không React, không Angular.
|
|
12
|
+
*
|
|
13
|
+
* Cùng khuôn với `themeController` của `@vestow/theme`, và vì đúng một lý do:
|
|
14
|
+
* phần dễ sai (đọc localStorage, đoán ngôn ngữ trình duyệt, ghi `<html lang>`,
|
|
15
|
+
* đồng bộ giữa các tab) phải nằm ở MỘT chỗ, để woodblock-react / woodblock-angular
|
|
16
|
+
* sau này không mỗi bên viết lại một kiểu rồi lệch nhau.
|
|
17
|
+
*
|
|
18
|
+
* KHÔNG chạm `document` ở tầng module: package phải import được trong SSR. Mọi
|
|
19
|
+
* thứ đụng DOM nằm trong `init()`.
|
|
20
|
+
*
|
|
21
|
+
* Ghi chú về đuôi `.js` trong import: TypeScript giữ nguyên specifier khi emit,
|
|
22
|
+
* nên viết `./registry` sẽ ra file ESM mà Node không resolve được.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** Một từ điển phẳng. Khoá lồng nhau viết bằng dấu chấm: `'table.empty'`. */
|
|
26
|
+
export type Messages = Record<string, string>
|
|
27
|
+
|
|
28
|
+
/** Giá trị chèn vào chỗ `{name}` trong câu. */
|
|
29
|
+
export type MessageParams = Record<string, string | number>
|
|
30
|
+
|
|
31
|
+
export interface I18nController {
|
|
32
|
+
/** Ảnh chụp trạng thái hiện tại. Không reactive — dùng `subscribe`. */
|
|
33
|
+
getState: () => LocaleState
|
|
34
|
+
/**
|
|
35
|
+
* Đăng ký lắng nghe. Gọi ngay một lần với trạng thái hiện tại, nên vỏ bọc
|
|
36
|
+
* framework không cần đọc riêng lúc khởi tạo. Trả về hàm huỷ đăng ký.
|
|
37
|
+
*/
|
|
38
|
+
subscribe: (listener: (state: LocaleState) => void) => () => void
|
|
39
|
+
/**
|
|
40
|
+
* Đọc localStorage (rồi tới ngôn ngữ trình duyệt), ghi `<html lang>`, bắt đầu
|
|
41
|
+
* theo dõi `storage`. Gọi bao nhiêu lần cũng được.
|
|
42
|
+
* Trả về hàm dọn dẹp (chỉ có tác dụng ở lần gọi đầu).
|
|
43
|
+
*/
|
|
44
|
+
init: () => () => void
|
|
45
|
+
setLocale: (value: LocaleId) => void
|
|
46
|
+
/**
|
|
47
|
+
* Nạp thêm từ điển cho một ngôn ngữ. GỘP vào phần đã có chứ không thay thế.
|
|
48
|
+
*
|
|
49
|
+
* Đây là điểm mấu chốt của thiết kế: `packages/ui` tự nạp bộ chuỗi của nó,
|
|
50
|
+
* app tự nạp bộ của app. Nhờ vậy thư viện mang theo bản dịch của chính mình
|
|
51
|
+
* mà không cần biết app là ai — nó không được phép phụ thuộc vào app.
|
|
52
|
+
*/
|
|
53
|
+
addMessages: (locale: LocaleId, messages: Messages) => void
|
|
54
|
+
/** Dịch một khoá. Xem `t` bên dưới về chuỗi rơi lui. */
|
|
55
|
+
t: (key: string, params?: MessageParams) => string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function noop(): void {}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Thay `{name}` bằng giá trị, và chọn dạng số ít / số nhiều.
|
|
62
|
+
*
|
|
63
|
+
* Số nhiều tách bằng `|`: `'1 dòng|{count} dòng'`. Không có `|` thì dùng chung
|
|
64
|
+
* một dạng cho mọi số — đủ cho tiếng Việt, vốn không biến đổi theo số lượng.
|
|
65
|
+
* Tiếng Anh chỉ cần phân biệt 1 với phần còn lại nên hai dạng là đủ; ngôn ngữ
|
|
66
|
+
* nào cần nhiều hơn (Ba Lan, Ả Rập) thì phải đổi chỗ này — nhưng đừng đổi trước
|
|
67
|
+
* khi thật sự thêm ngôn ngữ đó.
|
|
68
|
+
*/
|
|
69
|
+
function format(template: string, params?: MessageParams): string {
|
|
70
|
+
let text = template
|
|
71
|
+
|
|
72
|
+
if (text.includes('|')) {
|
|
73
|
+
const forms = text.split('|')
|
|
74
|
+
const count = Number(params?.count)
|
|
75
|
+
text = (Number.isFinite(count) && count === 1 ? forms[0] : forms[1]) ?? forms[0] ?? ''
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (params === undefined)
|
|
79
|
+
return text
|
|
80
|
+
|
|
81
|
+
return text.replace(/\{(\w+)\}/g, (whole, name: string) => {
|
|
82
|
+
const value = params[name]
|
|
83
|
+
return value === undefined ? whole : String(value)
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function createI18nController(): I18nController {
|
|
88
|
+
let locale: LocaleId = defaults.locale
|
|
89
|
+
let started = false
|
|
90
|
+
|
|
91
|
+
const dictionaries = new Map<LocaleId, Messages>()
|
|
92
|
+
const listeners = new Set<(state: LocaleState) => void>()
|
|
93
|
+
|
|
94
|
+
const getState = (): LocaleState => ({ locale, tag: localeTag(locale) })
|
|
95
|
+
|
|
96
|
+
function notify(): void {
|
|
97
|
+
const state = getState()
|
|
98
|
+
for (const listener of listeners)
|
|
99
|
+
listener(state)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Ghi locale lên `<html lang>`. Gọi lại được, không cộng dồn gì.
|
|
104
|
+
*
|
|
105
|
+
* Ghi thẻ BCP 47 đầy đủ (`vi-VN`) chứ không phải id ngắn: trình đọc màn hình
|
|
106
|
+
* chọn giọng theo thẻ đó. Đặt sai thì tiếng Việt bị đọc bằng giọng Anh —
|
|
107
|
+
* `lang` không phải thứ trang trí.
|
|
108
|
+
*/
|
|
109
|
+
function applyToDocument(): void {
|
|
110
|
+
if (typeof document === 'undefined')
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
document.documentElement.setAttribute(domTargets.langAttribute, localeTag(locale))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Chưa có lựa chọn đã lưu thì đoán theo trình duyệt.
|
|
118
|
+
*
|
|
119
|
+
* So bằng TIỀN TỐ chứ không so bằng nhau: `navigator.language` hay trả về
|
|
120
|
+
* `en-GB`, `vi-VN`, `en-AU`… nên `=== 'en'` gần như không bao giờ khớp.
|
|
121
|
+
*/
|
|
122
|
+
function guessFromBrowser(): LocaleId {
|
|
123
|
+
if (typeof navigator === 'undefined')
|
|
124
|
+
return defaults.locale
|
|
125
|
+
|
|
126
|
+
const tags = navigator.languages ?? [navigator.language]
|
|
127
|
+
for (const tag of tags) {
|
|
128
|
+
const base = tag.toLowerCase().split('-')[0]
|
|
129
|
+
if (isLocaleId(base))
|
|
130
|
+
return base
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return defaults.locale
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function readStored(): LocaleId {
|
|
137
|
+
try {
|
|
138
|
+
const raw = localStorage.getItem(storageKeys.locale)
|
|
139
|
+
if (raw !== null && isLocaleId(raw))
|
|
140
|
+
return raw
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// localStorage có thể bị chặn (private mode, policy) -> đoán tiếp bên dưới.
|
|
144
|
+
}
|
|
145
|
+
return guessFromBrowser()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function init(): () => void {
|
|
149
|
+
if (started || typeof window === 'undefined')
|
|
150
|
+
return noop
|
|
151
|
+
|
|
152
|
+
started = true
|
|
153
|
+
locale = readStored()
|
|
154
|
+
|
|
155
|
+
/*
|
|
156
|
+
* Đồng bộ giữa các document CÙNG ORIGIN — tab thứ hai, và iframe xem trước
|
|
157
|
+
* của trang Blocks. `storage` chỉ phát ở document KHÁC document vừa ghi nên
|
|
158
|
+
* không có vòng lặp; bên ghi đã tự áp trong `setLocale`.
|
|
159
|
+
*/
|
|
160
|
+
const onStorage = (event: StorageEvent): void => {
|
|
161
|
+
if (event.key !== null && event.key !== storageKeys.locale)
|
|
162
|
+
return
|
|
163
|
+
|
|
164
|
+
locale = readStored()
|
|
165
|
+
applyToDocument()
|
|
166
|
+
notify()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
window.addEventListener('storage', onStorage)
|
|
170
|
+
|
|
171
|
+
applyToDocument()
|
|
172
|
+
notify()
|
|
173
|
+
|
|
174
|
+
return () => {
|
|
175
|
+
window.removeEventListener('storage', onStorage)
|
|
176
|
+
started = false
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function setLocale(value: LocaleId): void {
|
|
181
|
+
locale = value
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
localStorage.setItem(storageKeys.locale, value)
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// Không lưu được thì chỉ mất lựa chọn sau khi tải lại trang.
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
applyToDocument()
|
|
191
|
+
notify()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function addMessages(target: LocaleId, messages: Messages): void {
|
|
195
|
+
dictionaries.set(target, { ...dictionaries.get(target), ...messages })
|
|
196
|
+
// Từ điển nạp muộn (lazy chunk) vẫn phải làm giao diện vẽ lại.
|
|
197
|
+
notify()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Chuỗi rơi lui: ngôn ngữ đang bật → ngôn ngữ mặc định → CHÍNH KHOÁ ĐÓ.
|
|
202
|
+
*
|
|
203
|
+
* Trả về khoá chứ không trả chuỗi rỗng là cố ý: chỗ thiếu bản dịch hiện ra
|
|
204
|
+
* `orders.title` — xấu, nhưng nhìn là biết thiếu gì và sửa ở đâu. Chuỗi rỗng
|
|
205
|
+
* thì giao diện chỉ đơn giản là mất chữ, và không ai phát hiện.
|
|
206
|
+
*/
|
|
207
|
+
function t(key: string, params?: MessageParams): string {
|
|
208
|
+
const template = dictionaries.get(locale)?.[key]
|
|
209
|
+
?? dictionaries.get(defaults.locale)?.[key]
|
|
210
|
+
?? key
|
|
211
|
+
|
|
212
|
+
return format(template, params)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function subscribe(listener: (state: LocaleState) => void): () => void {
|
|
216
|
+
listeners.add(listener)
|
|
217
|
+
listener(getState())
|
|
218
|
+
return () => {
|
|
219
|
+
listeners.delete(listener)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { getState, subscribe, init, setLocale, addMessages, t }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Instance dùng chung cho cả app.
|
|
228
|
+
*
|
|
229
|
+
* `<html lang>` chỉ có một, và từ điển phải gom về một chỗ để `packages/ui` và
|
|
230
|
+
* app cùng đóng góp vào — hai controller song song thì thư viện dịch được còn
|
|
231
|
+
* app thì không. `createI18nController` để dành cho test.
|
|
232
|
+
*/
|
|
233
|
+
export const i18nController: I18nController = createI18nController()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Messages } from './controller.js'
|
|
2
|
+
import type { LocaleId } from './registry.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bộ gom từ điển theo chủ đề, dùng chung cho thư viện lẫn app của cả hai cây.
|
|
6
|
+
*
|
|
7
|
+
* `en` khai kiểu theo `vi`, nên **thiếu một khoá là `tsc` báo ngay**. Đây là chỗ
|
|
8
|
+
* bịt lỗ hổng thật: thêm chuỗi vào tiếng Việt mà quên tiếng Anh thì không có gì
|
|
9
|
+
* hỏng, giao diện chỉ âm thầm rơi lui về tiếng Việt và không ai phát hiện.
|
|
10
|
+
*
|
|
11
|
+
* Chiều ngược lại (`en` thừa khoá) không bắt được — TypeScript chỉ kiểm thừa với
|
|
12
|
+
* object literal, mà đây là JSON import. Khoá thừa thì vô hại: không ai đọc tới.
|
|
13
|
+
*
|
|
14
|
+
* Hàm này từng được **chép tay ở hai chỗ** (`packages/ui` và app kit), và bản
|
|
15
|
+
* React sẽ thành bản thứ ba rồi thứ tư. Sáu dòng thân hàm thì chép cũng được,
|
|
16
|
+
* nhưng mẹo kiểu `Record<keyof T, string>` mới là thứ đang giữ hai ngôn ngữ khỏi
|
|
17
|
+
* lệch — mất nó ở một bản là mất phép kiểm ở đó mà không có gì báo.
|
|
18
|
+
*/
|
|
19
|
+
export interface Dictionary {
|
|
20
|
+
/**
|
|
21
|
+
* Ghép một chủ đề vào cả hai từ điển. **Tên file chính là tiền tố khoá**:
|
|
22
|
+
* `t('ui.table.empty')` nằm ở `vi/ui.json` dưới khoá `table.empty`.
|
|
23
|
+
*/
|
|
24
|
+
topic: <T extends Messages>(prefix: string, viFile: T, enFile: Record<keyof T, string>) => void
|
|
25
|
+
/** Từ điển đã ghép. Đọc SAU khi gọi hết `topic` — hai object được ghi tại chỗ. */
|
|
26
|
+
messages: Record<LocaleId, Messages>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createDictionary(): Dictionary {
|
|
30
|
+
const vi: Messages = {}
|
|
31
|
+
const en: Messages = {}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
topic(prefix, viFile, enFile) {
|
|
35
|
+
for (const [key, value] of Object.entries(viFile))
|
|
36
|
+
vi[`${prefix}.${key}`] = value
|
|
37
|
+
|
|
38
|
+
for (const [key, value] of Object.entries(enFile))
|
|
39
|
+
en[`${prefix}.${key}`] = value
|
|
40
|
+
},
|
|
41
|
+
messages: { vi, en },
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `src/i18n` — locale controller dùng chung cho mọi framework.
|
|
3
|
+
*
|
|
4
|
+
* Cố tình KHÔNG dùng `vue-i18n`: bộ chuỗi của thư viện phải đi theo chính thư
|
|
5
|
+
* viện, mà `packages/ui` thì không được kéo thêm một runtime i18n vào mọi app
|
|
6
|
+
* dùng nó. Cái giá là không có message compiler và không có bộ luật số nhiều
|
|
7
|
+
* đầy đủ — chấp nhận được, vì tiếng Việt không biến đổi theo số lượng, tiếng
|
|
8
|
+
* Anh chỉ cần phân biệt 1 với phần còn lại, còn ngày và số thì đã đi qua `Intl`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type { I18nController, MessageParams, Messages } from './controller.js'
|
|
12
|
+
|
|
13
|
+
export { createI18nController, i18nController } from './controller.js'
|
|
14
|
+
|
|
15
|
+
export type { Dictionary } from './dictionary.js'
|
|
16
|
+
|
|
17
|
+
export { createDictionary } from './dictionary.js'
|
|
18
|
+
|
|
19
|
+
export type { LocaleId, LocaleOption, LocaleState } from './registry.js'
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
defaults,
|
|
23
|
+
domTargets,
|
|
24
|
+
isLocaleId,
|
|
25
|
+
locales,
|
|
26
|
+
localeTag,
|
|
27
|
+
storageKeys,
|
|
28
|
+
} from './registry.js'
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Bộ chuỗi của chính thư viện. Nằm ở subpath riêng lúc còn là package rời; từ
|
|
32
|
+
* đợt gộp thì nó ở cùng cây nên barrel phơi luôn ra đây.
|
|
33
|
+
*/
|
|
34
|
+
export { uiMessages } from './ui/index.js'
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Danh mục ngôn ngữ, và quy ước locale được ghi lên DOM.
|
|
3
|
+
*
|
|
4
|
+
* DỮ LIỆU thuần — không import framework nào, không đụng `document`. Cùng lý do
|
|
5
|
+
* với `registry.ts` của `@vestow/theme`: Vue composable, React hook hay
|
|
6
|
+
* Angular service đều đọc chung file này, nên thêm một ngôn ngữ là mọi framework
|
|
7
|
+
* thấy ngay chứ không phải chép danh sách.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type LocaleId = 'vi' | 'en'
|
|
11
|
+
|
|
12
|
+
export interface LocaleOption {
|
|
13
|
+
id: LocaleId
|
|
14
|
+
/** Tên gọi trong CHÍNH ngôn ngữ đó — người đang lạc ngôn ngữ mới đọc ra. */
|
|
15
|
+
name: string
|
|
16
|
+
/** Thẻ BCP 47 đầy đủ, dùng cho `Intl` và cho thuộc tính `lang`. */
|
|
17
|
+
tag: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const locales: readonly LocaleOption[] = [
|
|
21
|
+
{ id: 'vi', name: 'Tiếng Việt', tag: 'vi-VN' },
|
|
22
|
+
{ id: 'en', name: 'English', tag: 'en-US' },
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
export function isLocaleId(value: unknown): value is LocaleId {
|
|
26
|
+
return typeof value === 'string' && locales.some(item => item.id === value)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function localeTag(id: LocaleId): string {
|
|
30
|
+
return locales.find(item => item.id === id)?.tag ?? id
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Mặc định là tiếng Việt: đây là template quản trị viết cho người Việt, và
|
|
35
|
+
* `init()` vẫn ưu tiên lựa chọn đã lưu rồi mới tới ngôn ngữ của trình duyệt.
|
|
36
|
+
*/
|
|
37
|
+
export const defaults = {
|
|
38
|
+
locale: 'vi',
|
|
39
|
+
} as const satisfies { locale: LocaleId }
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Khoá localStorage. Cùng tiền tố `hyperion-` với ba trục giao diện, và cũng
|
|
43
|
+
* một khoá cho một thứ — đừng gộp locale vào chung object với theme.
|
|
44
|
+
*/
|
|
45
|
+
export const storageKeys = {
|
|
46
|
+
locale: 'hyperion-locale',
|
|
47
|
+
} as const
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Locale hiện diện trên DOM bằng thuộc tính `lang` của `<html>`.
|
|
51
|
+
*
|
|
52
|
+
* Không phải để làm đẹp: `lang` quyết định trình đọc màn hình phát âm bằng
|
|
53
|
+
* giọng nào, và quyết định cả cách trình duyệt ngắt dòng, chọn dấu ngoặc kép,
|
|
54
|
+
* chọn font dự phòng. Đặt sai thì tiếng Việt bị đọc bằng giọng Anh.
|
|
55
|
+
*/
|
|
56
|
+
export const domTargets = {
|
|
57
|
+
langAttribute: 'lang',
|
|
58
|
+
} as const
|
|
59
|
+
|
|
60
|
+
export interface LocaleState {
|
|
61
|
+
locale: LocaleId
|
|
62
|
+
/** Thẻ BCP 47 của locale đang bật, tiện truyền thẳng vào `Intl`. */
|
|
63
|
+
tag: string
|
|
64
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cancel": "Cancel",
|
|
3
|
+
"carousel.dots": "Choose a slide",
|
|
4
|
+
"carousel.goTo": "Go to slide {index}",
|
|
5
|
+
"carousel.label": "Carousel",
|
|
6
|
+
"carousel.next": "Next slide",
|
|
7
|
+
"carousel.previous": "Previous slide",
|
|
8
|
+
"carousel.roleDescription": "carousel",
|
|
9
|
+
"carousel.slideLabel": "Slide {index} of {total}",
|
|
10
|
+
"carousel.slideRoleDescription": "slide",
|
|
11
|
+
"cascadeSelect.placeholder": "Select",
|
|
12
|
+
"chip.remove": "Remove",
|
|
13
|
+
"close": "Close",
|
|
14
|
+
"combobox.create": "Add “{value}”",
|
|
15
|
+
"combobox.empty": "No results.",
|
|
16
|
+
"command.empty": "No results found.",
|
|
17
|
+
"command.title": "Command palette",
|
|
18
|
+
"confirm": "Confirm",
|
|
19
|
+
"copied": "Copied",
|
|
20
|
+
"copy.code": "Copy code",
|
|
21
|
+
"copy.command": "Copy command",
|
|
22
|
+
"dataView.grid": "Grid view",
|
|
23
|
+
"dataView.list": "List view",
|
|
24
|
+
"datePicker.placeholder": "Pick a date",
|
|
25
|
+
"dateRangePicker.placeholder": "Pick a date range",
|
|
26
|
+
"editable.cancel": "Cancel",
|
|
27
|
+
"editable.edit": "Edit",
|
|
28
|
+
"editable.submit": "Save",
|
|
29
|
+
"fileUpload.label": "Drop files here, or click to choose",
|
|
30
|
+
"fileUpload.maxSize": "Up to {size}",
|
|
31
|
+
"fileUpload.remove": "Remove {name}",
|
|
32
|
+
"inputColor.open": "Pick a colour",
|
|
33
|
+
"inputNumber.decrement": "Decrease",
|
|
34
|
+
"inputNumber.increment": "Increase",
|
|
35
|
+
"inputPassword.fair": "Fair",
|
|
36
|
+
"inputPassword.good": "Good",
|
|
37
|
+
"inputPassword.hide": "Hide password",
|
|
38
|
+
"inputPassword.show": "Show password",
|
|
39
|
+
"inputPassword.strength": "Strength",
|
|
40
|
+
"inputPassword.strong": "Strong",
|
|
41
|
+
"inputPassword.weak": "Weak",
|
|
42
|
+
"inputTags.clear": "Clear all tags",
|
|
43
|
+
"locale.language": "Language",
|
|
44
|
+
"orderList.down": "Move down",
|
|
45
|
+
"orderList.label": "Ordered list",
|
|
46
|
+
"orderList.moved": "Moved {count} items",
|
|
47
|
+
"orderList.toBottom": "Move to bottom",
|
|
48
|
+
"orderList.toTop": "Move to top",
|
|
49
|
+
"orderList.up": "Move up",
|
|
50
|
+
"pagination.empty": "No results",
|
|
51
|
+
"pagination.first": "First page",
|
|
52
|
+
"pagination.label": "Pagination",
|
|
53
|
+
"pagination.last": "Last page",
|
|
54
|
+
"pagination.next": "Next page",
|
|
55
|
+
"pagination.page": "Page {page} of {total}",
|
|
56
|
+
"pagination.previous": "Previous page",
|
|
57
|
+
"pickList.return": "Move selected left",
|
|
58
|
+
"pickList.returnAll": "Move all left",
|
|
59
|
+
"pickList.returned": "Moved {count} items back to the available list",
|
|
60
|
+
"pickList.send": "Move selected right",
|
|
61
|
+
"pickList.sendAll": "Move all right",
|
|
62
|
+
"pickList.sent": "Moved {count} items to the selected list",
|
|
63
|
+
"pickList.source": "Available",
|
|
64
|
+
"pickList.target": "Selected",
|
|
65
|
+
"select.clear": "Clear selection",
|
|
66
|
+
"select.selected": "{count} selected",
|
|
67
|
+
"sidebar.description": "Navigation drawer for narrow screens.",
|
|
68
|
+
"sidebar.title": "Navigation",
|
|
69
|
+
"sidebar.toggle": "Toggle sidebar",
|
|
70
|
+
"skipLink": "Skip to main content",
|
|
71
|
+
"speedDial.open": "Open quick actions",
|
|
72
|
+
"spinner.label": "Loading",
|
|
73
|
+
"splitButton.more": "More options",
|
|
74
|
+
"stepper.label": "Progress",
|
|
75
|
+
"table.clearFilter": "Clear filter",
|
|
76
|
+
"table.columns": "Toggle columns",
|
|
77
|
+
"table.columnsTrigger": "Columns",
|
|
78
|
+
"table.empty": "No data",
|
|
79
|
+
"table.rowsPerPage": "Rows per page",
|
|
80
|
+
"table.selectAll": "Select every row on this page",
|
|
81
|
+
"table.selectRow": "Select row",
|
|
82
|
+
"table.summary": "{count} row|{count} rows",
|
|
83
|
+
"table.summarySelected": "{selected} of {count} row selected|{selected} of {count} rows selected",
|
|
84
|
+
"theme.appearance": "Appearance",
|
|
85
|
+
"theme.appearanceSettings": "Appearance settings",
|
|
86
|
+
"theme.modeDark": "Dark",
|
|
87
|
+
"theme.modeDarkHint": "Always dark, whatever the OS says",
|
|
88
|
+
"theme.modeLight": "Light",
|
|
89
|
+
"theme.modeLightHint": "Always light, whatever the OS says",
|
|
90
|
+
"theme.modeSystem": "System",
|
|
91
|
+
"theme.modeSystemHint": "Follows the operating system",
|
|
92
|
+
"theme.palette": "Color palette",
|
|
93
|
+
"theme.paletteHint.aurora": "Teal over deep navy",
|
|
94
|
+
"theme.paletteHint.eclipse": "Monochrome primary, status colours kept",
|
|
95
|
+
"theme.paletteHint.hyperion": "Repo default blue, cool neutrals",
|
|
96
|
+
"theme.paletteHint.nebula": "Electric violet, cool neutrals",
|
|
97
|
+
"theme.paletteHint.solar": "Molten orange, warm charcoal",
|
|
98
|
+
"theme.toDark": "Switch to the dark theme",
|
|
99
|
+
"theme.toLight": "Switch to the light theme",
|
|
100
|
+
"toast.label": "Notifications",
|
|
101
|
+
"tokenTable.heading": "Token · applies to",
|
|
102
|
+
"treeSelect.placeholder": "Select",
|
|
103
|
+
"videoPlayer.enterFullscreen": "Enter fullscreen",
|
|
104
|
+
"videoPlayer.exitFullscreen": "Exit fullscreen",
|
|
105
|
+
"videoPlayer.mute": "Mute",
|
|
106
|
+
"videoPlayer.pause": "Pause",
|
|
107
|
+
"videoPlayer.play": "Play",
|
|
108
|
+
"videoPlayer.playbackSpeed": "Playback speed",
|
|
109
|
+
"videoPlayer.seek": "Seek",
|
|
110
|
+
"videoPlayer.skipBack": "Back {seconds} seconds",
|
|
111
|
+
"videoPlayer.skipForward": "Forward {seconds} seconds",
|
|
112
|
+
"videoPlayer.unmute": "Unmute",
|
|
113
|
+
"videoPlayer.volume": "Volume"
|
|
114
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"array.max": "Pick at most {count} item|Pick at most {count} items",
|
|
3
|
+
"array.min": "Pick at least {count} item|Pick at least {count} items",
|
|
4
|
+
"date": "Not a valid date",
|
|
5
|
+
"email": "Not a valid email",
|
|
6
|
+
"enum": "Not one of the allowed values",
|
|
7
|
+
"format": "Wrong format",
|
|
8
|
+
"invalidType": "Wrong type (expected {expected})",
|
|
9
|
+
"number.max": "Must be at most {count}",
|
|
10
|
+
"number.min": "Must be at least {count}",
|
|
11
|
+
"required": "This field is required",
|
|
12
|
+
"string.max": "At most {count} character|At most {count} characters",
|
|
13
|
+
"string.min": "At least {count} character|At least {count} characters",
|
|
14
|
+
"url": "Not a valid URL",
|
|
15
|
+
"uuid": "Not a valid UUID"
|
|
16
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Messages } from '../controller.js'
|
|
2
|
+
import type { LocaleId } from '../registry.js'
|
|
3
|
+
import { createDictionary } from '../dictionary.js'
|
|
4
|
+
import enUi from './en/ui.json'
|
|
5
|
+
import enZod from './en/zod.json'
|
|
6
|
+
import viUi from './vi/ui.json'
|
|
7
|
+
import viZod from './vi/zod.json'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Bộ chuỗi của CHÍNH thư viện component, đi theo thư viện.
|
|
11
|
+
*
|
|
12
|
+
* `packages/ui` không được phụ thuộc vào app, nên nó cũng không thể trông chờ
|
|
13
|
+
* app khai hộ nhãn cho nút Đóng của Dialog hay `aria-label` của nút phân trang.
|
|
14
|
+
*
|
|
15
|
+
* Nằm ở `src/i18n` chứ không ở một cây nào, vì **mọi bản component đều
|
|
16
|
+
* cần đúng bộ khoá này** — 112 khoá tại thời điểm viết. Để mỗi cây một bản là cả
|
|
17
|
+
* bộ × 2 ngôn ngữ trôi khỏi nhau, mà không có gì đối chiếu. Đếm lại bằng
|
|
18
|
+
* `jq 'length' src/ui/en/ui.json`; con số ở đây từng ghi 86 trong khi file có 79,
|
|
19
|
+
* rồi ghi 90 trong khi file có 95.
|
|
20
|
+
*
|
|
21
|
+
* ## `ui.theme.*` và `ui.locale.*` — món nợ đã trả
|
|
22
|
+
*
|
|
23
|
+
* Ba control theme (`ThemeToggle`, `PaletteMenu`, `LocaleMenu`) là component THƯ
|
|
24
|
+
* VIỆN nhưng từng đọc khoá của APP: `kit.appearance`, `common.language` và cả
|
|
25
|
+
* nhánh `theme.*`. Tức thư viện trông chờ app khai hộ chuỗi cho chính nó — đúng
|
|
26
|
+
* cái mà đoạn trên nói là không được. Cả **ba** cây dính, và cả ba cùng một bộ 17
|
|
27
|
+
* khoá, nên nó là bản sao ba lần chứ không phải lệch của một cây.
|
|
28
|
+
*
|
|
29
|
+
* 17 khoá đó nay nằm ở đây. Cái mất, ghi thẳng: `ui.theme.paletteHint.<id>` khoá
|
|
30
|
+
* cứng năm id bảng màu (`aurora`, `eclipse`, `hyperion`, `nebula`, `solar`), nên
|
|
31
|
+
* thêm một bảng màu là phải thêm một khoá ở file này. Đó là cái giá đúng — id
|
|
32
|
+
* bảng màu vốn đã khoá cứng trong `packages/theme/src/registry.ts`, và để chuỗi
|
|
33
|
+
* ở app thì mỗi app phải tự biết cả năm.
|
|
34
|
+
*
|
|
35
|
+
* `theme.surface*` thì **ở lại app**: không control nào của thư viện đọc chúng,
|
|
36
|
+
* chỉ trang `/create` của `vue/apps/kit` dùng.
|
|
37
|
+
*
|
|
38
|
+
* Khoá viết PHẲNG trong JSON, không lồng. Lồng thì đẹp mắt hơn nhưng phải làm
|
|
39
|
+
* phẳng lúc chạy, mà chính bước đó xoá mất kiểu — và kiểu là thứ đang giữ cho
|
|
40
|
+
* hai ngôn ngữ không lệch nhau.
|
|
41
|
+
*/
|
|
42
|
+
const dictionary = createDictionary()
|
|
43
|
+
|
|
44
|
+
dictionary.topic('ui', viUi, enUi)
|
|
45
|
+
dictionary.topic('zod', viZod, enZod)
|
|
46
|
+
|
|
47
|
+
/** Từ điển đã ghép, khoá đầy đủ. Nạp ở vỏ `useI18n` của mỗi cây, không nạp ở đâu khác. */
|
|
48
|
+
export const uiMessages: Record<LocaleId, Messages> = dictionary.messages
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cancel": "Huỷ",
|
|
3
|
+
"carousel.dots": "Chọn trang",
|
|
4
|
+
"carousel.goTo": "Tới trang {index}",
|
|
5
|
+
"carousel.label": "Băng trình chiếu",
|
|
6
|
+
"carousel.next": "Trang sau",
|
|
7
|
+
"carousel.previous": "Trang trước",
|
|
8
|
+
"carousel.roleDescription": "băng trình chiếu",
|
|
9
|
+
"carousel.slideLabel": "Trang {index} / {total}",
|
|
10
|
+
"carousel.slideRoleDescription": "trang",
|
|
11
|
+
"cascadeSelect.placeholder": "Chọn",
|
|
12
|
+
"chip.remove": "Gỡ",
|
|
13
|
+
"close": "Đóng",
|
|
14
|
+
"combobox.create": "Thêm “{value}”",
|
|
15
|
+
"combobox.empty": "Không có kết quả.",
|
|
16
|
+
"command.empty": "Không tìm thấy kết quả.",
|
|
17
|
+
"command.title": "Bảng lệnh",
|
|
18
|
+
"confirm": "Đồng ý",
|
|
19
|
+
"copied": "Đã chép",
|
|
20
|
+
"copy.code": "Chép đoạn code",
|
|
21
|
+
"copy.command": "Chép câu lệnh",
|
|
22
|
+
"dataView.grid": "Xem dạng lưới",
|
|
23
|
+
"dataView.list": "Xem dạng danh sách",
|
|
24
|
+
"datePicker.placeholder": "Chọn ngày",
|
|
25
|
+
"dateRangePicker.placeholder": "Chọn khoảng ngày",
|
|
26
|
+
"editable.cancel": "Huỷ",
|
|
27
|
+
"editable.edit": "Sửa",
|
|
28
|
+
"editable.submit": "Lưu",
|
|
29
|
+
"fileUpload.label": "Kéo tệp vào đây hoặc bấm để chọn",
|
|
30
|
+
"fileUpload.maxSize": "Tối đa {size}",
|
|
31
|
+
"fileUpload.remove": "Gỡ {name}",
|
|
32
|
+
"inputColor.open": "Chọn màu",
|
|
33
|
+
"inputNumber.decrement": "Giảm",
|
|
34
|
+
"inputNumber.increment": "Tăng",
|
|
35
|
+
"inputPassword.fair": "Trung bình",
|
|
36
|
+
"inputPassword.good": "Khá",
|
|
37
|
+
"inputPassword.hide": "Ẩn mật khẩu",
|
|
38
|
+
"inputPassword.show": "Hiện mật khẩu",
|
|
39
|
+
"inputPassword.strength": "Độ mạnh",
|
|
40
|
+
"inputPassword.strong": "Mạnh",
|
|
41
|
+
"inputPassword.weak": "Yếu",
|
|
42
|
+
"inputTags.clear": "Xoá hết thẻ",
|
|
43
|
+
"locale.language": "Ngôn ngữ",
|
|
44
|
+
"orderList.down": "Đưa xuống một bậc",
|
|
45
|
+
"orderList.label": "Danh sách sắp thứ tự",
|
|
46
|
+
"orderList.moved": "Đã dời {count} mục",
|
|
47
|
+
"orderList.toBottom": "Đưa xuống cuối",
|
|
48
|
+
"orderList.toTop": "Đưa lên đầu",
|
|
49
|
+
"orderList.up": "Đưa lên một bậc",
|
|
50
|
+
"pagination.empty": "Không có kết quả",
|
|
51
|
+
"pagination.first": "Trang đầu",
|
|
52
|
+
"pagination.label": "Phân trang",
|
|
53
|
+
"pagination.last": "Trang cuối",
|
|
54
|
+
"pagination.next": "Trang sau",
|
|
55
|
+
"pagination.page": "Trang {page} / {total}",
|
|
56
|
+
"pagination.previous": "Trang trước",
|
|
57
|
+
"pickList.return": "Chuyển mục đang chọn sang trái",
|
|
58
|
+
"pickList.returnAll": "Chuyển tất cả sang trái",
|
|
59
|
+
"pickList.returned": "Đã trả {count} mục về danh sách có thể chọn",
|
|
60
|
+
"pickList.send": "Chuyển mục đang chọn sang phải",
|
|
61
|
+
"pickList.sendAll": "Chuyển tất cả sang phải",
|
|
62
|
+
"pickList.sent": "Đã chuyển {count} mục sang danh sách đã chọn",
|
|
63
|
+
"pickList.source": "Có thể chọn",
|
|
64
|
+
"pickList.target": "Đã chọn",
|
|
65
|
+
"select.clear": "Xoá lựa chọn",
|
|
66
|
+
"select.selected": "Đã chọn {count} mục",
|
|
67
|
+
"sidebar.description": "Ngăn kéo điều hướng cho màn hình hẹp.",
|
|
68
|
+
"sidebar.title": "Điều hướng",
|
|
69
|
+
"sidebar.toggle": "Bật/tắt thanh bên",
|
|
70
|
+
"skipLink": "Bỏ qua tới nội dung chính",
|
|
71
|
+
"speedDial.open": "Mở các hành động nhanh",
|
|
72
|
+
"spinner.label": "Đang tải",
|
|
73
|
+
"splitButton.more": "Mở thêm tuỳ chọn",
|
|
74
|
+
"stepper.label": "Tiến trình",
|
|
75
|
+
"table.clearFilter": "Bỏ lọc",
|
|
76
|
+
"table.columns": "Hiển thị cột",
|
|
77
|
+
"table.columnsTrigger": "Cột",
|
|
78
|
+
"table.empty": "Không có dữ liệu",
|
|
79
|
+
"table.rowsPerPage": "Dòng mỗi trang",
|
|
80
|
+
"table.selectAll": "Chọn tất cả dòng trong trang",
|
|
81
|
+
"table.selectRow": "Chọn dòng",
|
|
82
|
+
"table.summary": "{count} dòng",
|
|
83
|
+
"table.summarySelected": "Đã chọn {selected} / {count} dòng",
|
|
84
|
+
"theme.appearance": "Giao diện",
|
|
85
|
+
"theme.appearanceSettings": "Cài đặt giao diện",
|
|
86
|
+
"theme.modeDark": "Tối",
|
|
87
|
+
"theme.modeDarkHint": "Luôn tối, bất kể hệ điều hành nói gì",
|
|
88
|
+
"theme.modeLight": "Sáng",
|
|
89
|
+
"theme.modeLightHint": "Luôn sáng, bất kể hệ điều hành nói gì",
|
|
90
|
+
"theme.modeSystem": "Hệ thống",
|
|
91
|
+
"theme.modeSystemHint": "Theo hệ điều hành",
|
|
92
|
+
"theme.palette": "Bảng màu",
|
|
93
|
+
"theme.paletteHint.aurora": "Xanh mòng két trên nền navy sẫm",
|
|
94
|
+
"theme.paletteHint.eclipse": "Primary đơn sắc, giữ nguyên màu trạng thái",
|
|
95
|
+
"theme.paletteHint.hyperion": "Xanh dương mặc định của repo, trung tính lạnh",
|
|
96
|
+
"theme.paletteHint.nebula": "Tím điện, trung tính lạnh",
|
|
97
|
+
"theme.paletteHint.solar": "Cam nung chảy, than ấm",
|
|
98
|
+
"theme.toDark": "Chuyển sang giao diện tối",
|
|
99
|
+
"theme.toLight": "Chuyển sang giao diện sáng",
|
|
100
|
+
"toast.label": "Thông báo",
|
|
101
|
+
"tokenTable.heading": "Token · áp cho",
|
|
102
|
+
"treeSelect.placeholder": "Chọn",
|
|
103
|
+
"videoPlayer.enterFullscreen": "Bật toàn màn hình",
|
|
104
|
+
"videoPlayer.exitFullscreen": "Thoát toàn màn hình",
|
|
105
|
+
"videoPlayer.mute": "Tắt tiếng",
|
|
106
|
+
"videoPlayer.pause": "Tạm dừng",
|
|
107
|
+
"videoPlayer.play": "Chạy",
|
|
108
|
+
"videoPlayer.playbackSpeed": "Tốc độ phát",
|
|
109
|
+
"videoPlayer.seek": "Tua",
|
|
110
|
+
"videoPlayer.skipBack": "Lùi {seconds} giây",
|
|
111
|
+
"videoPlayer.skipForward": "Tới {seconds} giây",
|
|
112
|
+
"videoPlayer.unmute": "Bật tiếng",
|
|
113
|
+
"videoPlayer.volume": "Âm lượng"
|
|
114
|
+
}
|