@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,168 @@
|
|
|
1
|
+
<script setup lang="ts" generic="TItem">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import { computed, ref } from 'vue'
|
|
4
|
+
import { useI18n } from '../../composables/useI18n'
|
|
5
|
+
import { ChevronDown, ChevronsDown, ChevronsUp, ChevronUp } from '../../lib/icons'
|
|
6
|
+
import { cn } from '../../lib/utils'
|
|
7
|
+
import { moveItem, moveSelectionByStep, moveSelectionToEdge } from '../../variants/lib/list-move'
|
|
8
|
+
import Button from '../button/Button.vue'
|
|
9
|
+
import SelectableList from './SelectableList.vue'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(
|
|
12
|
+
defineProps<ClassProps & {
|
|
13
|
+
itemKey: (item: TItem, index: number) => string | number
|
|
14
|
+
header?: string
|
|
15
|
+
disabled?: boolean
|
|
16
|
+
/** Bật kéo thả. Mấy cái nút vẫn ở đó — xem ghi chú trợ năng. */
|
|
17
|
+
dragging?: boolean
|
|
18
|
+
maxHeight?: string
|
|
19
|
+
emptyMessage?: string
|
|
20
|
+
}>(),
|
|
21
|
+
{ disabled: false, dragging: true },
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
const items = defineModel<TItem[]>({ default: () => [] })
|
|
25
|
+
const selection = defineModel<(string | number)[]>('selection', { default: () => [] })
|
|
26
|
+
|
|
27
|
+
const { t } = useI18n()
|
|
28
|
+
|
|
29
|
+
const keys = computed(() => items.value.map((item, index) => props.itemKey(item, index)))
|
|
30
|
+
|
|
31
|
+
/** Thông báo lần dời gần nhất. Xem ghi chú trợ năng ở template. */
|
|
32
|
+
const announcement = ref('')
|
|
33
|
+
|
|
34
|
+
const selectedIndexes = computed(() => {
|
|
35
|
+
const set = new Set<number>()
|
|
36
|
+
items.value.forEach((item, index) => {
|
|
37
|
+
if (selection.value.includes(props.itemKey(item, index)))
|
|
38
|
+
set.add(index)
|
|
39
|
+
})
|
|
40
|
+
return set
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const canMoveUp = computed(() =>
|
|
44
|
+
selectedIndexes.value.size > 0 && !selectedIndexes.value.has(0),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const canMoveDown = computed(() =>
|
|
48
|
+
selectedIndexes.value.size > 0 && !selectedIndexes.value.has(items.value.length - 1),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
function apply(next: TItem[], indexes: Set<number>): void {
|
|
52
|
+
items.value = next
|
|
53
|
+
selection.value = [...indexes].map(index => props.itemKey(next[index], index))
|
|
54
|
+
announcement.value = t('ui.orderList.moved', { count: indexes.size })
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function step(direction: -1 | 1): void {
|
|
58
|
+
const indexes = new Set(selectedIndexes.value)
|
|
59
|
+
apply(moveSelectionByStep(items.value, indexes, direction), indexes)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function edge(target: 'start' | 'end'): void {
|
|
63
|
+
const indexes = new Set(selectedIndexes.value)
|
|
64
|
+
apply(moveSelectionToEdge(items.value, indexes, target), indexes)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function onDrop(payload: { fromListId: string, fromIndex: number, toIndex: number }): void {
|
|
68
|
+
if (payload.fromListId !== 'order')
|
|
69
|
+
return
|
|
70
|
+
// Bỏ chính mục đang kéo ra khỏi phép đếm: thả xuống ngay dưới chỗ cũ của nó
|
|
71
|
+
// thì chỉ số đích đã bị dịch đi một.
|
|
72
|
+
const to = payload.toIndex > payload.fromIndex ? payload.toIndex - 1 : payload.toIndex
|
|
73
|
+
items.value = moveItem(items.value, payload.fromIndex, to)
|
|
74
|
+
announcement.value = t('ui.orderList.moved', { count: 1 })
|
|
75
|
+
}
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<template>
|
|
79
|
+
<div :class="cn('flex flex-col gap-2', props.class)">
|
|
80
|
+
<p v-if="props.header || $slots.header" class="text-sm font-medium">
|
|
81
|
+
<slot name="header">
|
|
82
|
+
{{ props.header }}
|
|
83
|
+
</slot>
|
|
84
|
+
</p>
|
|
85
|
+
|
|
86
|
+
<div class="flex items-start gap-2">
|
|
87
|
+
<!--
|
|
88
|
+
Bốn cái nút KHÔNG phải phương án dự phòng cho việc kéo thả — chúng là
|
|
89
|
+
cơ chế chính, và kéo thả là phần thêm.
|
|
90
|
+
|
|
91
|
+
WCAG 2.5.7 đòi mọi thao tác kéo phải có một đường thay thế chỉ cần một
|
|
92
|
+
cú chạm. Dựng nút trước rồi thêm kéo sau thì thứ tự đó tự đúng; làm
|
|
93
|
+
ngược lại thì phần trợ năng luôn là thứ được ghép vào sau cùng.
|
|
94
|
+
-->
|
|
95
|
+
<div class="flex flex-col gap-1">
|
|
96
|
+
<Button
|
|
97
|
+
type="button"
|
|
98
|
+
variant="outline"
|
|
99
|
+
size="icon-sm"
|
|
100
|
+
:disabled="props.disabled || !canMoveUp"
|
|
101
|
+
:aria-label="t('ui.orderList.toTop')"
|
|
102
|
+
@click="edge('start')"
|
|
103
|
+
>
|
|
104
|
+
<ChevronsUp aria-hidden="true" />
|
|
105
|
+
</Button>
|
|
106
|
+
<Button
|
|
107
|
+
type="button"
|
|
108
|
+
variant="outline"
|
|
109
|
+
size="icon-sm"
|
|
110
|
+
:disabled="props.disabled || !canMoveUp"
|
|
111
|
+
:aria-label="t('ui.orderList.up')"
|
|
112
|
+
@click="step(-1)"
|
|
113
|
+
>
|
|
114
|
+
<ChevronUp aria-hidden="true" />
|
|
115
|
+
</Button>
|
|
116
|
+
<Button
|
|
117
|
+
type="button"
|
|
118
|
+
variant="outline"
|
|
119
|
+
size="icon-sm"
|
|
120
|
+
:disabled="props.disabled || !canMoveDown"
|
|
121
|
+
:aria-label="t('ui.orderList.down')"
|
|
122
|
+
@click="step(1)"
|
|
123
|
+
>
|
|
124
|
+
<ChevronDown aria-hidden="true" />
|
|
125
|
+
</Button>
|
|
126
|
+
<Button
|
|
127
|
+
type="button"
|
|
128
|
+
variant="outline"
|
|
129
|
+
size="icon-sm"
|
|
130
|
+
:disabled="props.disabled || !canMoveDown"
|
|
131
|
+
:aria-label="t('ui.orderList.toBottom')"
|
|
132
|
+
@click="edge('end')"
|
|
133
|
+
>
|
|
134
|
+
<ChevronsDown aria-hidden="true" />
|
|
135
|
+
</Button>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<SelectableList
|
|
139
|
+
v-model:selection="selection"
|
|
140
|
+
:items="items"
|
|
141
|
+
:keys="keys"
|
|
142
|
+
list-id="order"
|
|
143
|
+
:label="props.header ?? t('ui.orderList.label')"
|
|
144
|
+
:disabled="props.disabled"
|
|
145
|
+
:dragging="props.dragging"
|
|
146
|
+
:max-height="props.maxHeight"
|
|
147
|
+
:empty-message="props.emptyMessage ?? t('ui.table.empty')"
|
|
148
|
+
class="flex-1"
|
|
149
|
+
@move="onDrop"
|
|
150
|
+
>
|
|
151
|
+
<template #item="slotProps">
|
|
152
|
+
<slot name="item" v-bind="slotProps" />
|
|
153
|
+
</template>
|
|
154
|
+
</SelectableList>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<!--
|
|
158
|
+
Dời một dòng là một thay đổi KHÔNG nhìn thấy được nếu bạn không nhìn.
|
|
159
|
+
|
|
160
|
+
Focus không đi đâu cả, chữ trên nút không đổi, và thứ tự mới thì phải đọc
|
|
161
|
+
lại cả danh sách mới biết. Một vùng live nói "đã dời 2 mục" là chỗ duy
|
|
162
|
+
nhất phản hồi đó tồn tại.
|
|
163
|
+
-->
|
|
164
|
+
<p class="sr-only" role="status" aria-live="polite">
|
|
165
|
+
{{ announcement }}
|
|
166
|
+
</p>
|
|
167
|
+
</div>
|
|
168
|
+
</template>
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
<script setup lang="ts" generic="TItem">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
import { cn } from '../../lib/utils'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Danh sách chọn được nhiều mục, kéo thả được — phần chung của `OrderList` và
|
|
8
|
+
* `PickList`. KHÔNG export ra ngoài package: nó là bộ phận, không phải sản phẩm.
|
|
9
|
+
*
|
|
10
|
+
* PickList đúng nghĩa là hai cái này cộng mấy nút chuyển, nên nó nằm ở thư mục
|
|
11
|
+
* của `OrderList` — chỗ nó sinh ra — thay vì được nhân bản ở cả hai nơi.
|
|
12
|
+
*/
|
|
13
|
+
const props = withDefaults(
|
|
14
|
+
defineProps<ClassProps & {
|
|
15
|
+
items: TItem[]
|
|
16
|
+
/**
|
|
17
|
+
* Khoá của từng mục, tính SẴN bởi bên gọi.
|
|
18
|
+
*
|
|
19
|
+
* Không nhận `itemKey` là một hàm: tham số hàm contravariant, nên một
|
|
20
|
+
* `(item: TItem) => …` không gán được vào một prop khai `(item: unknown) => …`,
|
|
21
|
+
* và suy luận generic của Vue rơi về `unknown` khi component này nằm trong
|
|
22
|
+
* một component generic khác. Cùng cái bẫy đã ghi cho `FormField`.
|
|
23
|
+
*
|
|
24
|
+
* Mà con cũng đâu cần biết khoá được tính thế nào — nó chỉ cần khoá.
|
|
25
|
+
*/
|
|
26
|
+
keys: (string | number)[]
|
|
27
|
+
/** Phân biệt danh sách nguồn với danh sách đích lúc thả. */
|
|
28
|
+
listId: string
|
|
29
|
+
/**
|
|
30
|
+
* Tên trợ năng. Đặt tên là `label` chứ KHÔNG phải `ariaLabel`.
|
|
31
|
+
*
|
|
32
|
+
* `:aria-label="…"` trong template không camel-hoá thành prop `ariaLabel`:
|
|
33
|
+
* Vue coi đó là một thuộc tính HTML thật và để nguyên, nên prop vẫn thiếu.
|
|
34
|
+
* Cùng họ với cái bẫy `VisCrosshair` đã ghi ở mục Chart.
|
|
35
|
+
*/
|
|
36
|
+
label: string
|
|
37
|
+
disabled?: boolean
|
|
38
|
+
dragging?: boolean
|
|
39
|
+
maxHeight?: string
|
|
40
|
+
emptyMessage?: string
|
|
41
|
+
}>(),
|
|
42
|
+
{ disabled: false, dragging: true, maxHeight: '220px' },
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const emit = defineEmits<{
|
|
46
|
+
move: [payload: { fromListId: string, fromIndex: number, toIndex: number }]
|
|
47
|
+
}>()
|
|
48
|
+
|
|
49
|
+
/** Khoá của các mục đang chọn. Dùng khoá chứ không dùng chỉ số: chỉ số đổi sau mỗi lần dời. */
|
|
50
|
+
const selection = defineModel<(string | number)[]>('selection', { default: () => [] })
|
|
51
|
+
|
|
52
|
+
const activeIndex = ref(0)
|
|
53
|
+
/** Vị trí mà mục đang kéo sẽ rơi vào. `undefined` là không kéo gì. */
|
|
54
|
+
const dropIndex = ref<number>()
|
|
55
|
+
|
|
56
|
+
function isSelected(index: number): boolean {
|
|
57
|
+
return selection.value.includes(props.keys[index])
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function toggle(index: number, additive: boolean): void {
|
|
61
|
+
if (props.disabled)
|
|
62
|
+
return
|
|
63
|
+
const key = props.keys[index]
|
|
64
|
+
if (!additive) {
|
|
65
|
+
selection.value = isSelected(index) && selection.value.length === 1 ? [] : [key]
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
selection.value = isSelected(index)
|
|
69
|
+
? selection.value.filter(existing => existing !== key)
|
|
70
|
+
: [...selection.value, key]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function focusOption(container: HTMLElement, index: number): void {
|
|
74
|
+
activeIndex.value = index
|
|
75
|
+
container.querySelector<HTMLElement>(`[data-option="${index}"]`)?.focus()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function onKeydown(event: KeyboardEvent): void {
|
|
79
|
+
const container = event.currentTarget as HTMLElement
|
|
80
|
+
const last = props.items.length - 1
|
|
81
|
+
if (last < 0)
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
switch (event.key) {
|
|
85
|
+
case 'ArrowDown':
|
|
86
|
+
event.preventDefault()
|
|
87
|
+
focusOption(container, Math.min(activeIndex.value + 1, last))
|
|
88
|
+
break
|
|
89
|
+
case 'ArrowUp':
|
|
90
|
+
event.preventDefault()
|
|
91
|
+
focusOption(container, Math.max(activeIndex.value - 1, 0))
|
|
92
|
+
break
|
|
93
|
+
case 'Home':
|
|
94
|
+
event.preventDefault()
|
|
95
|
+
focusOption(container, 0)
|
|
96
|
+
break
|
|
97
|
+
case 'End':
|
|
98
|
+
event.preventDefault()
|
|
99
|
+
focusOption(container, last)
|
|
100
|
+
break
|
|
101
|
+
case ' ':
|
|
102
|
+
event.preventDefault()
|
|
103
|
+
toggle(activeIndex.value, true)
|
|
104
|
+
break
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const DRAG_TYPE = 'application/x-woodblock-list'
|
|
109
|
+
|
|
110
|
+
function onDragStart(event: DragEvent, index: number): void {
|
|
111
|
+
if (event.dataTransfer === null)
|
|
112
|
+
return
|
|
113
|
+
event.dataTransfer.effectAllowed = 'move'
|
|
114
|
+
event.dataTransfer.setData(DRAG_TYPE, JSON.stringify({ listId: props.listId, index }))
|
|
115
|
+
// Firefox không bắt đầu kéo nếu không có dữ liệu kiểu text/plain.
|
|
116
|
+
event.dataTransfer.setData('text/plain', String(props.keys[index]))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* `preventDefault()` trong `dragover` là BẮT BUỘC.
|
|
121
|
+
*
|
|
122
|
+
* Mặc định mọi phần tử đều từ chối nhận thả, và cách nói "tôi nhận" là huỷ sự
|
|
123
|
+
* kiện này. Thiếu nó thì `drop` không bao giờ bắn — con trỏ hiện dấu cấm, mà
|
|
124
|
+
* không có lỗi nào cả. Đây là cái bẫy số một của kéo thả gốc.
|
|
125
|
+
*/
|
|
126
|
+
function onDragOver(event: DragEvent, index: number): void {
|
|
127
|
+
event.preventDefault()
|
|
128
|
+
if (event.dataTransfer !== null)
|
|
129
|
+
event.dataTransfer.dropEffect = 'move'
|
|
130
|
+
|
|
131
|
+
// Nửa dưới của một mục nghĩa là thả xuống SAU nó.
|
|
132
|
+
const box = (event.currentTarget as HTMLElement).getBoundingClientRect()
|
|
133
|
+
dropIndex.value = event.clientY - box.top > box.height / 2 ? index + 1 : index
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function onDrop(event: DragEvent): void {
|
|
137
|
+
event.preventDefault()
|
|
138
|
+
const raw = event.dataTransfer?.getData(DRAG_TYPE)
|
|
139
|
+
const to = dropIndex.value
|
|
140
|
+
dropIndex.value = undefined
|
|
141
|
+
if (raw === undefined || raw === '' || to === undefined)
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
const payload = JSON.parse(raw) as { listId: string, index: number }
|
|
145
|
+
emit('move', { fromListId: payload.listId, fromIndex: payload.index, toIndex: to })
|
|
146
|
+
}
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<template>
|
|
150
|
+
<!--
|
|
151
|
+
`listbox` với `aria-multiselectable`: các mục là LỰA CHỌN, không phải nút.
|
|
152
|
+
Trạng thái chọn nằm ở `aria-selected` nên nó được đọc ra, thay vì chỉ là một
|
|
153
|
+
mảng màu nổi mà người không nhìn thấy phải tự đoán.
|
|
154
|
+
-->
|
|
155
|
+
<div
|
|
156
|
+
role="listbox"
|
|
157
|
+
aria-multiselectable="true"
|
|
158
|
+
:aria-label="props.label"
|
|
159
|
+
:aria-disabled="props.disabled || undefined"
|
|
160
|
+
:style="{ maxHeight: props.maxHeight }"
|
|
161
|
+
:class="cn(
|
|
162
|
+
'overflow-y-auto rounded-md border border-border bg-background p-1',
|
|
163
|
+
props.disabled && 'pointer-events-none opacity-50',
|
|
164
|
+
props.class,
|
|
165
|
+
)"
|
|
166
|
+
@keydown="onKeydown"
|
|
167
|
+
@dragover.prevent
|
|
168
|
+
@drop="onDrop"
|
|
169
|
+
>
|
|
170
|
+
<p v-if="props.items.length === 0" class="px-2 py-6 text-center text-sm text-muted-foreground">
|
|
171
|
+
{{ props.emptyMessage }}
|
|
172
|
+
</p>
|
|
173
|
+
|
|
174
|
+
<div
|
|
175
|
+
v-for="(item, index) in props.items"
|
|
176
|
+
:key="props.keys[index]"
|
|
177
|
+
role="option"
|
|
178
|
+
:data-option="index"
|
|
179
|
+
:aria-selected="isSelected(index)"
|
|
180
|
+
:tabindex="index === activeIndex ? 0 : -1"
|
|
181
|
+
:draggable="props.dragging && !props.disabled"
|
|
182
|
+
:class="cn(
|
|
183
|
+
'flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-none transition-colors',
|
|
184
|
+
'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring',
|
|
185
|
+
isSelected(index) ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/50',
|
|
186
|
+
// Vạch chỉ chỗ thả — vẽ bằng viền của chính mục, không chèn thêm thẻ,
|
|
187
|
+
// để danh sách không nhảy một dòng mỗi lần con trỏ đi qua.
|
|
188
|
+
dropIndex === index && 'border-t-2 border-t-primary',
|
|
189
|
+
dropIndex === index + 1 && 'border-b-2 border-b-primary',
|
|
190
|
+
)"
|
|
191
|
+
@click="toggle(index, $event.ctrlKey || $event.metaKey)"
|
|
192
|
+
@focus="activeIndex = index"
|
|
193
|
+
@dragstart="onDragStart($event, index)"
|
|
194
|
+
@dragover="onDragOver($event, index)"
|
|
195
|
+
@dragend="dropIndex = undefined"
|
|
196
|
+
>
|
|
197
|
+
<slot name="item" :item="item" :index="index" :selected="isSelected(index)" />
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as OrderList } from './OrderList.vue'
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import type { ButtonVariants } from '../button'
|
|
4
|
+
import {
|
|
5
|
+
PaginationEllipsis,
|
|
6
|
+
PaginationFirst,
|
|
7
|
+
PaginationLast,
|
|
8
|
+
PaginationList,
|
|
9
|
+
PaginationListItem,
|
|
10
|
+
PaginationNext,
|
|
11
|
+
PaginationPrev,
|
|
12
|
+
PaginationRoot,
|
|
13
|
+
} from 'reka-ui'
|
|
14
|
+
import { computed } from 'vue'
|
|
15
|
+
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, MoreHorizontal } from '../../lib/icons'
|
|
16
|
+
import { cn } from '../../lib/utils'
|
|
17
|
+
import { buttonVariants } from '../button'
|
|
18
|
+
|
|
19
|
+
const props = withDefaults(
|
|
20
|
+
defineProps<ClassProps & {
|
|
21
|
+
/** Tổng số bản ghi, không phải số trang. */
|
|
22
|
+
total?: number
|
|
23
|
+
pageSize?: number
|
|
24
|
+
/** Số nút trang hiện mỗi bên trang hiện tại. */
|
|
25
|
+
siblingCount?: number
|
|
26
|
+
/** Thêm nút về đầu và về cuối. */
|
|
27
|
+
showEdges?: boolean
|
|
28
|
+
/** Dòng "1–20 of 137" ở bên trái. */
|
|
29
|
+
showSummary?: boolean
|
|
30
|
+
size?: Extract<ButtonVariants['size'], 'sm' | 'default' | 'lg'>
|
|
31
|
+
disabled?: boolean
|
|
32
|
+
}>(),
|
|
33
|
+
{
|
|
34
|
+
total: 0,
|
|
35
|
+
pageSize: 20,
|
|
36
|
+
siblingCount: 1,
|
|
37
|
+
showEdges: false,
|
|
38
|
+
showSummary: false,
|
|
39
|
+
size: 'default',
|
|
40
|
+
disabled: false,
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Chỉ số trang tính từ **0**, khớp `TableQuery` của DataTable.
|
|
46
|
+
*
|
|
47
|
+
* Reka đánh số trang từ 1. Nếu để nguyên thì mọi chỗ nối Pagination với
|
|
48
|
+
* DataTable đều phải tự cộng trừ 1 — đúng chỗ sinh ra lỗi lệch một đơn vị. Quy
|
|
49
|
+
* đổi gói gọn ở đây, đúng một lần.
|
|
50
|
+
*/
|
|
51
|
+
const pageIndex = defineModel<number>('pageIndex', { default: 0 })
|
|
52
|
+
|
|
53
|
+
const page = computed({
|
|
54
|
+
get: () => pageIndex.value + 1,
|
|
55
|
+
set: (value: number) => { pageIndex.value = value - 1 },
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
/** Nút số trang là nút vuông; nút trước/sau cùng cỡ để hàng không gợn. */
|
|
59
|
+
const ICON_SIZE = { sm: 'icon-sm', default: 'icon', lg: 'icon-lg' } as const
|
|
60
|
+
const iconSize = computed(() => ICON_SIZE[props.size])
|
|
61
|
+
|
|
62
|
+
const from = computed(() => (props.total === 0 ? 0 : pageIndex.value * props.pageSize + 1))
|
|
63
|
+
const to = computed(() => Math.min((pageIndex.value + 1) * props.pageSize, props.total))
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<template>
|
|
67
|
+
<PaginationRoot
|
|
68
|
+
v-model:page="page"
|
|
69
|
+
:total="props.total"
|
|
70
|
+
:items-per-page="props.pageSize"
|
|
71
|
+
:sibling-count="props.siblingCount"
|
|
72
|
+
:show-edges="props.showEdges"
|
|
73
|
+
:disabled="props.disabled"
|
|
74
|
+
:class="cn('flex w-full flex-wrap items-center justify-between gap-3', props.class)"
|
|
75
|
+
>
|
|
76
|
+
<p v-if="props.showSummary" class="text-sm text-muted-foreground">
|
|
77
|
+
<template v-if="props.total > 0">
|
|
78
|
+
{{ from }}–{{ to }} of {{ props.total }}
|
|
79
|
+
</template>
|
|
80
|
+
<template v-else>
|
|
81
|
+
No results
|
|
82
|
+
</template>
|
|
83
|
+
</p>
|
|
84
|
+
|
|
85
|
+
<!--
|
|
86
|
+
`flex-wrap` ở đây chứ không chỉ ở gốc: bật `showEdges` là thêm hai nút,
|
|
87
|
+
hàng nút vượt 390px và tràn ra ngoài khung ở màn 375px.
|
|
88
|
+
-->
|
|
89
|
+
<PaginationList v-slot="{ items }" class="flex flex-wrap items-center justify-center gap-1">
|
|
90
|
+
<PaginationFirst v-if="props.showEdges" :class="buttonVariants({ variant: 'outline', size: iconSize })">
|
|
91
|
+
<ChevronsLeft />
|
|
92
|
+
</PaginationFirst>
|
|
93
|
+
<PaginationPrev :class="buttonVariants({ variant: 'outline', size: iconSize })">
|
|
94
|
+
<ChevronLeft />
|
|
95
|
+
</PaginationPrev>
|
|
96
|
+
|
|
97
|
+
<template v-for="(item, index) in items">
|
|
98
|
+
<!--
|
|
99
|
+
Trang hiện tại dùng nút ĐẶC, không phải outline: đây là thông tin "bạn
|
|
100
|
+
đang ở đâu", nó phải đọc ra trước các nút xung quanh chứ không chỉ là
|
|
101
|
+
một nút được chọn.
|
|
102
|
+
-->
|
|
103
|
+
<PaginationListItem
|
|
104
|
+
v-if="item.type === 'page'"
|
|
105
|
+
:key="`page-${item.value}`"
|
|
106
|
+
:value="item.value"
|
|
107
|
+
:class="buttonVariants({
|
|
108
|
+
variant: item.value === page ? 'default' : 'outline',
|
|
109
|
+
size: iconSize,
|
|
110
|
+
})"
|
|
111
|
+
>
|
|
112
|
+
{{ item.value }}
|
|
113
|
+
</PaginationListItem>
|
|
114
|
+
|
|
115
|
+
<!-- Dấu ba chấm KHÔNG bấm được, nên không tô như nút. -->
|
|
116
|
+
<PaginationEllipsis
|
|
117
|
+
v-else
|
|
118
|
+
:key="`ellipsis-${index}`"
|
|
119
|
+
class="grid size-8 place-items-center text-muted-foreground"
|
|
120
|
+
>
|
|
121
|
+
<MoreHorizontal class="size-4" />
|
|
122
|
+
</PaginationEllipsis>
|
|
123
|
+
</template>
|
|
124
|
+
|
|
125
|
+
<PaginationNext :class="buttonVariants({ variant: 'outline', size: iconSize })">
|
|
126
|
+
<ChevronRight />
|
|
127
|
+
</PaginationNext>
|
|
128
|
+
<PaginationLast v-if="props.showEdges" :class="buttonVariants({ variant: 'outline', size: iconSize })">
|
|
129
|
+
<ChevronsRight />
|
|
130
|
+
</PaginationLast>
|
|
131
|
+
</PaginationList>
|
|
132
|
+
</PaginationRoot>
|
|
133
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Pagination } from './Pagination.vue'
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from 'reka-ui'
|
|
4
|
+
import { ChevronDown } from '../../lib/icons'
|
|
5
|
+
import { cn } from '../../lib/utils'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(
|
|
8
|
+
defineProps<ClassProps & {
|
|
9
|
+
title?: string
|
|
10
|
+
/** Cho gập lại. Không bật thì tiêu đề không phải là nút. */
|
|
11
|
+
collapsible?: boolean
|
|
12
|
+
defaultOpen?: boolean
|
|
13
|
+
}>(),
|
|
14
|
+
{ collapsible: false, defaultOpen: true },
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const open = defineModel<boolean>('open')
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<!--
|
|
22
|
+
Không gập được thì render `<section>` thường, KHÔNG bọc Collapsible.
|
|
23
|
+
Collapsible luôn dựng một `<button>` cho tiêu đề; tiêu đề bấm vào không làm
|
|
24
|
+
gì là thứ người dùng bàn phím sẽ tab vào rồi mắc kẹt.
|
|
25
|
+
-->
|
|
26
|
+
<CollapsibleRoot
|
|
27
|
+
v-if="props.collapsible"
|
|
28
|
+
v-model:open="open"
|
|
29
|
+
:default-open="props.defaultOpen"
|
|
30
|
+
:class="cn('overflow-hidden rounded-xl border border-border bg-card', props.class)"
|
|
31
|
+
>
|
|
32
|
+
<CollapsibleTrigger
|
|
33
|
+
class="group/panel flex w-full items-center justify-between gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
|
|
34
|
+
>
|
|
35
|
+
<span class="text-sm font-medium">
|
|
36
|
+
<slot name="title">{{ props.title }}</slot>
|
|
37
|
+
</span>
|
|
38
|
+
<span class="flex items-center gap-2">
|
|
39
|
+
<slot name="actions" />
|
|
40
|
+
<ChevronDown
|
|
41
|
+
class="size-4 shrink-0 text-muted-foreground transition-transform duration-(--duration-base) group-data-[state=open]/panel:rotate-180 motion-reduce:transition-none"
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
/>
|
|
44
|
+
</span>
|
|
45
|
+
</CollapsibleTrigger>
|
|
46
|
+
<CollapsibleContent class="overflow-hidden data-[state=open]:animate-collapsible-down data-[state=closed]:animate-collapsible-up">
|
|
47
|
+
<div class="border-t border-border px-4 py-3 text-sm">
|
|
48
|
+
<slot />
|
|
49
|
+
</div>
|
|
50
|
+
</CollapsibleContent>
|
|
51
|
+
</CollapsibleRoot>
|
|
52
|
+
|
|
53
|
+
<section v-else :class="cn('overflow-hidden rounded-xl border border-border bg-card', props.class)">
|
|
54
|
+
<header
|
|
55
|
+
v-if="props.title || $slots.title || $slots.actions"
|
|
56
|
+
class="flex items-center justify-between gap-3 px-4 py-3"
|
|
57
|
+
>
|
|
58
|
+
<h3 class="text-sm font-medium">
|
|
59
|
+
<slot name="title">
|
|
60
|
+
{{ props.title }}
|
|
61
|
+
</slot>
|
|
62
|
+
</h3>
|
|
63
|
+
<slot name="actions" />
|
|
64
|
+
</header>
|
|
65
|
+
<div :class="cn('px-4 py-3 text-sm', (props.title || $slots.title) && 'border-t border-border')">
|
|
66
|
+
<slot />
|
|
67
|
+
</div>
|
|
68
|
+
</section>
|
|
69
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Panel } from './Panel.vue'
|