create-nala 3.0.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/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/index.js +227 -0
- package/package.json +48 -0
- package/template/.env.example +45 -0
- package/template/_gitignore +33 -0
- package/template/components.json +24 -0
- package/template/index.html +30 -0
- package/template/package.json +51 -0
- package/template/public/favicon.svg +15 -0
- package/template/public/icons.svg +24 -0
- package/template/public/img/avatar.webp +0 -0
- package/template/public/screenshot.png +0 -0
- package/template/src/App.vue +15 -0
- package/template/src/auto-imports.d.ts +691 -0
- package/template/src/components/AppLogo.vue +159 -0
- package/template/src/components/EmptyState.vue +60 -0
- package/template/src/components/PageHeader.vue +60 -0
- package/template/src/components/layout/AppNavbar.vue +159 -0
- package/template/src/components/layout/AppSidebar.vue +74 -0
- package/template/src/components/layout/CommandSearchDialog.vue +120 -0
- package/template/src/components/ui/accordion/Accordion.vue +15 -0
- package/template/src/components/ui/accordion/AccordionContent.vue +20 -0
- package/template/src/components/ui/accordion/AccordionItem.vue +20 -0
- package/template/src/components/ui/accordion/AccordionTrigger.vue +34 -0
- package/template/src/components/ui/accordion/index.ts +4 -0
- package/template/src/components/ui/alert/Alert.vue +21 -0
- package/template/src/components/ui/alert/AlertDescription.vue +17 -0
- package/template/src/components/ui/alert/AlertTitle.vue +17 -0
- package/template/src/components/ui/alert/index.ts +30 -0
- package/template/src/components/ui/alert-dialog/AlertDialog.vue +15 -0
- package/template/src/components/ui/alert-dialog/AlertDialogAction.vue +18 -0
- package/template/src/components/ui/alert-dialog/AlertDialogCancel.vue +25 -0
- package/template/src/components/ui/alert-dialog/AlertDialogContent.vue +44 -0
- package/template/src/components/ui/alert-dialog/AlertDialogDescription.vue +23 -0
- package/template/src/components/ui/alert-dialog/AlertDialogFooter.vue +22 -0
- package/template/src/components/ui/alert-dialog/AlertDialogHeader.vue +17 -0
- package/template/src/components/ui/alert-dialog/AlertDialogTitle.vue +21 -0
- package/template/src/components/ui/alert-dialog/AlertDialogTrigger.vue +12 -0
- package/template/src/components/ui/alert-dialog/index.ts +9 -0
- package/template/src/components/ui/avatar/Avatar.vue +50 -0
- package/template/src/components/ui/avatar/AvatarFallback.vue +21 -0
- package/template/src/components/ui/avatar/AvatarGroup.vue +69 -0
- package/template/src/components/ui/avatar/AvatarImage.vue +16 -0
- package/template/src/components/ui/avatar/index.ts +4 -0
- package/template/src/components/ui/badge/Badge.vue +40 -0
- package/template/src/components/ui/badge/index.ts +45 -0
- package/template/src/components/ui/breadcrumb/Breadcrumb.vue +17 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbLink.vue +21 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbList.vue +17 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
- package/template/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
- package/template/src/components/ui/breadcrumb/index.ts +7 -0
- package/template/src/components/ui/button/Button.spec.ts +46 -0
- package/template/src/components/ui/button/Button.vue +31 -0
- package/template/src/components/ui/button/index.ts +41 -0
- package/template/src/components/ui/calendar/Calendar.vue +106 -0
- package/template/src/components/ui/calendar/index.ts +1 -0
- package/template/src/components/ui/card/Card.vue +29 -0
- package/template/src/components/ui/card/CardAction.vue +17 -0
- package/template/src/components/ui/card/CardContent.vue +17 -0
- package/template/src/components/ui/card/CardDescription.vue +17 -0
- package/template/src/components/ui/card/CardFooter.vue +17 -0
- package/template/src/components/ui/card/CardHeader.vue +29 -0
- package/template/src/components/ui/card/CardTitle.vue +17 -0
- package/template/src/components/ui/card/index.ts +7 -0
- package/template/src/components/ui/chart/AreaChart.vue +143 -0
- package/template/src/components/ui/chart/BarChart.vue +142 -0
- package/template/src/components/ui/chart/DonutChart.vue +75 -0
- package/template/src/components/ui/chart/LineChart.vue +132 -0
- package/template/src/components/ui/chart/index.ts +4 -0
- package/template/src/components/ui/checkbox/Checkbox.vue +57 -0
- package/template/src/components/ui/checkbox/index.ts +1 -0
- package/template/src/components/ui/collapsible/Collapsible.vue +15 -0
- package/template/src/components/ui/collapsible/CollapsibleContent.vue +18 -0
- package/template/src/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
- package/template/src/components/ui/collapsible/index.ts +3 -0
- package/template/src/components/ui/command/Command.vue +88 -0
- package/template/src/components/ui/command/CommandDialog.vue +31 -0
- package/template/src/components/ui/command/CommandEmpty.vue +27 -0
- package/template/src/components/ui/command/CommandGroup.vue +45 -0
- package/template/src/components/ui/command/CommandInput.vue +39 -0
- package/template/src/components/ui/command/CommandItem.vue +76 -0
- package/template/src/components/ui/command/CommandList.vue +25 -0
- package/template/src/components/ui/command/CommandSeparator.vue +21 -0
- package/template/src/components/ui/command/CommandShortcut.vue +17 -0
- package/template/src/components/ui/command/index.ts +25 -0
- package/template/src/components/ui/context-menu/ContextMenu.vue +15 -0
- package/template/src/components/ui/context-menu/ContextMenuCheckboxItem.vue +36 -0
- package/template/src/components/ui/context-menu/ContextMenuContent.vue +33 -0
- package/template/src/components/ui/context-menu/ContextMenuGroup.vue +12 -0
- package/template/src/components/ui/context-menu/ContextMenuItem.vue +36 -0
- package/template/src/components/ui/context-menu/ContextMenuLabel.vue +25 -0
- package/template/src/components/ui/context-menu/ContextMenuRadioGroup.vue +18 -0
- package/template/src/components/ui/context-menu/ContextMenuRadioItem.vue +36 -0
- package/template/src/components/ui/context-menu/ContextMenuSeparator.vue +18 -0
- package/template/src/components/ui/context-menu/ContextMenuShortcut.vue +17 -0
- package/template/src/components/ui/context-menu/ContextMenuSub.vue +15 -0
- package/template/src/components/ui/context-menu/ContextMenuSubContent.vue +36 -0
- package/template/src/components/ui/context-menu/ContextMenuSubTrigger.vue +32 -0
- package/template/src/components/ui/context-menu/ContextMenuTrigger.vue +13 -0
- package/template/src/components/ui/context-menu/index.ts +14 -0
- package/template/src/components/ui/date-picker/DatePicker.vue +233 -0
- package/template/src/components/ui/date-picker/index.ts +1 -0
- package/template/src/components/ui/dialog/Dialog.vue +19 -0
- package/template/src/components/ui/dialog/DialogClose.vue +15 -0
- package/template/src/components/ui/dialog/DialogContent.vue +53 -0
- package/template/src/components/ui/dialog/DialogDescription.vue +23 -0
- package/template/src/components/ui/dialog/DialogFooter.vue +27 -0
- package/template/src/components/ui/dialog/DialogHeader.vue +17 -0
- package/template/src/components/ui/dialog/DialogOverlay.vue +21 -0
- package/template/src/components/ui/dialog/DialogScrollContent.vue +59 -0
- package/template/src/components/ui/dialog/DialogTitle.vue +23 -0
- package/template/src/components/ui/dialog/DialogTrigger.vue +15 -0
- package/template/src/components/ui/dialog/index.ts +10 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuContent.vue +41 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
- package/template/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/template/src/components/ui/dropdown-menu/index.ts +16 -0
- package/template/src/components/ui/form/FormControl.vue +17 -0
- package/template/src/components/ui/form/FormDescription.vue +21 -0
- package/template/src/components/ui/form/FormItem.vue +23 -0
- package/template/src/components/ui/form/FormLabel.vue +25 -0
- package/template/src/components/ui/form/FormMessage.vue +23 -0
- package/template/src/components/ui/form/index.ts +7 -0
- package/template/src/components/ui/form/injectionKeys.ts +4 -0
- package/template/src/components/ui/form/useFormField.ts +30 -0
- package/template/src/components/ui/hover-card/HoverCard.vue +19 -0
- package/template/src/components/ui/hover-card/HoverCardContent.vue +43 -0
- package/template/src/components/ui/hover-card/HoverCardTrigger.vue +15 -0
- package/template/src/components/ui/hover-card/index.ts +3 -0
- package/template/src/components/ui/index.ts +47 -0
- package/template/src/components/ui/input/Input.vue +33 -0
- package/template/src/components/ui/input/InputAddon.vue +29 -0
- package/template/src/components/ui/input/InputGroup.vue +17 -0
- package/template/src/components/ui/input/InputIcon.vue +29 -0
- package/template/src/components/ui/input/index.ts +4 -0
- package/template/src/components/ui/kbd/Kbd.vue +30 -0
- package/template/src/components/ui/kbd/index.ts +1 -0
- package/template/src/components/ui/label/Label.vue +26 -0
- package/template/src/components/ui/label/index.ts +1 -0
- package/template/src/components/ui/loading-bar/LoadingBar.vue +41 -0
- package/template/src/components/ui/loading-bar/index.ts +2 -0
- package/template/src/components/ui/pagination/Pagination.vue +26 -0
- package/template/src/components/ui/pagination/PaginationContent.vue +22 -0
- package/template/src/components/ui/pagination/PaginationEllipsis.vue +25 -0
- package/template/src/components/ui/pagination/PaginationFirst.vue +33 -0
- package/template/src/components/ui/pagination/PaginationItem.vue +34 -0
- package/template/src/components/ui/pagination/PaginationLast.vue +33 -0
- package/template/src/components/ui/pagination/PaginationNext.vue +33 -0
- package/template/src/components/ui/pagination/PaginationPrevious.vue +33 -0
- package/template/src/components/ui/pagination/index.ts +8 -0
- package/template/src/components/ui/pin-input/PinInput.vue +28 -0
- package/template/src/components/ui/pin-input/PinInputGroup.vue +18 -0
- package/template/src/components/ui/pin-input/PinInputInput.vue +24 -0
- package/template/src/components/ui/pin-input/PinInputSeparator.vue +21 -0
- package/template/src/components/ui/pin-input/index.ts +4 -0
- package/template/src/components/ui/popover/Popover.vue +19 -0
- package/template/src/components/ui/popover/PopoverAnchor.vue +15 -0
- package/template/src/components/ui/popover/PopoverContent.vue +45 -0
- package/template/src/components/ui/popover/PopoverTrigger.vue +15 -0
- package/template/src/components/ui/popover/index.ts +4 -0
- package/template/src/components/ui/progress/Progress.vue +35 -0
- package/template/src/components/ui/progress/index.ts +1 -0
- package/template/src/components/ui/radio-group/RadioGroup.vue +23 -0
- package/template/src/components/ui/radio-group/RadioGroupItem.vue +30 -0
- package/template/src/components/ui/radio-group/index.ts +2 -0
- package/template/src/components/ui/scroll-area/ScrollArea.vue +40 -0
- package/template/src/components/ui/scroll-area/ScrollBar.vue +44 -0
- package/template/src/components/ui/scroll-area/index.ts +2 -0
- package/template/src/components/ui/select/Select.vue +19 -0
- package/template/src/components/ui/select/SelectContent.vue +51 -0
- package/template/src/components/ui/select/SelectGroup.vue +15 -0
- package/template/src/components/ui/select/SelectItem.vue +44 -0
- package/template/src/components/ui/select/SelectItemText.vue +15 -0
- package/template/src/components/ui/select/SelectLabel.vue +17 -0
- package/template/src/components/ui/select/SelectScrollDownButton.vue +26 -0
- package/template/src/components/ui/select/SelectScrollUpButton.vue +26 -0
- package/template/src/components/ui/select/SelectSeparator.vue +19 -0
- package/template/src/components/ui/select/SelectTrigger.vue +33 -0
- package/template/src/components/ui/select/SelectValue.vue +15 -0
- package/template/src/components/ui/select/index.ts +11 -0
- package/template/src/components/ui/separator/Separator.vue +29 -0
- package/template/src/components/ui/separator/index.ts +1 -0
- package/template/src/components/ui/sheet/Sheet.vue +19 -0
- package/template/src/components/ui/sheet/SheetClose.vue +15 -0
- package/template/src/components/ui/sheet/SheetContent.vue +62 -0
- package/template/src/components/ui/sheet/SheetDescription.vue +21 -0
- package/template/src/components/ui/sheet/SheetFooter.vue +16 -0
- package/template/src/components/ui/sheet/SheetHeader.vue +15 -0
- package/template/src/components/ui/sheet/SheetOverlay.vue +21 -0
- package/template/src/components/ui/sheet/SheetTitle.vue +21 -0
- package/template/src/components/ui/sheet/SheetTrigger.vue +15 -0
- package/template/src/components/ui/sheet/index.ts +8 -0
- package/template/src/components/ui/sidebar/Sidebar.vue +113 -0
- package/template/src/components/ui/sidebar/SidebarContent.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarControl.vue +53 -0
- package/template/src/components/ui/sidebar/SidebarFooter.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarGroup.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarGroupAction.vue +27 -0
- package/template/src/components/ui/sidebar/SidebarGroupContent.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarGroupLabel.vue +25 -0
- package/template/src/components/ui/sidebar/SidebarHeader.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarInput.vue +22 -0
- package/template/src/components/ui/sidebar/SidebarInset.vue +21 -0
- package/template/src/components/ui/sidebar/SidebarMenu.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarMenuAction.vue +35 -0
- package/template/src/components/ui/sidebar/SidebarMenuBadge.vue +26 -0
- package/template/src/components/ui/sidebar/SidebarMenuButton.vue +48 -0
- package/template/src/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
- package/template/src/components/ui/sidebar/SidebarMenuItem.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
- package/template/src/components/ui/sidebar/SidebarMenuSub.vue +22 -0
- package/template/src/components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
- package/template/src/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
- package/template/src/components/ui/sidebar/SidebarProvider.vue +136 -0
- package/template/src/components/ui/sidebar/SidebarRail.vue +33 -0
- package/template/src/components/ui/sidebar/SidebarSeparator.vue +19 -0
- package/template/src/components/ui/sidebar/SidebarTrigger.vue +27 -0
- package/template/src/components/ui/sidebar/index.ts +61 -0
- package/template/src/components/ui/sidebar/utils.ts +27 -0
- package/template/src/components/ui/skeleton/Skeleton.vue +17 -0
- package/template/src/components/ui/skeleton/index.ts +1 -0
- package/template/src/components/ui/slider/Slider.vue +42 -0
- package/template/src/components/ui/slider/index.ts +1 -0
- package/template/src/components/ui/sonner/Sonner.vue +42 -0
- package/template/src/components/ui/sonner/index.ts +2 -0
- package/template/src/components/ui/stepper/Stepper.vue +102 -0
- package/template/src/components/ui/stepper/index.ts +1 -0
- package/template/src/components/ui/switch/Switch.vue +38 -0
- package/template/src/components/ui/switch/index.ts +1 -0
- package/template/src/components/ui/table/Table.vue +16 -0
- package/template/src/components/ui/table/TableBody.vue +17 -0
- package/template/src/components/ui/table/TableCaption.vue +17 -0
- package/template/src/components/ui/table/TableCell.vue +22 -0
- package/template/src/components/ui/table/TableEmpty.vue +34 -0
- package/template/src/components/ui/table/TableFooter.vue +17 -0
- package/template/src/components/ui/table/TableHead.vue +17 -0
- package/template/src/components/ui/table/TableHeader.vue +17 -0
- package/template/src/components/ui/table/TableRow.vue +17 -0
- package/template/src/components/ui/table/index.ts +9 -0
- package/template/src/components/ui/table/utils.ts +10 -0
- package/template/src/components/ui/tabs/Tabs.vue +24 -0
- package/template/src/components/ui/tabs/TabsContent.vue +21 -0
- package/template/src/components/ui/tabs/TabsList.vue +24 -0
- package/template/src/components/ui/tabs/TabsTrigger.vue +26 -0
- package/template/src/components/ui/tabs/index.ts +4 -0
- package/template/src/components/ui/textarea/Textarea.vue +28 -0
- package/template/src/components/ui/textarea/index.ts +1 -0
- package/template/src/components/ui/timeline/Timeline.vue +73 -0
- package/template/src/components/ui/timeline/index.ts +1 -0
- package/template/src/components/ui/toggle/Toggle.vue +35 -0
- package/template/src/components/ui/toggle/index.ts +28 -0
- package/template/src/components/ui/toggle-group/ToggleGroup.vue +41 -0
- package/template/src/components/ui/toggle-group/ToggleGroupItem.vue +43 -0
- package/template/src/components/ui/toggle-group/index.ts +12 -0
- package/template/src/components/ui/tooltip/Tooltip.vue +19 -0
- package/template/src/components/ui/tooltip/TooltipContent.vue +34 -0
- package/template/src/components/ui/tooltip/TooltipProvider.vue +14 -0
- package/template/src/components/ui/tooltip/TooltipTrigger.vue +15 -0
- package/template/src/components/ui/tooltip/index.ts +4 -0
- package/template/src/components.d.ts +228 -0
- package/template/src/composables/useFormatter.ts +105 -0
- package/template/src/composables/useThemeConfig.ts +252 -0
- package/template/src/layouts/AdminLayout.vue +55 -0
- package/template/src/layouts/AuthLayout.vue +42 -0
- package/template/src/lib/axios.spec.ts +46 -0
- package/template/src/lib/axios.ts +243 -0
- package/template/src/lib/formatters.spec.ts +137 -0
- package/template/src/lib/formatters.ts +332 -0
- package/template/src/lib/loading-bar.ts +103 -0
- package/template/src/lib/utils.spec.ts +26 -0
- package/template/src/lib/utils.ts +7 -0
- package/template/src/lib/validation.spec.ts +78 -0
- package/template/src/main.ts +13 -0
- package/template/src/router/index.ts +147 -0
- package/template/src/services/auth.service.spec.ts +93 -0
- package/template/src/services/auth.service.ts +243 -0
- package/template/src/services/index.ts +3 -0
- package/template/src/services/types.ts +28 -0
- package/template/src/services/user.service.spec.ts +91 -0
- package/template/src/services/user.service.ts +283 -0
- package/template/src/stores/auth.spec.ts +52 -0
- package/template/src/stores/auth.ts +130 -0
- package/template/src/style.css +587 -0
- package/template/src/views/_starter/BlankView.vue +47 -0
- package/template/src/views/auth/ConfirmEmailView.vue +209 -0
- package/template/src/views/auth/ForgotPasswordView.vue +151 -0
- package/template/src/views/auth/LockScreenView.vue +175 -0
- package/template/src/views/auth/LoginView.vue +54 -0
- package/template/src/views/auth/RegisterView.vue +265 -0
- package/template/src/views/auth/ResetPasswordView.vue +260 -0
- package/template/src/views/auth/VerifyOtpView.vue +261 -0
- package/template/src/views/dashboard/IndexView.vue +159 -0
- package/template/src/views/errors/ComingSoonView.vue +241 -0
- package/template/src/views/errors/MaintenanceView.vue +252 -0
- package/template/src/views/errors/NotFoundView.vue +81 -0
- package/template/src/views/errors/ServerErrorView.vue +101 -0
- package/template/src/views/errors/UnauthorizedView.vue +64 -0
- package/template/tsconfig.app.json +27 -0
- package/template/tsconfig.json +19 -0
- package/template/tsconfig.node.json +23 -0
- package/template/vite.config.ts +46 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes["class"]
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<ul
|
|
12
|
+
data-slot="sidebar-menu-sub"
|
|
13
|
+
data-sidebar="menu-badge"
|
|
14
|
+
:class="cn(
|
|
15
|
+
'border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5',
|
|
16
|
+
'group-data-[collapsible=icon]:hidden',
|
|
17
|
+
props.class,
|
|
18
|
+
)"
|
|
19
|
+
>
|
|
20
|
+
<slot />
|
|
21
|
+
</ul>
|
|
22
|
+
</template>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { PrimitiveProps } from "reka-ui"
|
|
3
|
+
import type { HTMLAttributes } from "vue"
|
|
4
|
+
import { Primitive } from "reka-ui"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<PrimitiveProps & {
|
|
8
|
+
size?: "sm" | "md"
|
|
9
|
+
isActive?: boolean
|
|
10
|
+
class?: HTMLAttributes["class"]
|
|
11
|
+
}>(), {
|
|
12
|
+
as: "a",
|
|
13
|
+
size: "md",
|
|
14
|
+
})
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<Primitive
|
|
19
|
+
data-slot="sidebar-menu-sub-button"
|
|
20
|
+
data-sidebar="menu-sub-button"
|
|
21
|
+
:as="as"
|
|
22
|
+
:as-child="asChild"
|
|
23
|
+
:data-size="size"
|
|
24
|
+
:data-active="isActive"
|
|
25
|
+
:class="cn(
|
|
26
|
+
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
|
|
27
|
+
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
|
|
28
|
+
size === 'sm' && 'text-xs',
|
|
29
|
+
size === 'md' && 'text-sm',
|
|
30
|
+
'group-data-[collapsible=icon]:hidden',
|
|
31
|
+
props.class,
|
|
32
|
+
)"
|
|
33
|
+
>
|
|
34
|
+
<slot />
|
|
35
|
+
</Primitive>
|
|
36
|
+
</template>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes["class"]
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<li
|
|
12
|
+
data-slot="sidebar-menu-sub-item"
|
|
13
|
+
data-sidebar="menu-sub-item"
|
|
14
|
+
:class="cn('group/menu-sub-item relative', props.class)"
|
|
15
|
+
>
|
|
16
|
+
<slot />
|
|
17
|
+
</li>
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes, Ref } from "vue"
|
|
3
|
+
import { defaultDocument, useEventListener, useMediaQuery, useVModel } from "@vueuse/core"
|
|
4
|
+
import { TooltipProvider } from "reka-ui"
|
|
5
|
+
import { computed, ref } from "vue"
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
import { provideSidebarContext, SIDEBAR_COOKIE_MAX_AGE, SIDEBAR_COOKIE_NAME, SIDEBAR_KEYBOARD_SHORTCUT, SIDEBAR_WIDTH, SIDEBAR_WIDTH_ICON, type SidebarControlMode } from "./utils"
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(defineProps<{
|
|
10
|
+
defaultOpen?: boolean
|
|
11
|
+
open?: boolean
|
|
12
|
+
class?: HTMLAttributes["class"]
|
|
13
|
+
}>(), {
|
|
14
|
+
defaultOpen: !defaultDocument?.cookie.includes(`${SIDEBAR_COOKIE_NAME}=false`),
|
|
15
|
+
open: undefined,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const emits = defineEmits<{
|
|
19
|
+
"update:open": [open: boolean]
|
|
20
|
+
}>()
|
|
21
|
+
|
|
22
|
+
const isMobile = useMediaQuery("(max-width: 768px)")
|
|
23
|
+
const openMobile = ref(false)
|
|
24
|
+
|
|
25
|
+
const open = useVModel(props, "open", emits, {
|
|
26
|
+
defaultValue: props.defaultOpen ?? false,
|
|
27
|
+
passive: (props.open === undefined) as false,
|
|
28
|
+
}) as Ref<boolean>
|
|
29
|
+
|
|
30
|
+
const getSavedMode = (): SidebarControlMode => {
|
|
31
|
+
if (typeof document === 'undefined') return 'expanded'
|
|
32
|
+
const match = document.cookie.match(/sidebar_mode=([^;]+)/)
|
|
33
|
+
if (match && ['expanded', 'collapsed', 'hover'].includes(match[1])) {
|
|
34
|
+
return match[1] as SidebarControlMode
|
|
35
|
+
}
|
|
36
|
+
return open.value ? 'expanded' : 'collapsed'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const sidebarMode = ref<SidebarControlMode>(getSavedMode())
|
|
40
|
+
const isHovered = ref(false)
|
|
41
|
+
const isMenuOpen = ref(false)
|
|
42
|
+
|
|
43
|
+
function setSidebarMode(mode: SidebarControlMode) {
|
|
44
|
+
sidebarMode.value = mode
|
|
45
|
+
document.cookie = `sidebar_mode=${mode}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
|
|
46
|
+
if (mode === "expanded") {
|
|
47
|
+
setOpen(true)
|
|
48
|
+
} else if (mode === "collapsed") {
|
|
49
|
+
setOpen(false)
|
|
50
|
+
} else if (mode === "hover") {
|
|
51
|
+
setOpen(false)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function setIsHovered(val: boolean) {
|
|
56
|
+
isHovered.value = val
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function setIsMenuOpen(val: boolean) {
|
|
60
|
+
isMenuOpen.value = val
|
|
61
|
+
if (val) {
|
|
62
|
+
isHovered.value = true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function setOpen(value: boolean) {
|
|
67
|
+
open.value = value // emits('update:open', value)
|
|
68
|
+
|
|
69
|
+
// This sets the cookie to keep the sidebar state.
|
|
70
|
+
document.cookie = `${SIDEBAR_COOKIE_NAME}=${open.value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function setOpenMobile(value: boolean) {
|
|
74
|
+
openMobile.value = value
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Helper to toggle the sidebar.
|
|
78
|
+
function toggleSidebar() {
|
|
79
|
+
if (isMobile.value) {
|
|
80
|
+
setOpenMobile(!openMobile.value)
|
|
81
|
+
} else {
|
|
82
|
+
const nextState = !open.value
|
|
83
|
+
setOpen(nextState)
|
|
84
|
+
sidebarMode.value = nextState ? 'expanded' : 'collapsed'
|
|
85
|
+
document.cookie = `sidebar_mode=${sidebarMode.value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
useEventListener("keydown", (event: KeyboardEvent) => {
|
|
90
|
+
if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
|
|
91
|
+
event.preventDefault()
|
|
92
|
+
toggleSidebar()
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// We add a state so that we can do data-state="expanded" or "collapsed".
|
|
97
|
+
// This makes it easier to style the sidebar with Tailwind classes.
|
|
98
|
+
const state = computed(() => {
|
|
99
|
+
if (sidebarMode.value === "hover") {
|
|
100
|
+
return (isHovered.value || isMenuOpen.value) ? "expanded" : "collapsed"
|
|
101
|
+
}
|
|
102
|
+
return open.value ? "expanded" : "collapsed"
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
provideSidebarContext({
|
|
106
|
+
state,
|
|
107
|
+
open,
|
|
108
|
+
setOpen,
|
|
109
|
+
isMobile,
|
|
110
|
+
openMobile,
|
|
111
|
+
setOpenMobile,
|
|
112
|
+
toggleSidebar,
|
|
113
|
+
sidebarMode,
|
|
114
|
+
setSidebarMode,
|
|
115
|
+
isHovered,
|
|
116
|
+
setIsHovered,
|
|
117
|
+
isMenuOpen,
|
|
118
|
+
setIsMenuOpen,
|
|
119
|
+
})
|
|
120
|
+
</script>
|
|
121
|
+
|
|
122
|
+
<template>
|
|
123
|
+
<TooltipProvider :delay-duration="0">
|
|
124
|
+
<div
|
|
125
|
+
data-slot="sidebar-wrapper"
|
|
126
|
+
:style="{
|
|
127
|
+
'--sidebar-width': SIDEBAR_WIDTH,
|
|
128
|
+
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
|
|
129
|
+
}"
|
|
130
|
+
:class="cn('group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full', props.class)"
|
|
131
|
+
v-bind="$attrs"
|
|
132
|
+
>
|
|
133
|
+
<slot />
|
|
134
|
+
</div>
|
|
135
|
+
</TooltipProvider>
|
|
136
|
+
</template>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
import { useSidebar } from "./utils"
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
class?: HTMLAttributes["class"]
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
const { toggleSidebar } = useSidebar()
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<button
|
|
15
|
+
data-sidebar="rail"
|
|
16
|
+
data-slot="sidebar-rail"
|
|
17
|
+
aria-label="Toggle Sidebar"
|
|
18
|
+
:tabindex="-1"
|
|
19
|
+
title="Toggle Sidebar"
|
|
20
|
+
:class="cn(
|
|
21
|
+
'hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-0.5 sm:flex',
|
|
22
|
+
'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',
|
|
23
|
+
'[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
|
|
24
|
+
'hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full',
|
|
25
|
+
'[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
|
|
26
|
+
'[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
|
|
27
|
+
props.class,
|
|
28
|
+
)"
|
|
29
|
+
@click="toggleSidebar"
|
|
30
|
+
>
|
|
31
|
+
<slot />
|
|
32
|
+
</button>
|
|
33
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
import { Separator } from '@/components/ui/separator'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
class?: HTMLAttributes["class"]
|
|
8
|
+
}>()
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<Separator
|
|
13
|
+
data-slot="sidebar-separator"
|
|
14
|
+
data-sidebar="separator"
|
|
15
|
+
:class="cn('bg-sidebar-border mx-2 w-auto', props.class)"
|
|
16
|
+
>
|
|
17
|
+
<slot />
|
|
18
|
+
</Separator>
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue"
|
|
3
|
+
import { PanelLeft } from "@lucide/vue"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
import { Button } from '@/components/ui/button'
|
|
6
|
+
import { useSidebar } from "./utils"
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
class?: HTMLAttributes["class"]
|
|
10
|
+
}>()
|
|
11
|
+
|
|
12
|
+
const { toggleSidebar } = useSidebar()
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<Button
|
|
17
|
+
data-sidebar="trigger"
|
|
18
|
+
data-slot="sidebar-trigger"
|
|
19
|
+
variant="ghost"
|
|
20
|
+
size="icon"
|
|
21
|
+
:class="cn('h-7 w-7', props.class)"
|
|
22
|
+
@click="toggleSidebar"
|
|
23
|
+
>
|
|
24
|
+
<PanelLeft />
|
|
25
|
+
<span class="sr-only">Toggle Sidebar</span>
|
|
26
|
+
</Button>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { VariantProps } from "class-variance-authority"
|
|
2
|
+
import type { HTMLAttributes } from "vue"
|
|
3
|
+
import { cva } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
export interface SidebarProps {
|
|
6
|
+
side?: "left" | "right"
|
|
7
|
+
variant?: "sidebar" | "floating" | "inset"
|
|
8
|
+
collapsible?: "offcanvas" | "icon" | "none"
|
|
9
|
+
class?: HTMLAttributes["class"]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { default as Sidebar } from "./Sidebar.vue"
|
|
13
|
+
export { default as SidebarContent } from "./SidebarContent.vue"
|
|
14
|
+
export { default as SidebarFooter } from "./SidebarFooter.vue"
|
|
15
|
+
export { default as SidebarGroup } from "./SidebarGroup.vue"
|
|
16
|
+
export { default as SidebarGroupAction } from "./SidebarGroupAction.vue"
|
|
17
|
+
export { default as SidebarGroupContent } from "./SidebarGroupContent.vue"
|
|
18
|
+
export { default as SidebarGroupLabel } from "./SidebarGroupLabel.vue"
|
|
19
|
+
export { default as SidebarHeader } from "./SidebarHeader.vue"
|
|
20
|
+
export { default as SidebarInput } from "./SidebarInput.vue"
|
|
21
|
+
export { default as SidebarInset } from "./SidebarInset.vue"
|
|
22
|
+
export { default as SidebarMenu } from "./SidebarMenu.vue"
|
|
23
|
+
export { default as SidebarMenuAction } from "./SidebarMenuAction.vue"
|
|
24
|
+
export { default as SidebarMenuBadge } from "./SidebarMenuBadge.vue"
|
|
25
|
+
export { default as SidebarMenuButton } from "./SidebarMenuButton.vue"
|
|
26
|
+
export { default as SidebarMenuItem } from "./SidebarMenuItem.vue"
|
|
27
|
+
export { default as SidebarMenuSkeleton } from "./SidebarMenuSkeleton.vue"
|
|
28
|
+
export { default as SidebarMenuSub } from "./SidebarMenuSub.vue"
|
|
29
|
+
export { default as SidebarMenuSubButton } from "./SidebarMenuSubButton.vue"
|
|
30
|
+
export { default as SidebarMenuSubItem } from "./SidebarMenuSubItem.vue"
|
|
31
|
+
export { default as SidebarProvider } from "./SidebarProvider.vue"
|
|
32
|
+
export { default as SidebarRail } from "./SidebarRail.vue"
|
|
33
|
+
export { default as SidebarSeparator } from "./SidebarSeparator.vue"
|
|
34
|
+
export { default as SidebarTrigger } from "./SidebarTrigger.vue"
|
|
35
|
+
export { default as SidebarControl } from "./SidebarControl.vue"
|
|
36
|
+
|
|
37
|
+
export { useSidebar } from "./utils"
|
|
38
|
+
|
|
39
|
+
export const sidebarMenuButtonVariants = cva(
|
|
40
|
+
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm text-sidebar-foreground outline-hidden ring-sidebar-ring transition-colors duration-150 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-foreground hover:[&>svg]:text-sidebar-accent-foreground data-[active=true]:[&>svg]:text-sidebar-accent-foreground",
|
|
41
|
+
{
|
|
42
|
+
variants: {
|
|
43
|
+
variant: {
|
|
44
|
+
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
45
|
+
outline:
|
|
46
|
+
"bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]",
|
|
47
|
+
},
|
|
48
|
+
size: {
|
|
49
|
+
default: "h-8 text-sm",
|
|
50
|
+
sm: "h-7 text-xs",
|
|
51
|
+
lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
defaultVariants: {
|
|
55
|
+
variant: "default",
|
|
56
|
+
size: "default",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
export type SidebarMenuButtonVariants = VariantProps<typeof sidebarMenuButtonVariants>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from "vue"
|
|
2
|
+
import { createContext } from "reka-ui"
|
|
3
|
+
|
|
4
|
+
export const SIDEBAR_COOKIE_NAME = "sidebar_state"
|
|
5
|
+
export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
|
|
6
|
+
export const SIDEBAR_WIDTH = "14rem"
|
|
7
|
+
export const SIDEBAR_WIDTH_MOBILE = "18rem"
|
|
8
|
+
export const SIDEBAR_WIDTH_ICON = "3.5rem"
|
|
9
|
+
export const SIDEBAR_KEYBOARD_SHORTCUT = "b"
|
|
10
|
+
|
|
11
|
+
export type SidebarControlMode = "expanded" | "collapsed" | "hover"
|
|
12
|
+
|
|
13
|
+
export const [useSidebar, provideSidebarContext] = createContext<{
|
|
14
|
+
state: ComputedRef<"expanded" | "collapsed">
|
|
15
|
+
open: Ref<boolean>
|
|
16
|
+
setOpen: (value: boolean) => void
|
|
17
|
+
isMobile: Ref<boolean>
|
|
18
|
+
openMobile: Ref<boolean>
|
|
19
|
+
setOpenMobile: (value: boolean) => void
|
|
20
|
+
toggleSidebar: () => void
|
|
21
|
+
sidebarMode: Ref<SidebarControlMode>
|
|
22
|
+
setSidebarMode: (mode: SidebarControlMode) => void
|
|
23
|
+
isHovered: Ref<boolean>
|
|
24
|
+
setIsHovered: (value: boolean) => void
|
|
25
|
+
isMenuOpen: Ref<boolean>
|
|
26
|
+
setIsMenuOpen: (value: boolean) => void
|
|
27
|
+
}>("Sidebar")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
interface SkeletonProps {
|
|
6
|
+
class?: HTMLAttributes["class"]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const props = defineProps<SkeletonProps>()
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<div
|
|
14
|
+
data-slot="skeleton"
|
|
15
|
+
:class="cn('animate-pulse rounded-md bg-primary/10', props.class)"
|
|
16
|
+
/>
|
|
17
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Skeleton } from "./Skeleton.vue"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { SliderRootEmits, SliderRootProps } from "reka-ui"
|
|
3
|
+
import type { HTMLAttributes } from "vue"
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core"
|
|
5
|
+
import { SliderRange, SliderRoot, SliderThumb, SliderTrack, useForwardPropsEmits } from "reka-ui"
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
|
|
8
|
+
const props = defineProps<SliderRootProps & { class?: HTMLAttributes["class"] }>()
|
|
9
|
+
const emits = defineEmits<SliderRootEmits>()
|
|
10
|
+
|
|
11
|
+
const delegatedProps = reactiveOmit(props, "class")
|
|
12
|
+
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<SliderRoot
|
|
17
|
+
data-slot="slider"
|
|
18
|
+
:class="
|
|
19
|
+
cn(
|
|
20
|
+
'relative flex w-full touch-none select-none items-center data-[orientation=vertical]:h-full data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col',
|
|
21
|
+
props.class
|
|
22
|
+
)
|
|
23
|
+
"
|
|
24
|
+
v-bind="forwarded"
|
|
25
|
+
>
|
|
26
|
+
<SliderTrack
|
|
27
|
+
data-slot="slider-track"
|
|
28
|
+
class="bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
|
|
29
|
+
>
|
|
30
|
+
<SliderRange
|
|
31
|
+
data-slot="slider-range"
|
|
32
|
+
class="bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
|
|
33
|
+
/>
|
|
34
|
+
</SliderTrack>
|
|
35
|
+
<SliderThumb
|
|
36
|
+
v-for="(_, key) in (modelValue ?? defaultValue ?? [0])"
|
|
37
|
+
:key="key"
|
|
38
|
+
data-slot="slider-thumb"
|
|
39
|
+
class="border-primary bg-background ring-offset-background focus-visible:ring-ring block size-4 rounded-full border-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 cursor-grab active:cursor-grabbing hover:scale-110"
|
|
40
|
+
/>
|
|
41
|
+
</SliderRoot>
|
|
42
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Slider } from './Slider.vue'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { ToasterProps } from "vue-sonner"
|
|
3
|
+
import { CircleCheckIcon, InfoIcon, Loader2Icon, OctagonXIcon, TriangleAlertIcon, XIcon } from "@lucide/vue"
|
|
4
|
+
import { Toaster as Sonner } from "vue-sonner"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const props = defineProps<ToasterProps>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<Sonner
|
|
12
|
+
:class="cn('toaster group', props.class)"
|
|
13
|
+
:style="{
|
|
14
|
+
'--normal-bg': 'var(--popover)',
|
|
15
|
+
'--normal-text': 'var(--popover-foreground)',
|
|
16
|
+
'--normal-border': 'var(--border)',
|
|
17
|
+
'--border-radius': 'var(--radius)',
|
|
18
|
+
}"
|
|
19
|
+
v-bind="props"
|
|
20
|
+
>
|
|
21
|
+
<template #success-icon>
|
|
22
|
+
<CircleCheckIcon class="size-4" />
|
|
23
|
+
</template>
|
|
24
|
+
<template #info-icon>
|
|
25
|
+
<InfoIcon class="size-4" />
|
|
26
|
+
</template>
|
|
27
|
+
<template #warning-icon>
|
|
28
|
+
<TriangleAlertIcon class="size-4" />
|
|
29
|
+
</template>
|
|
30
|
+
<template #error-icon>
|
|
31
|
+
<OctagonXIcon class="size-4" />
|
|
32
|
+
</template>
|
|
33
|
+
<template #loading-icon>
|
|
34
|
+
<div>
|
|
35
|
+
<Loader2Icon class="size-4 animate-spin" />
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
<template #close-icon>
|
|
39
|
+
<XIcon class="size-4" />
|
|
40
|
+
</template>
|
|
41
|
+
</Sonner>
|
|
42
|
+
</template>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
interface Step {
|
|
6
|
+
id: number | string
|
|
7
|
+
title: string
|
|
8
|
+
description?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
steps: Step[]
|
|
13
|
+
modelValue: number
|
|
14
|
+
class?: HTMLAttributes["class"]
|
|
15
|
+
orientation?: "horizontal" | "vertical"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
19
|
+
orientation: "horizontal",
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const emit = defineEmits<{
|
|
23
|
+
(e: "update:modelValue", value: number): void
|
|
24
|
+
}>()
|
|
25
|
+
|
|
26
|
+
const setStep = (stepIndex: number) => {
|
|
27
|
+
emit("update:modelValue", stepIndex)
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<div
|
|
33
|
+
data-slot="stepper"
|
|
34
|
+
:class="
|
|
35
|
+
cn(
|
|
36
|
+
'w-full',
|
|
37
|
+
orientation === 'horizontal' ? 'flex items-center justify-between' : 'flex flex-col gap-4',
|
|
38
|
+
props.class
|
|
39
|
+
)
|
|
40
|
+
"
|
|
41
|
+
>
|
|
42
|
+
<div
|
|
43
|
+
v-for="(step, index) in steps"
|
|
44
|
+
:key="step.id"
|
|
45
|
+
class="flex items-center flex-1 last:flex-none relative"
|
|
46
|
+
:class="orientation === 'vertical' ? 'flex-col items-start' : ''"
|
|
47
|
+
>
|
|
48
|
+
<!-- Step Trigger -->
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
51
|
+
@click="setStep(index + 1)"
|
|
52
|
+
class="flex items-center gap-3 text-left group cursor-pointer focus:outline-none"
|
|
53
|
+
>
|
|
54
|
+
<!-- Step Indicator Circle -->
|
|
55
|
+
<span
|
|
56
|
+
class="relative flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-xs font-semibold font-mono transition-all duration-300 shadow-2xs"
|
|
57
|
+
:class="[
|
|
58
|
+
index + 1 < modelValue
|
|
59
|
+
? 'bg-primary text-primary-foreground'
|
|
60
|
+
: index + 1 === modelValue
|
|
61
|
+
? 'bg-primary/10 text-primary border-2 border-primary ring-4 ring-primary/10'
|
|
62
|
+
: 'border border-border bg-muted/40 text-muted-foreground group-hover:border-muted-foreground/50'
|
|
63
|
+
]"
|
|
64
|
+
>
|
|
65
|
+
<!-- Checkmark for completed -->
|
|
66
|
+
<svg
|
|
67
|
+
v-if="index + 1 < modelValue"
|
|
68
|
+
class="h-4 w-4 stroke-3"
|
|
69
|
+
fill="none"
|
|
70
|
+
viewBox="0 0 24 24"
|
|
71
|
+
stroke="currentColor"
|
|
72
|
+
>
|
|
73
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
|
|
74
|
+
</svg>
|
|
75
|
+
<span v-else>{{ index + 1 }}</span>
|
|
76
|
+
</span>
|
|
77
|
+
|
|
78
|
+
<!-- Step Labels -->
|
|
79
|
+
<div class="space-y-0.5">
|
|
80
|
+
<p
|
|
81
|
+
class="text-xs font-medium transition-colors"
|
|
82
|
+
:class="[
|
|
83
|
+
index + 1 <= modelValue ? 'text-foreground font-semibold' : 'text-muted-foreground'
|
|
84
|
+
]"
|
|
85
|
+
>
|
|
86
|
+
{{ step.title }}
|
|
87
|
+
</p>
|
|
88
|
+
<p v-if="step.description" class="text-[11px] text-muted-foreground hidden sm:block">
|
|
89
|
+
{{ step.description }}
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
</button>
|
|
93
|
+
|
|
94
|
+
<!-- Connecting Separator Line (if not last) -->
|
|
95
|
+
<div
|
|
96
|
+
v-if="index < steps.length - 1 && orientation === 'horizontal'"
|
|
97
|
+
class="flex-1 mx-3 h-0.5 transition-colors duration-300"
|
|
98
|
+
:class="index + 1 < modelValue ? 'bg-primary' : 'bg-border'"
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Stepper } from './Stepper.vue'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { SwitchRootEmits, SwitchRootProps } from "reka-ui"
|
|
3
|
+
import type { HTMLAttributes } from "vue"
|
|
4
|
+
import { reactiveOmit } from "@vueuse/core"
|
|
5
|
+
import {
|
|
6
|
+
SwitchRoot,
|
|
7
|
+
SwitchThumb,
|
|
8
|
+
useForwardPropsEmits,
|
|
9
|
+
} from "reka-ui"
|
|
10
|
+
import { cn } from "@/lib/utils"
|
|
11
|
+
|
|
12
|
+
const props = defineProps<SwitchRootProps & { class?: HTMLAttributes["class"] }>()
|
|
13
|
+
|
|
14
|
+
const emits = defineEmits<SwitchRootEmits>()
|
|
15
|
+
|
|
16
|
+
const delegatedProps = reactiveOmit(props, "class")
|
|
17
|
+
|
|
18
|
+
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<SwitchRoot
|
|
23
|
+
v-slot="slotProps"
|
|
24
|
+
data-slot="switch"
|
|
25
|
+
v-bind="forwarded"
|
|
26
|
+
:class="cn(
|
|
27
|
+
'peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50',
|
|
28
|
+
props.class,
|
|
29
|
+
)"
|
|
30
|
+
>
|
|
31
|
+
<SwitchThumb
|
|
32
|
+
data-slot="switch-thumb"
|
|
33
|
+
:class="cn('bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0')"
|
|
34
|
+
>
|
|
35
|
+
<slot name="thumb" v-bind="slotProps" />
|
|
36
|
+
</SwitchThumb>
|
|
37
|
+
</SwitchRoot>
|
|
38
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Switch } from "./Switch.vue"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "vue"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes["class"]
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div data-slot="table-container" class="relative w-full overflow-auto">
|
|
12
|
+
<table data-slot="table" :class="cn('w-full caption-bottom text-sm', props.class)">
|
|
13
|
+
<slot />
|
|
14
|
+
</table>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|