feat: enhance header with notification buttons and user dropdown
- Updated header component to include notification buttons with icons. - Introduced a new NavUser component for user-related actions in the sidebar. - Added NotificationDot component for visual notification indicators. - Created UserCard component to display user information. - Implemented UserDropdown for user settings and logout functionality. - Added Avatar component for user images with fallback support. - Refactored Sheet and Tooltip components for consistency and improved styling. - Introduced QueryProvider for managing React Query context. - Updated SidebarProvider to use custom sidebar implementation. - Enhanced mobile detection hook for better responsiveness. - Updated dependencies in yarn.lock for new features and fixes. feat: remove dot
This commit is contained in:
parent
9225d8435a
commit
1d9ab84047
19 changed files with 501 additions and 104 deletions
40
src/components/buttons/notification-button.tsx
Normal file
40
src/components/buttons/notification-button.tsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
// DropdownMenuGroup,
|
||||
// DropdownMenuItem,
|
||||
// DropdownMenuLabel,
|
||||
// DropdownMenuPortal,
|
||||
// DropdownMenuSeparator,
|
||||
// DropdownMenuShortcut,
|
||||
// DropdownMenuSub,
|
||||
// DropdownMenuSubContent,
|
||||
// DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { NDot, NotificationDot } from '@/components/misc/notification-dot';
|
||||
|
||||
export function NotificationButton({
|
||||
dotVariant,
|
||||
children,
|
||||
...props
|
||||
}: {
|
||||
dotVariant?: NDot;
|
||||
children: React.ReactNode;
|
||||
} & React.ComponentProps<typeof Button>) {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type='button' variant='outline_primary' {...props}>
|
||||
{children}
|
||||
<NotificationDot
|
||||
dotVariant={dotVariant}
|
||||
className='absolute ml-[30px] mt-[30px]'
|
||||
/>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align='end'></DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
|
@ -6,27 +6,27 @@ import {
|
|||
SidebarContent,
|
||||
SidebarFooter,
|
||||
SidebarGroup,
|
||||
SidebarGroupAction,
|
||||
// SidebarGroupAction,
|
||||
SidebarGroupContent,
|
||||
SidebarGroupLabel,
|
||||
SidebarHeader,
|
||||
SidebarInput,
|
||||
SidebarInset,
|
||||
// SidebarInput,
|
||||
// SidebarInset,
|
||||
SidebarMenu,
|
||||
SidebarMenuAction,
|
||||
SidebarMenuBadge,
|
||||
// SidebarMenuAction,
|
||||
// SidebarMenuBadge,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuSkeleton,
|
||||
SidebarMenuSub,
|
||||
SidebarMenuSubButton,
|
||||
SidebarMenuSubItem,
|
||||
SidebarProvider,
|
||||
SidebarRail,
|
||||
SidebarSeparator,
|
||||
SidebarTrigger,
|
||||
useSidebar,
|
||||
} from '@/components/ui/sidebar';
|
||||
// SidebarMenuSkeleton,
|
||||
// SidebarMenuSub,
|
||||
// SidebarMenuSubButton,
|
||||
// SidebarMenuSubItem,
|
||||
// SidebarProvider,
|
||||
// SidebarRail,
|
||||
// SidebarSeparator,
|
||||
// SidebarTrigger,
|
||||
// useSidebar,
|
||||
} from '@/components/custom-ui/sidebar';
|
||||
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import {
|
||||
|
@ -39,8 +39,6 @@ import Logo from '@/components/misc/logo';
|
|||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { ThemePicker } from '@/components/misc/theme-picker';
|
||||
|
||||
import {
|
||||
Star,
|
||||
CalendarDays,
|
||||
|
|
|
@ -466,7 +466,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<'li'>) {
|
|||
<li
|
||||
data-slot='sidebar-menu-item'
|
||||
data-sidebar='menu-item'
|
||||
className={cn('group/menu-item relative', className)}
|
||||
className={cn('group/menu-item relative list-none', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
|
@ -1,5 +1,22 @@
|
|||
import { SidebarTrigger } from '@/components/ui/sidebar';
|
||||
import { SidebarTrigger } from '@/components/custom-ui/sidebar';
|
||||
import { ThemePicker } from '@/components/misc/theme-picker';
|
||||
import { NotificationButton } from '@/components/buttons/notification-button';
|
||||
|
||||
import { BellRing, Inbox } from 'lucide-react';
|
||||
import UserDropdown from '@/components/misc/user-dropdown';
|
||||
|
||||
const items = [
|
||||
{
|
||||
title: 'Calendar',
|
||||
url: '#',
|
||||
icon: Inbox,
|
||||
},
|
||||
{
|
||||
title: 'Friends',
|
||||
url: '#',
|
||||
icon: BellRing,
|
||||
},
|
||||
];
|
||||
|
||||
export default function Header({
|
||||
children,
|
||||
|
@ -8,13 +25,24 @@ export default function Header({
|
|||
}>) {
|
||||
return (
|
||||
<div className='w-full grid grid-rows-[50px_1fr] h-screen'>
|
||||
<header className='border-b-1 grid-cols-[1fr_3fr_1fr] grid items-center px-2'>
|
||||
<header className='border-b-1 grid-cols-[1fr_3fr_1fr] grid items-center px-2 shadow-md'>
|
||||
<span className='flex justify-start'>
|
||||
<SidebarTrigger variant='outline_primary' size='icon' />
|
||||
</span>
|
||||
<span className='flex justify-center'>Search</span>
|
||||
<span className='flex justify-end'>
|
||||
<span className='flex gap-1 justify-end'>
|
||||
<ThemePicker />
|
||||
{items.map((item) => (
|
||||
<NotificationButton
|
||||
key={item.title}
|
||||
variant='outline_primary'
|
||||
dotVariant='hidden'
|
||||
size='icon'
|
||||
>
|
||||
<item.icon />
|
||||
</NotificationButton>
|
||||
))}
|
||||
<UserDropdown />
|
||||
</span>
|
||||
</header>
|
||||
<main>{children}</main>
|
||||
|
|
110
src/components/misc/nav-user.tsx
Normal file
110
src/components/misc/nav-user.tsx
Normal file
|
@ -0,0 +1,110 @@
|
|||
'use client';
|
||||
|
||||
import {
|
||||
BadgeCheck,
|
||||
Bell,
|
||||
ChevronsUpDown,
|
||||
CreditCard,
|
||||
LogOut,
|
||||
Sparkles,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import {
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from '@/components/custom-ui/sidebar';
|
||||
|
||||
export function NavUser({
|
||||
user,
|
||||
}: {
|
||||
user: {
|
||||
name: string;
|
||||
email: string;
|
||||
avatar: string;
|
||||
};
|
||||
}) {
|
||||
const { isMobile } = useSidebar();
|
||||
|
||||
return (
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<SidebarMenuButton
|
||||
size='lg'
|
||||
className='data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground'
|
||||
>
|
||||
<Avatar className='h-8 w-8 rounded-lg'>
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
<AvatarFallback className='rounded-lg'>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className='grid flex-1 text-left text-sm leading-tight'>
|
||||
<span className='truncate font-medium'>{user.name}</span>
|
||||
<span className='truncate text-xs'>{user.email}</span>
|
||||
</div>
|
||||
<ChevronsUpDown className='ml-auto size-4' />
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className='w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg'
|
||||
side={isMobile ? 'bottom' : 'right'}
|
||||
align='end'
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenuLabel className='p-0 font-normal'>
|
||||
<div className='flex items-center gap-2 px-1 py-1.5 text-left text-sm'>
|
||||
<Avatar className='h-8 w-8 rounded-lg'>
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
<AvatarFallback className='rounded-lg'>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className='grid flex-1 text-left text-sm leading-tight'>
|
||||
<span className='truncate font-medium'>{user.name}</span>
|
||||
<span className='truncate text-xs'>{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
<Sparkles />
|
||||
Upgrade to Pro
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
<BadgeCheck />
|
||||
Account
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<CreditCard />
|
||||
Billing
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Bell />
|
||||
Notifications
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<LogOut />
|
||||
Log out
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
);
|
||||
}
|
35
src/components/misc/notification-dot.tsx
Normal file
35
src/components/misc/notification-dot.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { cn } from '@/lib/utils';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import { CircleSmall } from 'lucide-react';
|
||||
|
||||
const dotVariants = cva('', {
|
||||
variants: {
|
||||
variant: {
|
||||
neutral: 'fill-neutral-900',
|
||||
active: 'fill-red-600 stroke-red-600',
|
||||
hidden: 'hidden',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
function NotificationDot({
|
||||
className,
|
||||
dotVariant,
|
||||
...props
|
||||
}: {
|
||||
className: string;
|
||||
dotVariant: VariantProps<typeof dotVariants>['variant'];
|
||||
}) {
|
||||
return (
|
||||
<CircleSmall
|
||||
className={cn(dotVariants({ variant: dotVariant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export type NDot = VariantProps<typeof dotVariants>['variant'];
|
||||
export { NotificationDot, dotVariants };
|
29
src/components/misc/user-card.tsx
Normal file
29
src/components/misc/user-card.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { useGetApiUserMe } from '@/generated/api/user/user';
|
||||
import { Avatar } from '@/components/ui/avatar';
|
||||
import Image from 'next/image';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
export default function UserCard() {
|
||||
const { data } = useGetApiUserMe();
|
||||
return (
|
||||
<div className='w-full'>
|
||||
<Avatar className='flex justify-center items-center'>
|
||||
{data?.data.user.image ? (
|
||||
<Image
|
||||
className='border-2 rounded-md'
|
||||
src={data?.data.user.image}
|
||||
alt='Avatar'
|
||||
width='50'
|
||||
height='50'
|
||||
/>
|
||||
) : (
|
||||
<User />
|
||||
)}
|
||||
</Avatar>
|
||||
<div className='flex justify-center'>{data?.data.user.name}</div>
|
||||
<div className='flex justify-center text-text-muted'>
|
||||
{data?.data.user.email}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
59
src/components/misc/user-dropdown.tsx
Normal file
59
src/components/misc/user-dropdown.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
'use client';
|
||||
|
||||
import { Avatar } from '@/components/ui/avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
// DropdownMenuLabel,
|
||||
// DropdownMenuPortal,
|
||||
DropdownMenuSeparator,
|
||||
// DropdownMenuShortcut,
|
||||
// DropdownMenuSub,
|
||||
// DropdownMenuSubContent,
|
||||
// DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { useGetApiUserMe } from '@/generated/api/user/user';
|
||||
import { ChevronDown, User } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import UserCard from '@/components/misc/user-card';
|
||||
|
||||
export default function UserDropdown() {
|
||||
const { data } = useGetApiUserMe();
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='outline_primary'>
|
||||
<Avatar className='flex justify-center items-center'>
|
||||
{data?.data.user.image ? (
|
||||
<Image
|
||||
src={data?.data.user.image}
|
||||
alt='Avatar'
|
||||
width='20'
|
||||
height='20'
|
||||
/>
|
||||
) : (
|
||||
<User />
|
||||
)}
|
||||
</Avatar>
|
||||
<ChevronDown />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align='end'>
|
||||
<DropdownMenuItem>
|
||||
<UserCard />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>Settings</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<Link href='/logout'>Logout</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
53
src/components/ui/avatar.tsx
Normal file
53
src/components/ui/avatar.tsx
Normal file
|
@ -0,0 +1,53 @@
|
|||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Avatar({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
||||
return (
|
||||
<AvatarPrimitive.Root
|
||||
data-slot='avatar'
|
||||
className={cn(
|
||||
'relative flex shrink-0 overflow-hidden rounded-md',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarImage({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
||||
return (
|
||||
<AvatarPrimitive.Image
|
||||
data-slot='avatar-image'
|
||||
className={cn('aspect-square size-full', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarFallback({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
||||
return (
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot='avatar-fallback'
|
||||
className={cn(
|
||||
'bg-muted flex size-full items-center justify-center rounded-full',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback };
|
|
@ -1,31 +1,31 @@
|
|||
"use client"
|
||||
'use client';
|
||||
|
||||
import * as React from "react"
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
import * as React from 'react';
|
||||
import * as SheetPrimitive from '@radix-ui/react-dialog';
|
||||
import { XIcon } from 'lucide-react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
||||
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
||||
return <SheetPrimitive.Root data-slot='sheet' {...props} />;
|
||||
}
|
||||
|
||||
function SheetTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
||||
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
||||
return <SheetPrimitive.Trigger data-slot='sheet-trigger' {...props} />;
|
||||
}
|
||||
|
||||
function SheetClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
||||
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
||||
return <SheetPrimitive.Close data-slot='sheet-close' {...props} />;
|
||||
}
|
||||
|
||||
function SheetPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
||||
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
||||
return <SheetPrimitive.Portal data-slot='sheet-portal' {...props} />;
|
||||
}
|
||||
|
||||
function SheetOverlay({
|
||||
|
@ -34,71 +34,71 @@ function SheetOverlay({
|
|||
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
||||
return (
|
||||
<SheetPrimitive.Overlay
|
||||
data-slot="sheet-overlay"
|
||||
data-slot='sheet-overlay'
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function SheetContent({
|
||||
className,
|
||||
children,
|
||||
side = "right",
|
||||
side = 'right',
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
||||
side?: "top" | "right" | "bottom" | "left"
|
||||
side?: 'top' | 'right' | 'bottom' | 'left';
|
||||
}) {
|
||||
return (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content
|
||||
data-slot="sheet-content"
|
||||
data-slot='sheet-content'
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||
side === "right" &&
|
||||
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
||||
side === "left" &&
|
||||
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
||||
side === "top" &&
|
||||
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
||||
side === "bottom" &&
|
||||
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
||||
className
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
|
||||
side === 'right' &&
|
||||
'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm',
|
||||
side === 'left' &&
|
||||
'data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm',
|
||||
side === 'top' &&
|
||||
'data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b',
|
||||
side === 'bottom' &&
|
||||
'data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
||||
<XIcon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
<SheetPrimitive.Close className='ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none'>
|
||||
<XIcon className='size-4' />
|
||||
<span className='sr-only'>Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="sheet-header"
|
||||
className={cn("flex flex-col gap-1.5 p-4", className)}
|
||||
data-slot='sheet-header'
|
||||
className={cn('flex flex-col gap-1.5 p-4', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="sheet-footer"
|
||||
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||
data-slot='sheet-footer'
|
||||
className={cn('mt-auto flex flex-col gap-2 p-4', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function SheetTitle({
|
||||
|
@ -107,11 +107,11 @@ function SheetTitle({
|
|||
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
||||
return (
|
||||
<SheetPrimitive.Title
|
||||
data-slot="sheet-title"
|
||||
className={cn("text-foreground font-semibold", className)}
|
||||
data-slot='sheet-title'
|
||||
className={cn('text-foreground font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function SheetDescription({
|
||||
|
@ -120,11 +120,11 @@ function SheetDescription({
|
|||
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
||||
return (
|
||||
<SheetPrimitive.Description
|
||||
data-slot="sheet-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
data-slot='sheet-description'
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
|
@ -136,4 +136,4 @@ export {
|
|||
SheetFooter,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="skeleton"
|
||||
className={cn("bg-accent animate-pulse rounded-md", className)}
|
||||
data-slot='skeleton'
|
||||
className={cn('bg-accent animate-pulse rounded-md', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Skeleton }
|
||||
export { Skeleton };
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
"use client"
|
||||
'use client';
|
||||
|
||||
import * as React from "react"
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||
import * as React from 'react';
|
||||
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function TooltipProvider({
|
||||
delayDuration = 0,
|
||||
|
@ -11,11 +11,11 @@ function TooltipProvider({
|
|||
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
||||
return (
|
||||
<TooltipPrimitive.Provider
|
||||
data-slot="tooltip-provider"
|
||||
data-slot='tooltip-provider'
|
||||
delayDuration={delayDuration}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function Tooltip({
|
||||
|
@ -23,15 +23,15 @@ function Tooltip({
|
|||
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
||||
<TooltipPrimitive.Root data-slot='tooltip' {...props} />
|
||||
</TooltipProvider>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TooltipTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
||||
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
||||
return <TooltipPrimitive.Trigger data-slot='tooltip-trigger' {...props} />;
|
||||
}
|
||||
|
||||
function TooltipContent({
|
||||
|
@ -43,19 +43,19 @@ function TooltipContent({
|
|||
return (
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
data-slot="tooltip-content"
|
||||
data-slot='tooltip-content'
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
||||
className
|
||||
'bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
||||
<TooltipPrimitive.Arrow className='bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]' />
|
||||
</TooltipPrimitive.Content>
|
||||
</TooltipPrimitive.Portal>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { SidebarProvider } from '../ui/sidebar';
|
||||
import { SidebarProvider } from '../custom-ui/sidebar';
|
||||
|
||||
export default function SidebarProviderWrapper({
|
||||
defaultOpen,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue