@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,69 @@
|
|
|
1
|
+
<script setup lang="ts" generic="TData extends RowData">
|
|
2
|
+
import type { RowData, Table } from '@tanstack/vue-table'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import type { DataTableFeatures } from './features'
|
|
5
|
+
import { computed } from 'vue'
|
|
6
|
+
import { useI18n } from '../../composables/useI18n'
|
|
7
|
+
import { ChevronDown } from '../../lib/icons'
|
|
8
|
+
import { cn } from '../../lib/utils'
|
|
9
|
+
import DropdownMenu from '../dropdown-menu/DropdownMenu.vue'
|
|
10
|
+
import DropdownMenuCheckboxItem from '../dropdown-menu/DropdownMenuCheckboxItem.vue'
|
|
11
|
+
import DropdownMenuContent from '../dropdown-menu/DropdownMenuContent.vue'
|
|
12
|
+
import DropdownMenuLabel from '../dropdown-menu/DropdownMenuLabel.vue'
|
|
13
|
+
import DropdownMenuSeparator from '../dropdown-menu/DropdownMenuSeparator.vue'
|
|
14
|
+
import DropdownMenuTrigger from '../dropdown-menu/DropdownMenuTrigger.vue'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Gốc là `DropdownMenu`, một provider KHÔNG vẽ ra DOM — thuộc tính rơi tự do
|
|
18
|
+
* không có chỗ đáp và biến mất hẳn. Chuyển tay xuống chính cái trigger.
|
|
19
|
+
*/
|
|
20
|
+
defineOptions({ inheritAttrs: false })
|
|
21
|
+
|
|
22
|
+
const props = defineProps<ClassProps & {
|
|
23
|
+
table: Table<DataTableFeatures, TData>
|
|
24
|
+
}>()
|
|
25
|
+
const { t } = useI18n()
|
|
26
|
+
/**
|
|
27
|
+
* Chỉ liệt kê cột có thể ẩn. Cột điều khiển (checkbox chọn dòng, nút hành động)
|
|
28
|
+
* nên đặt enableHiding: false trong column def để không lọt vào đây.
|
|
29
|
+
*/
|
|
30
|
+
const hideableColumns = computed(() =>
|
|
31
|
+
props.table.getAllLeafColumns().filter(column => column.getCanHide()),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
function labelOf(columnId: string, header: unknown): string {
|
|
35
|
+
return typeof header === 'string' && header.length > 0 ? header : columnId
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<DropdownMenu>
|
|
41
|
+
<DropdownMenuTrigger
|
|
42
|
+
v-bind="$attrs"
|
|
43
|
+
:class="cn(
|
|
44
|
+
'flex h-(--ui-field-height-sm) items-center gap-1.5 rounded-md border border-border px-(--ui-field-padding-x-sm)',
|
|
45
|
+
'text-[11.5px] font-medium transition-colors hover:bg-muted',
|
|
46
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
47
|
+
props.class,
|
|
48
|
+
)"
|
|
49
|
+
>
|
|
50
|
+
{{ t('ui.table.columnsTrigger') }}
|
|
51
|
+
<ChevronDown class="size-3 text-muted-foreground" />
|
|
52
|
+
</DropdownMenuTrigger>
|
|
53
|
+
|
|
54
|
+
<DropdownMenuContent align="end" class="w-48">
|
|
55
|
+
<DropdownMenuLabel>{{ t('ui.table.columns') }}</DropdownMenuLabel>
|
|
56
|
+
<DropdownMenuSeparator />
|
|
57
|
+
|
|
58
|
+
<DropdownMenuCheckboxItem
|
|
59
|
+
v-for="column in hideableColumns"
|
|
60
|
+
:key="column.id"
|
|
61
|
+
:model-value="column.getIsVisible()"
|
|
62
|
+
@select="(event: Event) => event.preventDefault()"
|
|
63
|
+
@update:model-value="(value: boolean) => column.toggleVisibility(value)"
|
|
64
|
+
>
|
|
65
|
+
{{ labelOf(column.id, column.columnDef.header) }}
|
|
66
|
+
</DropdownMenuCheckboxItem>
|
|
67
|
+
</DropdownMenuContent>
|
|
68
|
+
</DropdownMenu>
|
|
69
|
+
</template>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { CellData, ColumnDef, RowData } from '@tanstack/vue-table'
|
|
2
|
+
import {
|
|
3
|
+
columnFilteringFeature,
|
|
4
|
+
columnPinningFeature,
|
|
5
|
+
columnSizingFeature,
|
|
6
|
+
columnVisibilityFeature,
|
|
7
|
+
createFilteredRowModel,
|
|
8
|
+
createPaginatedRowModel,
|
|
9
|
+
createSortedRowModel,
|
|
10
|
+
filterFn_equals,
|
|
11
|
+
filterFn_includesString,
|
|
12
|
+
filterFn_inNumberRange,
|
|
13
|
+
rowPaginationFeature,
|
|
14
|
+
rowSelectionFeature,
|
|
15
|
+
rowSortingFeature,
|
|
16
|
+
sortFn_alphanumeric,
|
|
17
|
+
sortFn_basic,
|
|
18
|
+
sortFn_datetime,
|
|
19
|
+
sortFn_text,
|
|
20
|
+
tableFeatures,
|
|
21
|
+
} from '@tanstack/vue-table'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Kiểu của `columnDef.meta`.
|
|
25
|
+
*
|
|
26
|
+
* v9 cho khai kiểu này qua slot `columnMeta` của tableFeatures — dùng cách đó
|
|
27
|
+
* thay vì `declare module`: augment sẽ cần `@tanstack/table-core` là dependency
|
|
28
|
+
* trực tiếp, mà nó chỉ là dependency gián tiếp của `@tanstack/vue-table`.
|
|
29
|
+
*/
|
|
30
|
+
export interface DataTableColumnMeta {
|
|
31
|
+
/**
|
|
32
|
+
* Căn chữ cho cả header và ô. Mặc định 'left'.
|
|
33
|
+
*
|
|
34
|
+
* `'number'` không phải một kiểu căn — nó nói cột này CHỨA SỐ, và kéo theo
|
|
35
|
+
* hai thứ luôn đi cùng nhau: căn phải + `tabular-nums`.
|
|
36
|
+
*
|
|
37
|
+
* Tách riêng khỏi `'right'` vì `'right'` một mình là chưa đủ: font mặc định
|
|
38
|
+
* (Geist) dùng chữ số tỉ lệ, nên `1.111` hẹp hơn `8.888` và cột số căn phải
|
|
39
|
+
* vẫn so le từng hàng. Phải có tabular figures thì các chữ số mới xếp thành
|
|
40
|
+
* lưới dọc quét được bằng mắt — vốn là lý do duy nhất để căn phải cột số.
|
|
41
|
+
*
|
|
42
|
+
* Để bên gọi tự nhớ `class: 'text-right tabular-nums'` thì kiểu gì cũng có
|
|
43
|
+
* bảng quên một nửa.
|
|
44
|
+
*/
|
|
45
|
+
align?: 'left' | 'center' | 'right' | 'number'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* v9 không bật feature nào sẵn — phải khai báo tường minh.
|
|
50
|
+
* Chỉ đăng ký đúng những gì DataTable cần, để bundle không kéo theo
|
|
51
|
+
* grouping / expanding / resizing khi không dùng.
|
|
52
|
+
*
|
|
53
|
+
* Row model client-side vẫn đăng ký cả khi chạy server-side: ở chế độ
|
|
54
|
+
* serverSide ta bật manual* nên các row model đó bị bỏ qua, đổi lại
|
|
55
|
+
* một component duy nhất phục vụ được cả hai chế độ.
|
|
56
|
+
*/
|
|
57
|
+
export const dataTableFeatures = tableFeatures({
|
|
58
|
+
columnVisibilityFeature,
|
|
59
|
+
columnPinningFeature,
|
|
60
|
+
columnSizingFeature,
|
|
61
|
+
columnFilteringFeature,
|
|
62
|
+
filteredRowModel: createFilteredRowModel(),
|
|
63
|
+
filterFns: {
|
|
64
|
+
includesString: filterFn_includesString,
|
|
65
|
+
equals: filterFn_equals,
|
|
66
|
+
inNumberRange: filterFn_inNumberRange,
|
|
67
|
+
},
|
|
68
|
+
rowSortingFeature,
|
|
69
|
+
sortedRowModel: createSortedRowModel(),
|
|
70
|
+
sortFns: {
|
|
71
|
+
alphanumeric: sortFn_alphanumeric,
|
|
72
|
+
text: sortFn_text,
|
|
73
|
+
datetime: sortFn_datetime,
|
|
74
|
+
basic: sortFn_basic,
|
|
75
|
+
},
|
|
76
|
+
rowPaginationFeature,
|
|
77
|
+
paginatedRowModel: createPaginatedRowModel(),
|
|
78
|
+
rowSelectionFeature,
|
|
79
|
+
|
|
80
|
+
columnMeta: {} as DataTableColumnMeta,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
export type DataTableFeatures = typeof dataTableFeatures
|
|
84
|
+
|
|
85
|
+
/** Kiểu column def đã gắn sẵn feature set — dùng ở app thay cho ColumnDef thô. */
|
|
86
|
+
export type DataTableColumn<TData extends RowData, TValue extends CellData = CellData>
|
|
87
|
+
= ColumnDef<DataTableFeatures, TData, TValue>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export { default as DataTable } from './DataTable.vue'
|
|
2
|
+
export { default as DataTableFacetedFilter } from './DataTableFacetedFilter.vue'
|
|
3
|
+
export type { FacetOption } from './DataTableFacetedFilter.vue'
|
|
4
|
+
export { default as DataTablePagination } from './DataTablePagination.vue'
|
|
5
|
+
export { default as DataTableViewOptions } from './DataTableViewOptions.vue'
|
|
6
|
+
export type { DataTableColumn, DataTableFeatures } from './features'
|
|
7
|
+
export { dataTableFeatures } from './features'
|
|
8
|
+
export {
|
|
9
|
+
createCustomAdapter,
|
|
10
|
+
createPageNumberAdapter,
|
|
11
|
+
createSkipTakeAdapter,
|
|
12
|
+
} from './pagination-adapter'
|
|
13
|
+
export { createSelectionColumn } from './selection-column'
|
|
14
|
+
export type {
|
|
15
|
+
PageNumberParams,
|
|
16
|
+
PaginationAdapter,
|
|
17
|
+
SkipTakeParams,
|
|
18
|
+
TableQuery,
|
|
19
|
+
} from './types'
|
|
20
|
+
// Re-export type state của TanStack: app không được import thẳng
|
|
21
|
+
// @tanstack/vue-table (nó không phải dependency của apps/admin).
|
|
22
|
+
export type {
|
|
23
|
+
ColumnFiltersState,
|
|
24
|
+
PaginationState,
|
|
25
|
+
RowSelectionState,
|
|
26
|
+
SortingState,
|
|
27
|
+
} from '@tanstack/vue-table'
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PageNumberParams,
|
|
3
|
+
PaginationAdapter,
|
|
4
|
+
SkipTakeParams,
|
|
5
|
+
TableQuery,
|
|
6
|
+
} from './types'
|
|
7
|
+
|
|
8
|
+
function toFilterRecord(query: TableQuery): Record<string, unknown> | undefined {
|
|
9
|
+
if (query.filters.length === 0)
|
|
10
|
+
return undefined
|
|
11
|
+
|
|
12
|
+
return Object.fromEntries(query.filters.map(f => [f.id, f.value]))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Backend nhận `page` (1-based), `pageSize`, `sortBy`, `sortDir`.
|
|
17
|
+
*
|
|
18
|
+
* const adapter = createPageNumberAdapter()
|
|
19
|
+
* const params = adapter.toParams(query)
|
|
20
|
+
* // { page: 1, pageSize: 20, sortBy: 'createdAt', sortDir: 'desc' }
|
|
21
|
+
*/
|
|
22
|
+
export function createPageNumberAdapter(): PaginationAdapter<PageNumberParams> {
|
|
23
|
+
return {
|
|
24
|
+
toParams(query) {
|
|
25
|
+
const primarySort = query.sorting[0]
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
page: query.pageIndex + 1,
|
|
29
|
+
pageSize: query.pageSize,
|
|
30
|
+
sortBy: primarySort?.id,
|
|
31
|
+
sortDir: primarySort ? (primarySort.desc ? 'desc' : 'asc') : undefined,
|
|
32
|
+
filters: toFilterRecord(query),
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Backend nhận `skip` / `take` và chuỗi `orderBy` kiểu OData ("name desc").
|
|
40
|
+
*/
|
|
41
|
+
export function createSkipTakeAdapter(): PaginationAdapter<SkipTakeParams> {
|
|
42
|
+
return {
|
|
43
|
+
toParams(query) {
|
|
44
|
+
const orderBy = query.sorting
|
|
45
|
+
.map(sort => `${sort.id} ${sort.desc ? 'desc' : 'asc'}`)
|
|
46
|
+
.join(', ')
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
skip: query.pageIndex * query.pageSize,
|
|
50
|
+
take: query.pageSize,
|
|
51
|
+
orderBy: orderBy.length > 0 ? orderBy : undefined,
|
|
52
|
+
filters: toFilterRecord(query),
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Khi backend có quy ước riêng: tự viết hàm map, vẫn giữ được kiểu.
|
|
60
|
+
*
|
|
61
|
+
* const adapter = createCustomAdapter(q => ({ offset: q.pageIndex * q.pageSize }))
|
|
62
|
+
*/
|
|
63
|
+
export function createCustomAdapter<TParams>(
|
|
64
|
+
toParams: (query: TableQuery) => TParams,
|
|
65
|
+
): PaginationAdapter<TParams> {
|
|
66
|
+
return { toParams }
|
|
67
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { RowData } from '@tanstack/vue-table'
|
|
2
|
+
import type { DataTableColumn } from './features'
|
|
3
|
+
import { h } from 'vue'
|
|
4
|
+
import { translate } from '../../composables/useI18n'
|
|
5
|
+
import Checkbox from '../checkbox/Checkbox.vue'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Cột checkbox chọn dòng. Đặt ở đầu mảng columns:
|
|
9
|
+
*
|
|
10
|
+
* const columns = [createSelectionColumn<User>(), ...]
|
|
11
|
+
*
|
|
12
|
+
* Cột này không sort / không ẩn được / không lọt vào menu "Cột".
|
|
13
|
+
*
|
|
14
|
+
* Nhãn đi qua `translate` chứ không `useI18n`: đây là hàm thường, không phải
|
|
15
|
+
* component, nên không gọi được composable có `onMounted`. Vẫn reactive vì hai
|
|
16
|
+
* hàm render bên dưới chạy trong effect render của bảng.
|
|
17
|
+
*/
|
|
18
|
+
export function createSelectionColumn<TData extends RowData>(): DataTableColumn<TData> {
|
|
19
|
+
return {
|
|
20
|
+
id: '__select',
|
|
21
|
+
size: 40,
|
|
22
|
+
enableSorting: false,
|
|
23
|
+
enableHiding: false,
|
|
24
|
+
header: ({ table }) =>
|
|
25
|
+
h(Checkbox, {
|
|
26
|
+
'modelValue': table.getIsAllPageRowsSelected()
|
|
27
|
+
? true
|
|
28
|
+
: table.getIsSomePageRowsSelected()
|
|
29
|
+
? 'indeterminate'
|
|
30
|
+
: false,
|
|
31
|
+
'aria-label': translate('ui.table.selectAll'),
|
|
32
|
+
'onUpdate:modelValue': (value: boolean | 'indeterminate') => {
|
|
33
|
+
table.toggleAllPageRowsSelected(value === true)
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
cell: ({ row }) =>
|
|
37
|
+
h(Checkbox, {
|
|
38
|
+
'modelValue': row.getIsSelected(),
|
|
39
|
+
'disabled': !row.getCanSelect(),
|
|
40
|
+
'aria-label': translate('ui.table.selectRow'),
|
|
41
|
+
'onUpdate:modelValue': (value: boolean | 'indeterminate') => {
|
|
42
|
+
row.toggleSelected(value === true)
|
|
43
|
+
},
|
|
44
|
+
// Chọn dòng không được kéo theo sự kiện click của cả hàng.
|
|
45
|
+
'onClick': (event: Event) => event.stopPropagation(),
|
|
46
|
+
}),
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ColumnFiltersState, SortingState } from '@tanstack/vue-table'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Trạng thái truy vấn mà DataTable phát ra. Cố tình trung lập với backend:
|
|
5
|
+
* DataTable không biết .NET dùng page/pageSize hay skip/take.
|
|
6
|
+
* Việc dịch sang tham số thật do adapter lo (xem pagination-adapter.ts).
|
|
7
|
+
*/
|
|
8
|
+
export interface TableQuery {
|
|
9
|
+
/** 0-based, dùng nguyên bản của TanStack */
|
|
10
|
+
pageIndex: number
|
|
11
|
+
pageSize: number
|
|
12
|
+
sorting: SortingState
|
|
13
|
+
filters: ColumnFiltersState
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Tham số kiểu page number — PagedList, X.PagedList... */
|
|
17
|
+
export interface PageNumberParams {
|
|
18
|
+
page: number
|
|
19
|
+
pageSize: number
|
|
20
|
+
sortBy?: string
|
|
21
|
+
sortDir?: 'asc' | 'desc'
|
|
22
|
+
filters?: Record<string, unknown>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Tham số kiểu offset — OData, EF Core raw. */
|
|
26
|
+
export interface SkipTakeParams {
|
|
27
|
+
skip: number
|
|
28
|
+
take: number
|
|
29
|
+
orderBy?: string
|
|
30
|
+
filters?: Record<string, unknown>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface PaginationAdapter<TParams> {
|
|
34
|
+
/** Biến TableQuery thành query string params gửi cho API. */
|
|
35
|
+
toParams: (query: TableQuery) => TParams
|
|
36
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<script setup lang="ts" generic="TItem">
|
|
2
|
+
import type { ClassProps } from '../../lib/types'
|
|
3
|
+
import type { TableQuery } from '../data-table/types'
|
|
4
|
+
import { computed, ref, watch } from 'vue'
|
|
5
|
+
import { useI18n } from '../../composables/useI18n'
|
|
6
|
+
import { LayoutGrid, List } from '../../lib/icons'
|
|
7
|
+
import { cn } from '../../lib/utils'
|
|
8
|
+
import Button from '../button/Button.vue'
|
|
9
|
+
import Empty from '../empty/Empty.vue'
|
|
10
|
+
import Pagination from '../pagination/Pagination.vue'
|
|
11
|
+
import Spinner from '../spinner/Spinner.vue'
|
|
12
|
+
|
|
13
|
+
const props = withDefaults(
|
|
14
|
+
defineProps<ClassProps & {
|
|
15
|
+
items: TItem[]
|
|
16
|
+
/** Khoá duy nhất của một mục. Bắt buộc — `index` không sống sót qua sắp xếp. */
|
|
17
|
+
itemKey: (item: TItem, index: number) => string | number
|
|
18
|
+
/** Backend lo phân trang; khi đó `totalRecords` là bắt buộc. */
|
|
19
|
+
serverSide?: boolean
|
|
20
|
+
totalRecords?: number
|
|
21
|
+
pageSize?: number
|
|
22
|
+
paginate?: boolean
|
|
23
|
+
loading?: boolean
|
|
24
|
+
emptyMessage?: string
|
|
25
|
+
/** Cho người dùng đổi giữa danh sách và lưới. */
|
|
26
|
+
switchable?: boolean
|
|
27
|
+
/** Lớp lưới khi ở chế độ `grid`. Số cột là chuyện của trang, không phải của đây. */
|
|
28
|
+
gridClass?: string
|
|
29
|
+
}>(),
|
|
30
|
+
{
|
|
31
|
+
serverSide: false,
|
|
32
|
+
pageSize: 12,
|
|
33
|
+
paginate: true,
|
|
34
|
+
loading: false,
|
|
35
|
+
switchable: true,
|
|
36
|
+
gridClass: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',
|
|
37
|
+
},
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const emit = defineEmits<{ 'page-change': [query: TableQuery] }>()
|
|
41
|
+
|
|
42
|
+
const layout = defineModel<'list' | 'grid'>('layout', { default: 'list' })
|
|
43
|
+
|
|
44
|
+
const { t } = useI18n()
|
|
45
|
+
|
|
46
|
+
const pageIndex = ref(0)
|
|
47
|
+
|
|
48
|
+
const total = computed(() =>
|
|
49
|
+
props.serverSide ? props.totalRecords ?? 0 : props.items.length,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Chế độ server-side thì KHÔNG tự cắt mảng.
|
|
54
|
+
*
|
|
55
|
+
* Cắt tiếp một trang mà backend đã cắt sẵn là hiện ra một trang của một trang —
|
|
56
|
+
* lỗi im lặng vì nó chỉ lộ ra khi tổng số vượt quá `pageSize`.
|
|
57
|
+
*/
|
|
58
|
+
const visible = computed(() => {
|
|
59
|
+
if (props.serverSide || !props.paginate)
|
|
60
|
+
return props.items
|
|
61
|
+
const start = pageIndex.value * props.pageSize
|
|
62
|
+
return props.items.slice(start, start + props.pageSize)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
/** Cùng hình dạng `TableQuery` mà DataTable phát ra, nên dùng chung adapter. */
|
|
66
|
+
function query(): TableQuery {
|
|
67
|
+
return { pageIndex: pageIndex.value, pageSize: props.pageSize, sorting: [], filters: [] }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
watch(pageIndex, () => emit('page-change', query()))
|
|
71
|
+
|
|
72
|
+
// Lọc lại ở tầng trên làm số trang co lại; đứng ở trang 7 của một danh sách còn
|
|
73
|
+
// 2 trang thì màn hình trống trơn mà không có gì nói vì sao.
|
|
74
|
+
watch(total, (value) => {
|
|
75
|
+
const last = Math.max(0, Math.ceil(value / props.pageSize) - 1)
|
|
76
|
+
if (pageIndex.value > last)
|
|
77
|
+
pageIndex.value = last
|
|
78
|
+
})
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<template>
|
|
82
|
+
<div :class="cn('flex flex-col gap-4', props.class)">
|
|
83
|
+
<div v-if="props.switchable || $slots.toolbar" class="flex items-center justify-between gap-3">
|
|
84
|
+
<slot name="toolbar" />
|
|
85
|
+
<div v-if="props.switchable" class="ml-auto flex items-center gap-1">
|
|
86
|
+
<Button
|
|
87
|
+
v-for="mode in (['list', 'grid'] as const)"
|
|
88
|
+
:key="mode"
|
|
89
|
+
type="button"
|
|
90
|
+
:variant="layout === mode ? 'secondary' : 'ghost'"
|
|
91
|
+
size="icon-sm"
|
|
92
|
+
:aria-pressed="layout === mode"
|
|
93
|
+
:aria-label="t(`ui.dataView.${mode}`)"
|
|
94
|
+
@click="layout = mode"
|
|
95
|
+
>
|
|
96
|
+
<List v-if="mode === 'list'" aria-hidden="true" />
|
|
97
|
+
<LayoutGrid v-else aria-hidden="true" />
|
|
98
|
+
</Button>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div v-if="props.loading" class="flex items-center justify-center py-10">
|
|
103
|
+
<Spinner class="text-muted-foreground" />
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<slot v-else-if="visible.length === 0" name="empty">
|
|
107
|
+
<Empty :title="props.emptyMessage ?? t('ui.table.empty')" />
|
|
108
|
+
</slot>
|
|
109
|
+
|
|
110
|
+
<!--
|
|
111
|
+
`role="list"` khai TƯỜNG MINH, dù đây đã là một `<ul>`.
|
|
112
|
+
|
|
113
|
+
Preflight của Tailwind đặt `list-style: none`, và Safari bỏ ngữ nghĩa danh
|
|
114
|
+
sách của một `<ul>` không có dấu đầu dòng — nên số mục không còn được đọc
|
|
115
|
+
ra. Khai lại role là cách chữa đã biết, và nó chẳng tốn gì ở nơi khác.
|
|
116
|
+
-->
|
|
117
|
+
<ul
|
|
118
|
+
v-else
|
|
119
|
+
role="list"
|
|
120
|
+
:class="cn(layout === 'grid' ? cn('grid gap-4', props.gridClass) : 'flex flex-col gap-3')"
|
|
121
|
+
>
|
|
122
|
+
<li v-for="(item, index) in visible" :key="props.itemKey(item, index)">
|
|
123
|
+
<slot name="item" :item="item" :index="index" :layout="layout" />
|
|
124
|
+
</li>
|
|
125
|
+
</ul>
|
|
126
|
+
|
|
127
|
+
<Pagination
|
|
128
|
+
v-if="props.paginate && total > props.pageSize"
|
|
129
|
+
v-model:page-index="pageIndex"
|
|
130
|
+
:total="total"
|
|
131
|
+
:page-size="props.pageSize"
|
|
132
|
+
show-summary
|
|
133
|
+
class="justify-between"
|
|
134
|
+
/>
|
|
135
|
+
</div>
|
|
136
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as DataView } from './DataView.vue'
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DateValue } from '@internationalized/date'
|
|
3
|
+
import type { ClassProps } from '../../lib/types'
|
|
4
|
+
import { DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
|
5
|
+
import { computed, ref } from 'vue'
|
|
6
|
+
import { useI18n } from '../../composables/useI18n'
|
|
7
|
+
import { invalidRingStatic } from '../../lib/focus'
|
|
8
|
+
import { CalendarIcon } from '../../lib/icons'
|
|
9
|
+
import { cn } from '../../lib/utils'
|
|
10
|
+
import { Button } from '../button'
|
|
11
|
+
import { Calendar } from '../calendar'
|
|
12
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../popover'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Gốc của component là một provider KHÔNG vẽ ra DOM, nên thuộc tính rơi tự do
|
|
16
|
+
* không có chỗ đáp và biến mất hẳn. Đo được: đặt `id` lên component thì nó
|
|
17
|
+
* không xuất hiện ở bất kỳ đâu trong DOM, và `<Label for>` trỏ vào hư không.
|
|
18
|
+
*
|
|
19
|
+
* Chuyển tay xuống chính cái trigger.
|
|
20
|
+
*/
|
|
21
|
+
defineOptions({ inheritAttrs: false })
|
|
22
|
+
|
|
23
|
+
const props = withDefaults(
|
|
24
|
+
defineProps<ClassProps & {
|
|
25
|
+
placeholder?: string
|
|
26
|
+
locale?: string
|
|
27
|
+
/** Kiểu hiện ngày trên nút. `long` ra "31 tháng 8, 2026". */
|
|
28
|
+
dateStyle?: 'short' | 'medium' | 'long'
|
|
29
|
+
size?: 'sm' | 'default'
|
|
30
|
+
invalid?: boolean
|
|
31
|
+
disabled?: boolean
|
|
32
|
+
minValue?: DateValue
|
|
33
|
+
maxValue?: DateValue
|
|
34
|
+
}>(),
|
|
35
|
+
{
|
|
36
|
+
dateStyle: 'medium',
|
|
37
|
+
size: 'default',
|
|
38
|
+
invalid: false,
|
|
39
|
+
disabled: false,
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const { t, tag } = useI18n()
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Cả hai mặc định phải đi qua i18n, không đặt được trong `withDefaults`.
|
|
47
|
+
*
|
|
48
|
+
* `locale` cũng vậy chứ không riêng `placeholder`: chốt cứng 'vi-VN' thì đổi
|
|
49
|
+
* sang tiếng Anh xong nút vẫn hiện "31 tháng 8, 2026". Prop vẫn thắng khi có —
|
|
50
|
+
* hai picker trên cùng một trang được phép khác locale.
|
|
51
|
+
*/
|
|
52
|
+
const placeholder = computed(() => props.placeholder ?? t('ui.datePicker.placeholder'))
|
|
53
|
+
const locale = computed(() => props.locale ?? tag.value)
|
|
54
|
+
|
|
55
|
+
const model = defineModel<DateValue | undefined>()
|
|
56
|
+
|
|
57
|
+
const open = ref(false)
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* `DateFormatter` tạo lại mỗi khi đổi locale hoặc kiểu hiện — không cache sẵn ở
|
|
61
|
+
* cấp module, vì locale là prop và có thể khác nhau giữa hai picker trên cùng
|
|
62
|
+
* một trang.
|
|
63
|
+
*/
|
|
64
|
+
const formatter = computed(() => new DateFormatter(locale.value, { dateStyle: props.dateStyle }))
|
|
65
|
+
|
|
66
|
+
const label = computed(() =>
|
|
67
|
+
model.value ? formatter.value.format(model.value.toDate(getLocalTimeZone())) : placeholder.value,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Chọn xong thì đóng.
|
|
72
|
+
*
|
|
73
|
+
* Reka không tự đóng — nó không biết popover có tồn tại hay không. Để mở là
|
|
74
|
+
* người dùng phải tự bấm ra ngoài sau mỗi lần chọn, mà thao tác chọn ngày thì
|
|
75
|
+
* gần như luôn là thao tác cuối.
|
|
76
|
+
*/
|
|
77
|
+
function onUpdate(value: DateValue | undefined): void {
|
|
78
|
+
model.value = value
|
|
79
|
+
if (value)
|
|
80
|
+
open.value = false
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<template>
|
|
85
|
+
<Popover v-model:open="open">
|
|
86
|
+
<PopoverTrigger as-child>
|
|
87
|
+
<Button
|
|
88
|
+
v-bind="$attrs"
|
|
89
|
+
variant="outline"
|
|
90
|
+
:size="props.size === 'sm' ? 'sm' : 'lg'"
|
|
91
|
+
:disabled="props.disabled"
|
|
92
|
+
:aria-invalid="props.invalid || undefined"
|
|
93
|
+
:class="cn(
|
|
94
|
+
'w-full justify-start gap-2 font-normal',
|
|
95
|
+
// Nút mặc định canh giữa; ở đây là một ô nhập nên chữ phải bám trái.
|
|
96
|
+
!model && 'text-muted-foreground',
|
|
97
|
+
props.invalid && invalidRingStatic,
|
|
98
|
+
props.class,
|
|
99
|
+
)"
|
|
100
|
+
>
|
|
101
|
+
<CalendarIcon class="text-muted-foreground" />
|
|
102
|
+
{{ label }}
|
|
103
|
+
</Button>
|
|
104
|
+
</PopoverTrigger>
|
|
105
|
+
|
|
106
|
+
<PopoverContent class="w-auto p-0" align="start">
|
|
107
|
+
<Calendar
|
|
108
|
+
:model-value="model"
|
|
109
|
+
:locale="locale"
|
|
110
|
+
:min-value="props.minValue"
|
|
111
|
+
:max-value="props.maxValue"
|
|
112
|
+
class="p-3"
|
|
113
|
+
@update:model-value="onUpdate"
|
|
114
|
+
/>
|
|
115
|
+
</PopoverContent>
|
|
116
|
+
</Popover>
|
|
117
|
+
</template>
|