@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,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Một field của TanStack Form giữ lỗi trong `meta.errorMap`, khoá theo **nguyên
|
|
3
|
+
* nhân** chứ không phải một chuỗi duy nhất:
|
|
4
|
+
*
|
|
5
|
+
* { onChange: …, onBlur: …, onSubmit: …, onMount: … }
|
|
6
|
+
*
|
|
7
|
+
* Mỗi giá trị có thể là chuỗi (lỗi tự đặt), một issue, hoặc **mảng issue** —
|
|
8
|
+
* đây là chỗ dễ hụt: validator ở cấp form gom issue theo path rồi gán cả
|
|
9
|
+
* `[issue, …]` cho từng field (`prefixSchemaToErrors` trong form-core), nên
|
|
10
|
+
* một field hỏng vì hai luật sẽ nhận mảng hai phần tử. Đọc thẳng `.message`
|
|
11
|
+
* của mảng ra `undefined` và lỗi biến mất **âm thầm** — không cảnh báo, không
|
|
12
|
+
* throw, chỉ là form không bao giờ báo sai.
|
|
13
|
+
*
|
|
14
|
+
* Nằm ở `src/variants` vì `form-core` là chung cho cả hai adapter, nên cả
|
|
15
|
+
* hai cây gặp đúng hình dạng errorMap này. Viết hai lần là hai cơ hội để một
|
|
16
|
+
* bên quên nhánh mảng.
|
|
17
|
+
*/
|
|
18
|
+
export type FieldErrorMap = Record<string, unknown>
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Không có `onServer` trong danh sách: lỗi từ server đi đường riêng chứ không
|
|
22
|
+
* ghi vào errorMap. Lý do ở `Form` của mỗi cây.
|
|
23
|
+
*/
|
|
24
|
+
const CAUSE_ORDER = ['onChange', 'onBlur', 'onSubmit', 'onDynamic', 'onMount'] as const
|
|
25
|
+
|
|
26
|
+
function toMessage(error: unknown): string | undefined {
|
|
27
|
+
if (typeof error === 'string')
|
|
28
|
+
return error === '' ? undefined : error
|
|
29
|
+
|
|
30
|
+
if (Array.isArray(error)) {
|
|
31
|
+
for (const item of error) {
|
|
32
|
+
const message = toMessage(item)
|
|
33
|
+
if (message !== undefined)
|
|
34
|
+
return message
|
|
35
|
+
}
|
|
36
|
+
return undefined
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (error !== null && typeof error === 'object' && 'message' in error) {
|
|
40
|
+
const { message } = error as { message: unknown }
|
|
41
|
+
return typeof message === 'string' ? message : undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return undefined
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Quy errorMap về đúng MỘT chuỗi để hiển thị — chỗ báo lỗi chỉ vẽ một dòng. */
|
|
48
|
+
export function firstErrorMessage(errorMap: FieldErrorMap | undefined): string | undefined {
|
|
49
|
+
if (errorMap === undefined)
|
|
50
|
+
return undefined
|
|
51
|
+
|
|
52
|
+
for (const cause of CAUSE_ORDER) {
|
|
53
|
+
const message = toMessage(errorMap[cause])
|
|
54
|
+
if (message !== undefined)
|
|
55
|
+
return message
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return undefined
|
|
59
|
+
}
|
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tô màu cú pháp cho các đoạn code trong trang tài liệu.
|
|
3
|
+
*
|
|
4
|
+
* Tự viết thay vì thêm Shiki hay Prism: trang docs chỉ hiển thị chính các ví
|
|
5
|
+
* dụ do repo này viết ra — một tập nhỏ, biết trước — nên không cần bộ
|
|
6
|
+
* highlight đầy đủ nặng cả megabyte. Đổi lại nó *không* phải parser thật:
|
|
7
|
+
* chỉ nhận đúng những cấu trúc dùng trong ví dụ (template Vue và vài dòng
|
|
8
|
+
* import/khai báo TypeScript). Gặp cú pháp lạ thì phần đó rơi về `text`,
|
|
9
|
+
* mất màu chứ không vỡ.
|
|
10
|
+
*
|
|
11
|
+
* Muốn tô đúng một file bất kỳ do người dùng dán vào thì phải đổi sang Shiki.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export type TokenKind
|
|
15
|
+
= | 'text'
|
|
16
|
+
| 'comment'
|
|
17
|
+
| 'tag'
|
|
18
|
+
| 'component'
|
|
19
|
+
| 'attr'
|
|
20
|
+
| 'string'
|
|
21
|
+
| 'keyword'
|
|
22
|
+
| 'function'
|
|
23
|
+
| 'number'
|
|
24
|
+
| 'punctuation'
|
|
25
|
+
|
|
26
|
+
export interface Token {
|
|
27
|
+
kind: TokenKind
|
|
28
|
+
value: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type Language = 'vue' | 'ts' | 'tsx'
|
|
32
|
+
|
|
33
|
+
const KEYWORDS = new Set([
|
|
34
|
+
'as',
|
|
35
|
+
'async',
|
|
36
|
+
'await',
|
|
37
|
+
'break',
|
|
38
|
+
'case',
|
|
39
|
+
'catch',
|
|
40
|
+
'class',
|
|
41
|
+
'const',
|
|
42
|
+
'continue',
|
|
43
|
+
'default',
|
|
44
|
+
'delete',
|
|
45
|
+
'else',
|
|
46
|
+
'export',
|
|
47
|
+
'extends',
|
|
48
|
+
'finally',
|
|
49
|
+
'for',
|
|
50
|
+
'from',
|
|
51
|
+
'function',
|
|
52
|
+
'if',
|
|
53
|
+
'import',
|
|
54
|
+
'in',
|
|
55
|
+
'interface',
|
|
56
|
+
'let',
|
|
57
|
+
'new',
|
|
58
|
+
'of',
|
|
59
|
+
'return',
|
|
60
|
+
'satisfies',
|
|
61
|
+
'switch',
|
|
62
|
+
'throw',
|
|
63
|
+
'try',
|
|
64
|
+
'type',
|
|
65
|
+
'typeof',
|
|
66
|
+
'var',
|
|
67
|
+
'while',
|
|
68
|
+
])
|
|
69
|
+
|
|
70
|
+
const LITERALS = new Set(['true', 'false', 'null', 'undefined'])
|
|
71
|
+
|
|
72
|
+
/** Gộp token liền kề cùng loại để bớt số thẻ span phải render. */
|
|
73
|
+
function push(tokens: Token[], kind: TokenKind, value: string): void {
|
|
74
|
+
if (value === '')
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
const last = tokens[tokens.length - 1]
|
|
78
|
+
if (last?.kind === kind)
|
|
79
|
+
last.value += value
|
|
80
|
+
else
|
|
81
|
+
tokens.push({ kind, value })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Một dòng TypeScript: chú thích, chuỗi, từ khoá, số, tên hàm. */
|
|
85
|
+
const TS_PATTERN = /(?<comment>\/\/[^\n]*|\/\*[\s\S]*?\*\/)|(?<string>'[^']*'|"[^"]*"|`[^`]*`)|(?<number>\b\d+(?:\.\d+)?\b)|(?<word>[A-Z_$][\w$]*)|(?<punctuation>[{}()[\].,;:=<>!?+\-*/&|]+)/gi
|
|
86
|
+
|
|
87
|
+
function tokenizeScript(code: string, tokens: Token[]): void {
|
|
88
|
+
let index = 0
|
|
89
|
+
|
|
90
|
+
for (const match of code.matchAll(TS_PATTERN)) {
|
|
91
|
+
push(tokens, 'text', code.slice(index, match.index))
|
|
92
|
+
index = match.index + match[0].length
|
|
93
|
+
|
|
94
|
+
const groups = match.groups ?? {}
|
|
95
|
+
|
|
96
|
+
if (groups.comment !== undefined)
|
|
97
|
+
push(tokens, 'comment', groups.comment)
|
|
98
|
+
else if (groups.string !== undefined)
|
|
99
|
+
push(tokens, 'string', groups.string)
|
|
100
|
+
else if (groups.number !== undefined)
|
|
101
|
+
push(tokens, 'number', groups.number)
|
|
102
|
+
else if (groups.punctuation !== undefined)
|
|
103
|
+
push(tokens, 'punctuation', groups.punctuation)
|
|
104
|
+
else if (groups.word !== undefined)
|
|
105
|
+
push(tokens, wordKind(groups.word, code, index), groups.word)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
push(tokens, 'text', code.slice(index))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function wordKind(word: string, code: string, after: number): TokenKind {
|
|
112
|
+
if (KEYWORDS.has(word))
|
|
113
|
+
return 'keyword'
|
|
114
|
+
|
|
115
|
+
if (LITERALS.has(word))
|
|
116
|
+
return 'number'
|
|
117
|
+
|
|
118
|
+
// Theo sau là dấu `(` thì coi là lời gọi hàm.
|
|
119
|
+
return code[after] === '(' ? 'function' : 'text'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Nội dung bên trong một cặp ngoặc nhọn: tên thẻ, thuộc tính, giá trị. */
|
|
123
|
+
// Thân thẻ liệt kê tường minh chuỗi-trong-nháy và ký tự thường, không dùng
|
|
124
|
+
// [sS]*? — kiểu lười đó chồng lấn với tên thẻ nên regex backtrack rất tệ.
|
|
125
|
+
const TAG_PATTERN = /(?<open><\/?)(?<name>[a-z][\w.-]*)(?![\w.-])(?<body>(?:"[^"]*"|'[^']*'|[^>"'])*)(?<close>>)/gi
|
|
126
|
+
|
|
127
|
+
const ATTR_PATTERN = /(?<name>[\w:@.[\]-]+)|(?<equals>=)|(?<value>"[^"]*"|'[^']*')/g
|
|
128
|
+
|
|
129
|
+
function tokenizeTagBody(body: string, tokens: Token[]): void {
|
|
130
|
+
let index = 0
|
|
131
|
+
|
|
132
|
+
for (const match of body.matchAll(ATTR_PATTERN)) {
|
|
133
|
+
push(tokens, 'text', body.slice(index, match.index))
|
|
134
|
+
index = match.index + match[0].length
|
|
135
|
+
|
|
136
|
+
const groups = match.groups ?? {}
|
|
137
|
+
|
|
138
|
+
if (groups.name !== undefined)
|
|
139
|
+
push(tokens, 'attr', groups.name)
|
|
140
|
+
else if (groups.equals !== undefined)
|
|
141
|
+
push(tokens, 'keyword', groups.equals)
|
|
142
|
+
else if (groups.value !== undefined)
|
|
143
|
+
push(tokens, 'string', groups.value)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
push(tokens, 'text', body.slice(index))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Thẻ viết hoa là component, viết thường là thẻ HTML — hai màu khác nhau. */
|
|
150
|
+
function tagKind(name: string): TokenKind {
|
|
151
|
+
return /^[A-Z]/.test(name) ? 'component' : 'tag'
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const COMMENT_PATTERN = /<!--[\s\S]*?-->/g
|
|
155
|
+
|
|
156
|
+
function tokenizeMarkup(code: string, tokens: Token[]): void {
|
|
157
|
+
let index = 0
|
|
158
|
+
|
|
159
|
+
for (const match of code.matchAll(TAG_PATTERN)) {
|
|
160
|
+
const before = code.slice(index, match.index)
|
|
161
|
+
tokenizeMarkupText(before, tokens)
|
|
162
|
+
index = match.index + match[0].length
|
|
163
|
+
|
|
164
|
+
const groups = match.groups ?? {}
|
|
165
|
+
|
|
166
|
+
// Dấu `<` `>` `</` để màu chữ thường, chỉ tên thẻ mới đổi màu — GitHub và
|
|
167
|
+
// shadcn đều tô như vậy.
|
|
168
|
+
push(tokens, 'punctuation', groups.open ?? '')
|
|
169
|
+
push(tokens, tagKind(groups.name ?? ''), groups.name ?? '')
|
|
170
|
+
tokenizeTagBody(groups.body ?? '', tokens)
|
|
171
|
+
push(tokens, 'punctuation', groups.close ?? '')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
tokenizeMarkupText(code.slice(index), tokens)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Phần nằm ngoài thẻ: chỉ cần tách chú thích HTML ra khỏi chữ thường. */
|
|
178
|
+
function tokenizeMarkupText(text: string, tokens: Token[]): void {
|
|
179
|
+
let index = 0
|
|
180
|
+
|
|
181
|
+
for (const match of text.matchAll(COMMENT_PATTERN)) {
|
|
182
|
+
push(tokens, 'text', text.slice(index, match.index))
|
|
183
|
+
push(tokens, 'comment', match[0])
|
|
184
|
+
index = match.index + match[0].length
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
push(tokens, 'text', text.slice(index))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Tách khối <script> ra khỏi phần template của một SFC. */
|
|
191
|
+
const SCRIPT_PATTERN = /(<script\b[^>]*>)([\s\S]*?)(<\/script>)/g
|
|
192
|
+
|
|
193
|
+
function tokenizeSfc(code: string, tokens: Token[]): void {
|
|
194
|
+
let index = 0
|
|
195
|
+
|
|
196
|
+
for (const match of code.matchAll(SCRIPT_PATTERN)) {
|
|
197
|
+
tokenizeMarkup(code.slice(index, match.index), tokens)
|
|
198
|
+
index = match.index + match[0].length
|
|
199
|
+
|
|
200
|
+
tokenizeMarkup(match[1], tokens)
|
|
201
|
+
tokenizeScript(match[2], tokens)
|
|
202
|
+
tokenizeMarkup(match[3], tokens)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
tokenizeMarkup(code.slice(index), tokens)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function highlight(code: string, language: Language = 'vue'): Token[] {
|
|
209
|
+
const tokens: Token[] = []
|
|
210
|
+
|
|
211
|
+
if (language === 'tsx')
|
|
212
|
+
tokenizeTsx(code, tokens)
|
|
213
|
+
else if (language === 'ts')
|
|
214
|
+
tokenizeScript(code, tokens)
|
|
215
|
+
else
|
|
216
|
+
tokenizeSfc(code, tokens)
|
|
217
|
+
|
|
218
|
+
return tokens
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Tô màu cả khối rồi mới cắt theo dòng.
|
|
223
|
+
*
|
|
224
|
+
* Đừng gọi `highlight()` cho từng dòng một: `<script>` và `</script>` nằm ở hai
|
|
225
|
+
* dòng khác nhau nên regex tách khối script không bao giờ khớp, và toàn bộ phần
|
|
226
|
+
* TypeScript rơi về `text` — mất màu sạch. Chú thích nhiều dòng cũng vậy.
|
|
227
|
+
*/
|
|
228
|
+
export function highlightLines(code: string, language: Language = 'vue'): Token[][] {
|
|
229
|
+
const lines: Token[][] = [[]]
|
|
230
|
+
|
|
231
|
+
for (const token of highlight(code.replace(/\n+$/, ''), language)) {
|
|
232
|
+
const parts = token.value.split('\n')
|
|
233
|
+
|
|
234
|
+
parts.forEach((part, index) => {
|
|
235
|
+
if (index > 0)
|
|
236
|
+
lines.push([])
|
|
237
|
+
|
|
238
|
+
if (part !== '')
|
|
239
|
+
lines[lines.length - 1].push({ kind: token.kind, value: part })
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return lines
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* ------------------------------------------------------------------------- *
|
|
247
|
+
* TSX
|
|
248
|
+
* ------------------------------------------------------------------------- */
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* JSX KHÔNG tách được bằng cách cắt khối như SFC.
|
|
252
|
+
*
|
|
253
|
+
* `tokenizeSfc` chạy được vì `<script>` và `<template>` là hai vùng rời nhau,
|
|
254
|
+
* cắt một lần là xong. Trong `.tsx` thì JSX lồng vào giữa biểu thức — một `<`
|
|
255
|
+
* có thể là thẻ, là toán tử nhỏ hơn, hay là tham số generic — nên phải quét
|
|
256
|
+
* tuần tự và đổi chế độ tại chỗ.
|
|
257
|
+
*
|
|
258
|
+
* Vẫn theo đúng tinh thần của cả file: không phải parser thật, chỉ nhận những
|
|
259
|
+
* cấu trúc mà ví dụ trong repo dùng, gặp cú pháp lạ thì rơi về `text` chứ
|
|
260
|
+
* không vỡ.
|
|
261
|
+
*/
|
|
262
|
+
|
|
263
|
+
/** Ký tự đứng ngay trước `<` cho phép nó mở một thẻ JSX. */
|
|
264
|
+
const JSX_OPENERS = new Set(['(', '{', '}', '=', ',', '&', '|', '?', ':', ';', '>', '['])
|
|
265
|
+
|
|
266
|
+
/** Từ khoá đứng trước `<` cũng mở được — `return <div/>`. */
|
|
267
|
+
const JSX_OPENER_WORDS = /(?:return|=>|\?\?|&&|\|\|)\s*$/
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* `<` này có phải mở đầu một thẻ JSX không.
|
|
271
|
+
*
|
|
272
|
+
* Hai điều kiện, và cần cả hai. Sau `<` phải là chữ cái, `_`, `$` (tên thẻ)
|
|
273
|
+
* hoặc `>` (fragment). Trước `<` phải là chỗ mà một BIỂU THỨC được phép bắt
|
|
274
|
+
* đầu — nếu không thì `a < b` và `Array<string>` đều bị đọc thành thẻ.
|
|
275
|
+
*/
|
|
276
|
+
function opensJsx(code: string, at: number): boolean {
|
|
277
|
+
const next = code[at + 1]
|
|
278
|
+
if (next === undefined || !/[A-Z_$>]/i.test(next))
|
|
279
|
+
return false
|
|
280
|
+
|
|
281
|
+
const before = code.slice(0, at)
|
|
282
|
+
const trimmed = before.trimEnd()
|
|
283
|
+
if (trimmed === '')
|
|
284
|
+
return true
|
|
285
|
+
|
|
286
|
+
const last = trimmed[trimmed.length - 1]!
|
|
287
|
+
return JSX_OPENERS.has(last) || JSX_OPENER_WORDS.test(trimmed)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Bỏ qua chuỗi và chú thích khi đi tìm `<`.
|
|
292
|
+
*
|
|
293
|
+
* Không có bước này thì một `<` nằm trong `'a < b'` hay trong chú thích cũng mở
|
|
294
|
+
* ra một thẻ, và phần còn lại của file bị đọc nhầm hết.
|
|
295
|
+
*/
|
|
296
|
+
function findJsxStart(code: string, from: number): number {
|
|
297
|
+
let i = from
|
|
298
|
+
|
|
299
|
+
while (i < code.length) {
|
|
300
|
+
const ch = code[i]!
|
|
301
|
+
|
|
302
|
+
if (ch === '/' && code[i + 1] === '/') {
|
|
303
|
+
const end = code.indexOf('\n', i)
|
|
304
|
+
i = end === -1 ? code.length : end
|
|
305
|
+
continue
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (ch === '/' && code[i + 1] === '*') {
|
|
309
|
+
const end = code.indexOf('*/', i + 2)
|
|
310
|
+
i = end === -1 ? code.length : end + 2
|
|
311
|
+
continue
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (ch === '\'' || ch === '"' || ch === '`') {
|
|
315
|
+
i = skipString(code, i)
|
|
316
|
+
continue
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (ch === '<' && opensJsx(code, i))
|
|
320
|
+
return i
|
|
321
|
+
|
|
322
|
+
i += 1
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return -1
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** Trả về chỉ số NGAY SAU dấu đóng của chuỗi bắt đầu tại `start`. */
|
|
329
|
+
function skipString(code: string, start: number): number {
|
|
330
|
+
const quote = code[start]!
|
|
331
|
+
let i = start + 1
|
|
332
|
+
|
|
333
|
+
while (i < code.length) {
|
|
334
|
+
if (code[i] === '\\') {
|
|
335
|
+
i += 2
|
|
336
|
+
continue
|
|
337
|
+
}
|
|
338
|
+
if (code[i] === quote)
|
|
339
|
+
return i + 1
|
|
340
|
+
i += 1
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return code.length
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Quét thân thẻ mở, có ĐẾM NGOẶC NHỌN.
|
|
348
|
+
*
|
|
349
|
+
* `TAG_PATTERN` của phần markup không dùng lại được ở đây: nó coi `>` đầu tiên
|
|
350
|
+
* là hết thẻ, mà JSX thì `onClick={() => x}` có một `>` nằm giữa thân. Thẻ bị
|
|
351
|
+
* cắt sớm và phần còn lại của file lệch hết.
|
|
352
|
+
*
|
|
353
|
+
* Trả về chỉ số của `>` đóng thẻ.
|
|
354
|
+
*/
|
|
355
|
+
function findTagEnd(code: string, from: number): number {
|
|
356
|
+
let i = from
|
|
357
|
+
let braces = 0
|
|
358
|
+
|
|
359
|
+
while (i < code.length) {
|
|
360
|
+
const ch = code[i]!
|
|
361
|
+
|
|
362
|
+
if (ch === '\'' || ch === '"' || ch === '`') {
|
|
363
|
+
i = skipString(code, i)
|
|
364
|
+
continue
|
|
365
|
+
}
|
|
366
|
+
if (ch === '{') {
|
|
367
|
+
braces += 1
|
|
368
|
+
i += 1
|
|
369
|
+
continue
|
|
370
|
+
}
|
|
371
|
+
if (ch === '}') {
|
|
372
|
+
braces -= 1
|
|
373
|
+
i += 1
|
|
374
|
+
continue
|
|
375
|
+
}
|
|
376
|
+
if (ch === '>' && braces === 0)
|
|
377
|
+
return i
|
|
378
|
+
|
|
379
|
+
i += 1
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return code.length
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const JSX_NAME = /^<\/?([A-Z_$][\w$.]*)?/i
|
|
386
|
+
|
|
387
|
+
/** Thuộc tính JSX: tên, `=`, rồi chuỗi hoặc một biểu thức trong ngoặc nhọn. */
|
|
388
|
+
function tokenizeJsxAttrs(body: string, tokens: Token[]): void {
|
|
389
|
+
let i = 0
|
|
390
|
+
|
|
391
|
+
while (i < body.length) {
|
|
392
|
+
const ch = body[i]!
|
|
393
|
+
|
|
394
|
+
if (/\s/.test(ch)) {
|
|
395
|
+
const start = i
|
|
396
|
+
while (i < body.length && /\s/.test(body[i]!))
|
|
397
|
+
i += 1
|
|
398
|
+
push(tokens, 'text', body.slice(start, i))
|
|
399
|
+
continue
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (ch === '=') {
|
|
403
|
+
push(tokens, 'keyword', '=')
|
|
404
|
+
i += 1
|
|
405
|
+
continue
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (ch === '\'' || ch === '"') {
|
|
409
|
+
const end = skipString(body, i)
|
|
410
|
+
push(tokens, 'string', body.slice(i, end))
|
|
411
|
+
i = end
|
|
412
|
+
continue
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// `{...}`: ngoặc là dấu câu, ruột quay lại chế độ script.
|
|
416
|
+
if (ch === '{') {
|
|
417
|
+
const end = findMatchingBrace(body, i)
|
|
418
|
+
push(tokens, 'punctuation', '{')
|
|
419
|
+
tokenizeTsx(body.slice(i + 1, end), tokens)
|
|
420
|
+
if (end < body.length)
|
|
421
|
+
push(tokens, 'punctuation', '}')
|
|
422
|
+
i = end + 1
|
|
423
|
+
continue
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const name = /^[\w:.$-]+/.exec(body.slice(i))
|
|
427
|
+
if (name !== null) {
|
|
428
|
+
push(tokens, 'attr', name[0])
|
|
429
|
+
i += name[0].length
|
|
430
|
+
continue
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
push(tokens, 'text', ch)
|
|
434
|
+
i += 1
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/** Chỉ số của `}` khớp với `{` tại `start`; `code.length` nếu không có. */
|
|
439
|
+
function findMatchingBrace(code: string, start: number): number {
|
|
440
|
+
let depth = 0
|
|
441
|
+
let i = start
|
|
442
|
+
|
|
443
|
+
while (i < code.length) {
|
|
444
|
+
const ch = code[i]!
|
|
445
|
+
|
|
446
|
+
if (ch === '\'' || ch === '"' || ch === '`') {
|
|
447
|
+
i = skipString(code, i)
|
|
448
|
+
continue
|
|
449
|
+
}
|
|
450
|
+
if (ch === '{') {
|
|
451
|
+
depth += 1
|
|
452
|
+
}
|
|
453
|
+
else if (ch === '}') {
|
|
454
|
+
depth -= 1
|
|
455
|
+
if (depth === 0)
|
|
456
|
+
return i
|
|
457
|
+
}
|
|
458
|
+
i += 1
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return code.length
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Một phần tử JSX, từ `<` mở tới hết thẻ đóng. Trả về chỉ số ngay sau nó.
|
|
466
|
+
*
|
|
467
|
+
* Đệ quy cho phần tử lồng nhau; `{...}` trong nội dung quay lại chế độ script,
|
|
468
|
+
* nên `{items.map(x => <Row key={x} />)}` được tô đúng ở cả ba tầng.
|
|
469
|
+
*/
|
|
470
|
+
function tokenizeJsxElement(code: string, start: number, tokens: Token[]): number {
|
|
471
|
+
const tagEnd = findTagEnd(code, start)
|
|
472
|
+
const raw = code.slice(start, tagEnd + 1)
|
|
473
|
+
const name = JSX_NAME.exec(raw)?.[1] ?? ''
|
|
474
|
+
|
|
475
|
+
const openLen = 1
|
|
476
|
+
push(tokens, 'punctuation', '<')
|
|
477
|
+
push(tokens, tagKind(name), name)
|
|
478
|
+
|
|
479
|
+
const bodyEnd = raw.endsWith('/>') ? tagEnd - 1 : tagEnd
|
|
480
|
+
tokenizeJsxAttrs(code.slice(start + openLen + name.length, bodyEnd), tokens)
|
|
481
|
+
|
|
482
|
+
if (raw.endsWith('/>')) {
|
|
483
|
+
push(tokens, 'punctuation', '/>')
|
|
484
|
+
return tagEnd + 1
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
push(tokens, 'punctuation', '>')
|
|
488
|
+
|
|
489
|
+
let i = tagEnd + 1
|
|
490
|
+
while (i < code.length) {
|
|
491
|
+
const ch = code[i]!
|
|
492
|
+
|
|
493
|
+
// Thẻ đóng: hết phần tử này.
|
|
494
|
+
if (ch === '<' && code[i + 1] === '/') {
|
|
495
|
+
const end = code.indexOf('>', i)
|
|
496
|
+
const closeName = JSX_NAME.exec(code.slice(i))?.[1] ?? ''
|
|
497
|
+
push(tokens, 'punctuation', '</')
|
|
498
|
+
push(tokens, tagKind(closeName), closeName)
|
|
499
|
+
push(tokens, 'punctuation', '>')
|
|
500
|
+
return end === -1 ? code.length : end + 1
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (ch === '<' && /[A-Z_$>]/i.test(code[i + 1] ?? '')) {
|
|
504
|
+
i = tokenizeJsxElement(code, i, tokens)
|
|
505
|
+
continue
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (ch === '{') {
|
|
509
|
+
const end = findMatchingBrace(code, i)
|
|
510
|
+
push(tokens, 'punctuation', '{')
|
|
511
|
+
tokenizeTsx(code.slice(i + 1, end), tokens)
|
|
512
|
+
if (end < code.length)
|
|
513
|
+
push(tokens, 'punctuation', '}')
|
|
514
|
+
i = end + 1
|
|
515
|
+
continue
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Chữ thường giữa hai thẻ.
|
|
519
|
+
const start2 = i
|
|
520
|
+
while (i < code.length && code[i] !== '<' && code[i] !== '{')
|
|
521
|
+
i += 1
|
|
522
|
+
push(tokens, 'text', code.slice(start2, i))
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return code.length
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Chế độ `tsx`: script và JSX xen kẽ.
|
|
530
|
+
*
|
|
531
|
+
* Đệ quy lẫn nhau với `tokenizeJsxElement` — đó là hình dạng thật của JSX, và
|
|
532
|
+
* cũng là lý do `tokenizeSfc` không dùng lại được.
|
|
533
|
+
*/
|
|
534
|
+
function tokenizeTsx(code: string, tokens: Token[]): void {
|
|
535
|
+
let i = 0
|
|
536
|
+
|
|
537
|
+
while (i < code.length) {
|
|
538
|
+
const next = findJsxStart(code, i)
|
|
539
|
+
|
|
540
|
+
if (next === -1) {
|
|
541
|
+
tokenizeScript(code.slice(i), tokens)
|
|
542
|
+
return
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
tokenizeScript(code.slice(i, next), tokens)
|
|
546
|
+
i = tokenizeJsxElement(code, next, tokens)
|
|
547
|
+
}
|
|
548
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phép dời phần tử trong mảng, dùng chung cho `OrderList` và `PickList`.
|
|
3
|
+
*
|
|
4
|
+
* KHÔNG dính framework nào — cùng lý do `lib/mask` và `lib/tree` nằm ở đây. Bốn
|
|
5
|
+
* component (`OrderList`, `PickList` của cả hai cây) chạy đúng một thuật toán
|
|
6
|
+
* dời chỗ, chứ không phải hai bản hôm nay còn khớp.
|
|
7
|
+
*
|
|
8
|
+
* Hàm thuần, trả về mảng MỚI — không sửa tại chỗ. Danh sách ở đây luôn là giá
|
|
9
|
+
* trị do bên ngoài giữ, mà sửa tại chỗ thì cả hai framework đều không thấy tham
|
|
10
|
+
* chiếu đổi và một nửa số chỗ đọc sẽ không vẽ lại.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Dời một phần tử từ `from` tới `to`. Chỉ số ngoài phạm vi thì trả nguyên mảng. */
|
|
14
|
+
export function moveItem<T>(items: T[], from: number, to: number): T[] {
|
|
15
|
+
if (from === to || from < 0 || from >= items.length)
|
|
16
|
+
return items
|
|
17
|
+
const next = [...items]
|
|
18
|
+
const [moved] = next.splice(from, 1)
|
|
19
|
+
next.splice(Math.max(0, Math.min(to, next.length)), 0, moved)
|
|
20
|
+
return next
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Dời cả một nhóm đang chọn lên hoặc xuống một bậc.
|
|
25
|
+
*
|
|
26
|
+
* `step` là `-1` hoặc `1`. Nhóm giữ nguyên thứ tự tương đối, và một phần tử
|
|
27
|
+
* đang chọn KHÔNG đổi chỗ với một phần tử cũng đang chọn — nếu không thì chọn
|
|
28
|
+
* ba dòng liền nhau rồi bấm lên sẽ đảo lộn chính ba dòng đó.
|
|
29
|
+
*
|
|
30
|
+
* Duyệt theo chiều ngược với hướng dời: đi lên thì duyệt từ đầu, đi xuống thì
|
|
31
|
+
* duyệt từ cuối. Duyệt sai chiều là phần tử vừa dời bị dời tiếp.
|
|
32
|
+
*/
|
|
33
|
+
export function moveSelectionByStep<T>(items: T[], selected: Set<number>, step: -1 | 1): T[] {
|
|
34
|
+
const next = [...items]
|
|
35
|
+
const order = [...selected].sort((a, b) => (step === -1 ? a - b : b - a))
|
|
36
|
+
|
|
37
|
+
for (const index of order) {
|
|
38
|
+
const target = index + step
|
|
39
|
+
if (target < 0 || target >= next.length || selected.has(target))
|
|
40
|
+
continue
|
|
41
|
+
const temp = next[index]
|
|
42
|
+
next[index] = next[target]
|
|
43
|
+
next[target] = temp
|
|
44
|
+
selected.delete(index)
|
|
45
|
+
selected.add(target)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return next
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Dồn nhóm đang chọn về đầu hoặc cuối, giữ nguyên thứ tự tương đối của nhóm. */
|
|
52
|
+
export function moveSelectionToEdge<T>(items: T[], selected: Set<number>, edge: 'start' | 'end'): T[] {
|
|
53
|
+
const picked = items.filter((_, index) => selected.has(index))
|
|
54
|
+
const rest = items.filter((_, index) => !selected.has(index))
|
|
55
|
+
const next = edge === 'start' ? [...picked, ...rest] : [...rest, ...picked]
|
|
56
|
+
|
|
57
|
+
selected.clear()
|
|
58
|
+
const offset = edge === 'start' ? 0 : rest.length
|
|
59
|
+
for (let i = 0; i < picked.length; i += 1)
|
|
60
|
+
selected.add(offset + i)
|
|
61
|
+
|
|
62
|
+
return next
|
|
63
|
+
}
|