upgrade lucide-react and move it to devDep

- add more testing for dropdown
This commit is contained in:
Borris 2025-03-01 20:23:31 +04:00
parent fcf5488a59
commit f82f416855
10 changed files with 355 additions and 417 deletions

View File

@ -6,32 +6,30 @@ import { Providers } from '@/components/providers';
import { Metadata } from 'next'; import { Metadata } from 'next';
const fontSans = Geist({ const fontSans = Geist({
subsets: ['latin'], subsets: ['latin'],
variable: '--font-sans', variable: '--font-sans',
}); });
const fontMono = Geist_Mono({ const fontMono = Geist_Mono({
subsets: ['latin'], subsets: ['latin'],
variable: '--font-mono', variable: '--font-mono',
}); });
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Create Next App', title: 'Create Next App',
description: 'Generated by create next app', description: 'Generated by create next app',
}; };
export default function RootLayout({ export default function RootLayout({
children, children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<body <body className={`${fontSans.variable} ${fontMono.variable} font-sans antialiased `}>
className={`${fontSans.variable} ${fontMono.variable} font-sans antialiased `} <Providers>{children}</Providers>
> </body>
<Providers>{children}</Providers> </html>
</body> );
</html>
);
} }

View File

@ -1,121 +1,115 @@
import { ModeToggle } from '@/components/mode-toggle'; import { ModeToggle } from '@/components/mode-toggle';
import { Button, buttonVariants } from '@workspace/ui/components/button'; import { Button, buttonVariants } from '@workspace/ui/components/button';
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from '@workspace/ui/components/dropdown-menu';
import { cn } from '@workspace/ui/lib/utils'; import { cn } from '@workspace/ui/lib/utils';
import { ChevronDownIcon } from 'lucide-react';
import Image from 'next/image'; import Image from 'next/image';
export default function Home() { export default function Home() {
return ( return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]"> <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start"> <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<Image <Image className="dark:invert" src="/next.svg" alt="Next.js logo" width={180} height={38} priority />
className="dark:invert" <ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
src="/next.svg" <li className="mb-2">
alt="Next.js logo" Get started by editing{' '}
width={180} <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">app/page.tsx</code>.
height={38} </li>
priority <li>Save and see your changes instantly.</li>
/> </ol>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
Get started by editing{' '}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
app/page.tsx
</code>
.
</li>
<li>Save and see your changes instantly.</li>
</ol>
<p> <p>
All the buttons are from the <kbd>ui</kbd> package. The auto complete All the buttons are from the <kbd>ui</kbd> package. The auto complete works as well.
works as well. </p>
</p>
<pre className="border rounded-sm p-1.5 bg-foreground/10"> <pre className="border rounded-sm p-1.5 bg-foreground/10">
<code>{`import { Button, buttonVariants } from '@workspace/ui/components/button'; <code>{`import { Button, buttonVariants } from '@workspace/ui/components/button';
import { cn } from '@workspace/ui/lib/utils';`}</code> import { cn } from '@workspace/ui/lib/utils';`}</code>
</pre> </pre>
<ModeToggle /> <ModeToggle />
<Button size={'sm'}>Click me</Button> <Button size={'sm'}>Click me</Button>
<div className="flex gap-4 items-center flex-col sm:flex-row"> <DropdownMenu>
<a <DropdownMenuTrigger asChild>
className={cn(buttonVariants({ size: 'lg' }), 'rounded-full')} <Button size={'sm'}>
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" Dropdown <ChevronDownIcon />
target="_blank" </Button>
rel="noopener noreferrer" </DropdownMenuTrigger>
> <DropdownMenuContent>
<Image <DropdownMenuItem>Item 1</DropdownMenuItem>
className="dark:invert" <DropdownMenuItem>Item 2</DropdownMenuItem>
src="/vercel.svg" <DropdownMenuCheckboxItem checked>Item 3</DropdownMenuCheckboxItem>
alt="Vercel logomark" <DropdownMenuCheckboxItem>Item 3</DropdownMenuCheckboxItem>
width={20} <DropdownMenuSub>
height={20} <DropdownMenuSubTrigger>Item 3</DropdownMenuSubTrigger>
/> <DropdownMenuSubContent>
Deploy now <DropdownMenuItem>Item 3.1</DropdownMenuItem>
</a> <DropdownMenuItem>Item 3.2</DropdownMenuItem>
<a </DropdownMenuSubContent>
className={cn( </DropdownMenuSub>
buttonVariants({ size: 'lg', variant: 'outline' }), </DropdownMenuContent>
'rounded-full', </DropdownMenu>
)}
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" <div className="flex gap-4 items-center flex-col sm:flex-row">
target="_blank" <a
rel="noopener noreferrer" className={cn(buttonVariants({ size: 'lg' }), 'rounded-full')}
> href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
Read our docs target="_blank"
</a> rel="noopener noreferrer"
</div> >
</main> <Image className="dark:invert" src="/vercel.svg" alt="Vercel logomark" width={20} height={20} />
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center"> Deploy now
<a </a>
className="flex items-center gap-2 hover:underline hover:underline-offset-4" <a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" className={cn(buttonVariants({ size: 'lg', variant: 'outline' }), 'rounded-full')}
target="_blank" href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
rel="noopener noreferrer" target="_blank"
> rel="noopener noreferrer"
<Image >
aria-hidden Read our docs
src="/file.svg" </a>
alt="File icon" </div>
width={16} </main>
height={16} <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
/> <a
Learn className="flex items-center gap-2 hover:underline hover:underline-offset-4"
</a> href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
<a target="_blank"
className="flex items-center gap-2 hover:underline hover:underline-offset-4" rel="noopener noreferrer"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" >
target="_blank" <Image aria-hidden src="/file.svg" alt="File icon" width={16} height={16} />
rel="noopener noreferrer" Learn
> </a>
<Image <a
aria-hidden className="flex items-center gap-2 hover:underline hover:underline-offset-4"
src="/window.svg" href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
alt="Window icon" target="_blank"
width={16} rel="noopener noreferrer"
height={16} >
/> <Image aria-hidden src="/window.svg" alt="Window icon" width={16} height={16} />
Examples Examples
</a> </a>
<a <a
className="flex items-center gap-2 hover:underline hover:underline-offset-4" className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<Image <Image aria-hidden src="/globe.svg" alt="Globe icon" width={16} height={16} />
aria-hidden Go to nextjs.org
src="/globe.svg" </a>
alt="Globe icon" </footer>
width={16} </div>
height={16} );
/>
Go to nextjs.org
</a>
</footer>
</div>
);
} }

View File

@ -6,35 +6,29 @@ import { useTheme } from 'next-themes';
import { Button } from '@workspace/ui/components/button'; import { Button } from '@workspace/ui/components/button';
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@workspace/ui/components/dropdown-menu'; } from '@workspace/ui/components/dropdown-menu';
export function ModeToggle() { export function ModeToggle() {
const { setTheme } = useTheme(); const { setTheme } = useTheme();
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="outline" size="icon"> <Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" /> <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" /> <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span> <span className="sr-only">Toggle theme</span>
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme('light')}> <DropdownMenuItem onClick={() => setTheme('light')}>Light</DropdownMenuItem>
Light <DropdownMenuItem onClick={() => setTheme('dark')}>Dark</DropdownMenuItem>
</DropdownMenuItem> <DropdownMenuItem onClick={() => setTheme('system')}>System</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('dark')}> </DropdownMenuContent>
Dark </DropdownMenu>
</DropdownMenuItem> );
<DropdownMenuItem onClick={() => setTheme('system')}>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
} }

View File

@ -4,15 +4,15 @@ import * as React from 'react';
import { ThemeProvider as NextThemesProvider } from 'next-themes'; import { ThemeProvider as NextThemesProvider } from 'next-themes';
export function Providers({ children }: { children: React.ReactNode }) { export function Providers({ children }: { children: React.ReactNode }) {
return ( return (
<NextThemesProvider <NextThemesProvider
attribute="class" attribute="class"
defaultTheme="system" defaultTheme="system"
enableSystem enableSystem
disableTransitionOnChange disableTransitionOnChange
enableColorScheme enableColorScheme
> >
{children} {children}
</NextThemesProvider> </NextThemesProvider>
); );
} }

View File

@ -11,7 +11,7 @@
}, },
"dependencies": { "dependencies": {
"@workspace/ui": "workspace:*", "@workspace/ui": "workspace:*",
"lucide-react": "0.475.0", "lucide-react": "0.477.0",
"next-themes": "^0.4.4", "next-themes": "^0.4.4",
"next": "15.2.0", "next": "15.2.0",
"react": "19.0.0", "react": "19.0.0",

View File

@ -17,7 +17,6 @@
"@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-slot": "^1.1.2",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "0.475.0",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"tailwind-merge": "^3.0.1", "tailwind-merge": "^3.0.1",
@ -33,6 +32,7 @@
"@workspace/eslint-config": "workspace:*", "@workspace/eslint-config": "workspace:*",
"@workspace/typescript-config": "workspace:*", "@workspace/typescript-config": "workspace:*",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"lucide-react": "0.477.0",
"tailwindcss": "^4", "tailwindcss": "^4",
"typescript": "^5" "typescript": "^5"
} }

View File

@ -5,54 +5,44 @@ import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@workspace/ui/lib/utils'; import { cn } from '@workspace/ui/lib/utils';
const buttonVariants = cva( const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 focus-visible:ring-4 focus-visible:outline-1 aria-invalid:focus-visible:ring-0", "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 focus-visible:ring-4 focus-visible:outline-1 aria-invalid:focus-visible:ring-0",
{ {
variants: { variants: {
variant: { variant: {
default: default: 'bg-primary text-primary-foreground shadow-sm hover:bg-primary/90',
'bg-primary text-primary-foreground shadow-sm hover:bg-primary/90', destructive: 'bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90',
destructive: outline: 'border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground',
'bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90', secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
outline: ghost: 'hover:bg-accent hover:text-accent-foreground',
'border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground', link: 'text-primary underline-offset-4 hover:underline',
secondary: },
'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', size: {
ghost: 'hover:bg-accent hover:text-accent-foreground', default: 'h-9 px-4 py-2 has-[>svg]:px-3',
link: 'text-primary underline-offset-4 hover:underline', sm: 'h-8 rounded-md px-3 has-[>svg]:px-2.5',
}, lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
size: { icon: 'size-9',
default: 'h-9 px-4 py-2 has-[>svg]:px-3', },
sm: 'h-8 rounded-md px-3 has-[>svg]:px-2.5', },
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', defaultVariants: {
icon: 'size-9', variant: 'default',
}, size: 'default',
}, },
defaultVariants: { },
variant: 'default',
size: 'default',
},
},
); );
function Button({ function Button({
className, className,
variant, variant,
size, size,
asChild = false, asChild = false,
...props ...props
}: React.ComponentProps<'button'> & }: React.ComponentProps<'button'> &
VariantProps<typeof buttonVariants> & { VariantProps<typeof buttonVariants> & {
asChild?: boolean; asChild?: boolean;
}) { }) {
const Comp = asChild ? Slot : 'button'; const Comp = asChild ? Slot : 'button';
return ( return <Comp data-slot="button" className={cn(buttonVariants({ variant, size, className }))} {...props} />;
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
} }
export { Button, buttonVariants }; export { Button, buttonVariants };

View File

@ -6,252 +6,214 @@ import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
import { cn } from '@workspace/ui/lib/utils'; import { cn } from '@workspace/ui/lib/utils';
function DropdownMenu({ function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
...props return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
} }
function DropdownMenuPortal({ function DropdownMenuPortal({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
...props return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
);
} }
function DropdownMenuTrigger({ function DropdownMenuTrigger({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
...props return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
return (
<DropdownMenuPrimitive.Trigger
data-slot="dropdown-menu-trigger"
{...props}
/>
);
} }
function DropdownMenuContent({ function DropdownMenuContent({
className, className,
sideOffset = 4, sideOffset = 4,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
return ( return (
<DropdownMenuPrimitive.Portal> <DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content <DropdownMenuPrimitive.Content
data-slot="dropdown-menu-content" data-slot="dropdown-menu-content"
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md', 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md',
className, className,
)} )}
{...props} {...props}
/> />
</DropdownMenuPrimitive.Portal> </DropdownMenuPrimitive.Portal>
); );
} }
function DropdownMenuGroup({ function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
...props return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return (
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
);
} }
function DropdownMenuItem({ function DropdownMenuItem({
className, className,
inset, inset,
variant = 'default', variant = 'default',
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & { }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean; inset?: boolean;
variant?: 'default' | 'destructive'; variant?: 'default' | 'destructive';
}) { }) {
return ( return (
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
data-slot="dropdown-menu-item" data-slot="dropdown-menu-item"
data-inset={inset} data-inset={inset}
data-variant={variant} data-variant={variant}
className={cn( className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className, className,
)} )}
{...props} {...props}
/> />
); );
} }
function DropdownMenuCheckboxItem({ function DropdownMenuCheckboxItem({
className, className,
children, children,
checked, checked,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
return ( return (
<DropdownMenuPrimitive.CheckboxItem <DropdownMenuPrimitive.CheckboxItem
data-slot="dropdown-menu-checkbox-item" data-slot="dropdown-menu-checkbox-item"
className={cn( className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className, className,
)} )}
checked={checked} checked={checked}
{...props} {...props}
> >
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator> <DropdownMenuPrimitive.ItemIndicator>
<CheckIcon className="size-4" /> <CheckIcon className="size-4" />
</DropdownMenuPrimitive.ItemIndicator> </DropdownMenuPrimitive.ItemIndicator>
</span> </span>
{children} {children}
</DropdownMenuPrimitive.CheckboxItem> </DropdownMenuPrimitive.CheckboxItem>
); );
} }
function DropdownMenuRadioGroup({ function DropdownMenuRadioGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
...props return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
return (
<DropdownMenuPrimitive.RadioGroup
data-slot="dropdown-menu-radio-group"
{...props}
/>
);
} }
function DropdownMenuRadioItem({ function DropdownMenuRadioItem({
className, className,
children, children,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
return ( return (
<DropdownMenuPrimitive.RadioItem <DropdownMenuPrimitive.RadioItem
data-slot="dropdown-menu-radio-item" data-slot="dropdown-menu-radio-item"
className={cn( className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className, className,
)} )}
{...props} {...props}
> >
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator> <DropdownMenuPrimitive.ItemIndicator>
<CircleIcon className="size-2 fill-current" /> <CircleIcon className="size-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator> </DropdownMenuPrimitive.ItemIndicator>
</span> </span>
{children} {children}
</DropdownMenuPrimitive.RadioItem> </DropdownMenuPrimitive.RadioItem>
); );
} }
function DropdownMenuLabel({ function DropdownMenuLabel({
className, className,
inset, inset,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & { }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean; inset?: boolean;
}) { }) {
return ( return (
<DropdownMenuPrimitive.Label <DropdownMenuPrimitive.Label
data-slot="dropdown-menu-label" data-slot="dropdown-menu-label"
data-inset={inset} data-inset={inset}
className={cn( className={cn('px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8', className)}
'px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8', {...props}
className, />
)} );
{...props}
/>
);
} }
function DropdownMenuSeparator({ function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
className, return (
...props <DropdownMenuPrimitive.Separator
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) { data-slot="dropdown-menu-separator"
return ( className={cn('bg-border -mx-1 my-1 h-px', className)}
<DropdownMenuPrimitive.Separator {...props}
data-slot="dropdown-menu-separator" />
className={cn('bg-border -mx-1 my-1 h-px', className)} );
{...props}
/>
);
} }
function DropdownMenuShortcut({ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
className, return (
...props <span
}: React.ComponentProps<'span'>) { data-slot="dropdown-menu-shortcut"
return ( className={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
<span {...props}
data-slot="dropdown-menu-shortcut" />
className={cn( );
'text-muted-foreground ml-auto text-xs tracking-widest',
className,
)}
{...props}
/>
);
} }
function DropdownMenuSub({ function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
...props return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
} }
function DropdownMenuSubTrigger({ function DropdownMenuSubTrigger({
className, className,
inset, inset,
children, children,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean; inset?: boolean;
}) { }) {
return ( return (
<DropdownMenuPrimitive.SubTrigger <DropdownMenuPrimitive.SubTrigger
data-slot="dropdown-menu-sub-trigger" data-slot="dropdown-menu-sub-trigger"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8', 'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',
className, className,
)} )}
{...props} {...props}
> >
{children} {children}
<ChevronRightIcon className="ml-auto size-4" /> <ChevronRightIcon className="ml-auto size-4" />
</DropdownMenuPrimitive.SubTrigger> </DropdownMenuPrimitive.SubTrigger>
); );
} }
function DropdownMenuSubContent({ function DropdownMenuSubContent({
className, className,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
return ( return (
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content" data-slot="dropdown-menu-sub-content"
className={cn( className={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg', 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg',
className, className,
)} )}
{...props} {...props}
/> />
); );
} }
export { export {
DropdownMenu, DropdownMenu,
DropdownMenuPortal, DropdownMenuPortal,
DropdownMenuTrigger, DropdownMenuTrigger,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuGroup, DropdownMenuGroup,
DropdownMenuLabel, DropdownMenuLabel,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuCheckboxItem, DropdownMenuCheckboxItem,
DropdownMenuRadioGroup, DropdownMenuRadioGroup,
DropdownMenuRadioItem, DropdownMenuRadioItem,
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuShortcut, DropdownMenuShortcut,
DropdownMenuSub, DropdownMenuSub,
DropdownMenuSubTrigger, DropdownMenuSubTrigger,
DropdownMenuSubContent, DropdownMenuSubContent,
}; };

View File

@ -2,5 +2,5 @@ import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)); return twMerge(clsx(inputs));
} }

View File

@ -30,8 +30,8 @@ importers:
specifier: workspace:* specifier: workspace:*
version: link:../../packages/ui version: link:../../packages/ui
lucide-react: lucide-react:
specifier: 0.475.0 specifier: 0.477.0
version: 0.475.0(react@19.0.0) version: 0.477.0(react@19.0.0)
next: next:
specifier: 15.2.0 specifier: 15.2.0
version: 15.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) version: 15.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -122,9 +122,6 @@ importers:
clsx: clsx:
specifier: ^2.1.1 specifier: ^2.1.1
version: 2.1.1 version: 2.1.1
lucide-react:
specifier: 0.475.0
version: 0.475.0(react@19.0.0)
react: react:
specifier: ^19.0.0 specifier: ^19.0.0
version: 19.0.0 version: 19.0.0
@ -165,6 +162,9 @@ importers:
class-variance-authority: class-variance-authority:
specifier: ^0.7.1 specifier: ^0.7.1
version: 0.7.1 version: 0.7.1
lucide-react:
specifier: 0.477.0
version: 0.477.0(react@19.0.0)
tailwindcss: tailwindcss:
specifier: ^4 specifier: ^4
version: 4.0.6 version: 4.0.6
@ -1855,8 +1855,8 @@ packages:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'} engines: {node: '>=12'}
lucide-react@0.475.0: lucide-react@0.477.0:
resolution: {integrity: sha512-NJzvVu1HwFVeZ+Gwq2q00KygM1aBhy/ZrhY9FsAgJtpB+E4R7uxRk9M2iKvHa6/vNxZydIB59htha4c2vvwvVg==} resolution: {integrity: sha512-yCf7aYxerFZAbd8jHJxjwe1j7jEMPptjnaOqdYeirFnEy85cNR3/L+o0I875CYFYya+eEVzZSbNuRk8BZPDpVw==}
peerDependencies: peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
@ -4475,7 +4475,7 @@ snapshots:
lru-cache@7.18.3: {} lru-cache@7.18.3: {}
lucide-react@0.475.0(react@19.0.0): lucide-react@0.477.0(react@19.0.0):
dependencies: dependencies:
react: 19.0.0 react: 19.0.0