parent
88b6d2a597
commit
99a2893d51
@ -0,0 +1,50 @@ |
||||
'use client'; |
||||
|
||||
import { |
||||
AlertDialog, |
||||
AlertDialogAction, |
||||
AlertDialogDescription, |
||||
AlertDialogHeader, |
||||
AlertDialogContent, |
||||
AlertDialogTitle, |
||||
AlertDialogTrigger, |
||||
AlertDialogFooter, |
||||
AlertDialogCancel, |
||||
} from '@/components/ui/alert-dialog'; |
||||
import { Button } from '@/components/ui/button'; |
||||
import { useState } from 'react'; |
||||
|
||||
export default function AlertDialogPage() { |
||||
const [dialogResult, setDialogResult] = useState(''); |
||||
|
||||
function handleContinue() { |
||||
setDialogResult('계속을 눌렀습니다.'); |
||||
} |
||||
|
||||
function handleCancel() { |
||||
setDialogResult('취소를 눌렀습니다.'); |
||||
} |
||||
|
||||
return ( |
||||
<> |
||||
<AlertDialog> |
||||
<AlertDialogTrigger asChild> |
||||
<Button variant="outline">다이얼로그 출력</Button> |
||||
</AlertDialogTrigger> |
||||
<AlertDialogContent> |
||||
<AlertDialogHeader> |
||||
<AlertDialogTitle>작업을 확인하세요</AlertDialogTitle> |
||||
<AlertDialogDescription> |
||||
이 작업은 되돌릴 수 없습니다. 진행하시겠습니까? |
||||
</AlertDialogDescription> |
||||
</AlertDialogHeader> |
||||
<AlertDialogFooter> |
||||
<AlertDialogCancel onClick={handleCancel}>취소</AlertDialogCancel> |
||||
<AlertDialogAction onClick={handleContinue}>계속</AlertDialogAction> |
||||
</AlertDialogFooter> |
||||
</AlertDialogContent> |
||||
</AlertDialog> |
||||
{dialogResult && <p className="mt-4 text-blue-500">{dialogResult}</p>} |
||||
</> |
||||
); |
||||
} |
@ -0,0 +1,31 @@ |
||||
'use client'; |
||||
|
||||
import { Badge } from '@/components/ui/badge'; |
||||
import { BadgeCheckIcon } from 'lucide-react'; |
||||
|
||||
export default function BadgePage() { |
||||
return ( |
||||
<> |
||||
<div className="flex items-center justify-center space-x-3"> |
||||
<div className="flex flex-col items-center gap-2 rounded p-3 shadow-lg"> |
||||
<Badge>Badge</Badge> |
||||
<Badge variant="secondary">Secondary</Badge> |
||||
<Badge variant="outline">Outline</Badge> |
||||
<Badge variant="destructive">Destructive</Badge> |
||||
</div> |
||||
<div className="flex flex-col items-center gap-2 rounded p-3 shadow-lg"> |
||||
<Badge> |
||||
<BadgeCheckIcon /> |
||||
Varified |
||||
</Badge> |
||||
<Badge variant="destructive" className="h-5 min-w-5 rounded-full font-mono tabular-nums"> |
||||
99 |
||||
</Badge> |
||||
<Badge variant="outline" className="h-5 min-w-5 rounded-full font-mono tabular-nums"> |
||||
100+ |
||||
</Badge> |
||||
</div> |
||||
</div> |
||||
</> |
||||
); |
||||
} |
@ -0,0 +1,40 @@ |
||||
'use client'; |
||||
|
||||
import { Calendar } from '@/components/ui/calendar'; |
||||
import { DateTime } from 'luxon'; |
||||
import { useState } from 'react'; |
||||
|
||||
export default function CalendarPage() { |
||||
const [date, setDate] = useState<Date | undefined>(new Date()); |
||||
const [luxonDate, setLuxonDate] = useState<DateTime | undefined>(DateTime.now()); |
||||
|
||||
return ( |
||||
<> |
||||
<div className="flex items-center justify-center space-x-4"> |
||||
<div> |
||||
<Calendar |
||||
mode="single" |
||||
selected={date} |
||||
onSelect={setDate} |
||||
className="rounded-md border shadow-sm" |
||||
captionLayout="dropdown" |
||||
/> |
||||
<p className="text-center text-sm text-gray-600">{date?.toLocaleDateString()}</p> |
||||
</div> |
||||
|
||||
<div> |
||||
<Calendar |
||||
mode="single" |
||||
selected={luxonDate ? luxonDate.toJSDate() : undefined} |
||||
onSelect={(d: Date | undefined) => setLuxonDate(d ? DateTime.fromJSDate(d) : undefined)} |
||||
className="rounded-md border shadow-sm" |
||||
captionLayout="dropdown" |
||||
/> |
||||
<p className="text-center text-sm text-gray-600"> |
||||
{luxonDate?.toJSDate().toLocaleDateString()} |
||||
</p> |
||||
</div> |
||||
</div> |
||||
</> |
||||
); |
||||
} |
@ -0,0 +1,157 @@ |
||||
"use client" |
||||
|
||||
import * as React from "react" |
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" |
||||
|
||||
import { cn } from "@/lib/utils" |
||||
import { buttonVariants } from "@/components/ui/button" |
||||
|
||||
function AlertDialog({ |
||||
...props |
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) { |
||||
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} /> |
||||
} |
||||
|
||||
function AlertDialogTrigger({ |
||||
...props |
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) { |
||||
return ( |
||||
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} /> |
||||
) |
||||
} |
||||
|
||||
function AlertDialogPortal({ |
||||
...props |
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) { |
||||
return ( |
||||
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} /> |
||||
) |
||||
} |
||||
|
||||
function AlertDialogOverlay({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) { |
||||
return ( |
||||
<AlertDialogPrimitive.Overlay |
||||
data-slot="alert-dialog-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 |
||||
)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function AlertDialogContent({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) { |
||||
return ( |
||||
<AlertDialogPortal> |
||||
<AlertDialogOverlay /> |
||||
<AlertDialogPrimitive.Content |
||||
data-slot="alert-dialog-content" |
||||
className={cn( |
||||
"bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg", |
||||
className |
||||
)} |
||||
{...props} |
||||
/> |
||||
</AlertDialogPortal> |
||||
) |
||||
} |
||||
|
||||
function AlertDialogHeader({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<"div">) { |
||||
return ( |
||||
<div |
||||
data-slot="alert-dialog-header" |
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function AlertDialogFooter({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<"div">) { |
||||
return ( |
||||
<div |
||||
data-slot="alert-dialog-footer" |
||||
className={cn( |
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", |
||||
className |
||||
)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function AlertDialogTitle({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) { |
||||
return ( |
||||
<AlertDialogPrimitive.Title |
||||
data-slot="alert-dialog-title" |
||||
className={cn("text-lg font-semibold", className)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function AlertDialogDescription({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) { |
||||
return ( |
||||
<AlertDialogPrimitive.Description |
||||
data-slot="alert-dialog-description" |
||||
className={cn("text-muted-foreground text-sm", className)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function AlertDialogAction({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) { |
||||
return ( |
||||
<AlertDialogPrimitive.Action |
||||
className={cn(buttonVariants(), className)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function AlertDialogCancel({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) { |
||||
return ( |
||||
<AlertDialogPrimitive.Cancel |
||||
className={cn(buttonVariants({ variant: "outline" }), className)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
export { |
||||
AlertDialog, |
||||
AlertDialogPortal, |
||||
AlertDialogOverlay, |
||||
AlertDialogTrigger, |
||||
AlertDialogContent, |
||||
AlertDialogHeader, |
||||
AlertDialogFooter, |
||||
AlertDialogTitle, |
||||
AlertDialogDescription, |
||||
AlertDialogAction, |
||||
AlertDialogCancel, |
||||
} |
@ -0,0 +1,66 @@ |
||||
import * as React from "react" |
||||
import { cva, type VariantProps } from "class-variance-authority" |
||||
|
||||
import { cn } from "@/lib/utils" |
||||
|
||||
const alertVariants = cva( |
||||
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", |
||||
{ |
||||
variants: { |
||||
variant: { |
||||
default: "bg-card text-card-foreground", |
||||
destructive: |
||||
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", |
||||
}, |
||||
}, |
||||
defaultVariants: { |
||||
variant: "default", |
||||
}, |
||||
} |
||||
) |
||||
|
||||
function Alert({ |
||||
className, |
||||
variant, |
||||
...props |
||||
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) { |
||||
return ( |
||||
<div |
||||
data-slot="alert" |
||||
role="alert" |
||||
className={cn(alertVariants({ variant }), className)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { |
||||
return ( |
||||
<div |
||||
data-slot="alert-title" |
||||
className={cn( |
||||
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", |
||||
className |
||||
)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function AlertDescription({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<"div">) { |
||||
return ( |
||||
<div |
||||
data-slot="alert-description" |
||||
className={cn( |
||||
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed", |
||||
className |
||||
)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
export { Alert, AlertTitle, AlertDescription } |
@ -0,0 +1,46 @@ |
||||
import * as React from "react" |
||||
import { Slot } from "@radix-ui/react-slot" |
||||
import { cva, type VariantProps } from "class-variance-authority" |
||||
|
||||
import { cn } from "@/lib/utils" |
||||
|
||||
const badgeVariants = cva( |
||||
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", |
||||
{ |
||||
variants: { |
||||
variant: { |
||||
default: |
||||
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", |
||||
secondary: |
||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", |
||||
destructive: |
||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", |
||||
outline: |
||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", |
||||
}, |
||||
}, |
||||
defaultVariants: { |
||||
variant: "default", |
||||
}, |
||||
} |
||||
) |
||||
|
||||
function Badge({ |
||||
className, |
||||
variant, |
||||
asChild = false, |
||||
...props |
||||
}: React.ComponentProps<"span"> & |
||||
VariantProps<typeof badgeVariants> & { asChild?: boolean }) { |
||||
const Comp = asChild ? Slot : "span" |
||||
|
||||
return ( |
||||
<Comp |
||||
data-slot="badge" |
||||
className={cn(badgeVariants({ variant }), className)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
export { Badge, badgeVariants } |
@ -0,0 +1,213 @@ |
||||
"use client" |
||||
|
||||
import * as React from "react" |
||||
import { |
||||
ChevronDownIcon, |
||||
ChevronLeftIcon, |
||||
ChevronRightIcon, |
||||
} from "lucide-react" |
||||
import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker" |
||||
|
||||
import { cn } from "@/lib/utils" |
||||
import { Button, buttonVariants } from "@/components/ui/button" |
||||
|
||||
function Calendar({ |
||||
className, |
||||
classNames, |
||||
showOutsideDays = true, |
||||
captionLayout = "label", |
||||
buttonVariant = "ghost", |
||||
formatters, |
||||
components, |
||||
...props |
||||
}: React.ComponentProps<typeof DayPicker> & { |
||||
buttonVariant?: React.ComponentProps<typeof Button>["variant"] |
||||
}) { |
||||
const defaultClassNames = getDefaultClassNames() |
||||
|
||||
return ( |
||||
<DayPicker |
||||
showOutsideDays={showOutsideDays} |
||||
className={cn( |
||||
"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent", |
||||
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`, |
||||
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, |
||||
className |
||||
)} |
||||
captionLayout={captionLayout} |
||||
formatters={{ |
||||
formatMonthDropdown: (date) => |
||||
date.toLocaleString("default", { month: "short" }), |
||||
...formatters, |
||||
}} |
||||
classNames={{ |
||||
root: cn("w-fit", defaultClassNames.root), |
||||
months: cn( |
||||
"flex gap-4 flex-col md:flex-row relative", |
||||
defaultClassNames.months |
||||
), |
||||
month: cn("flex flex-col w-full gap-4", defaultClassNames.month), |
||||
nav: cn( |
||||
"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between", |
||||
defaultClassNames.nav |
||||
), |
||||
button_previous: cn( |
||||
buttonVariants({ variant: buttonVariant }), |
||||
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none", |
||||
defaultClassNames.button_previous |
||||
), |
||||
button_next: cn( |
||||
buttonVariants({ variant: buttonVariant }), |
||||
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none", |
||||
defaultClassNames.button_next |
||||
), |
||||
month_caption: cn( |
||||
"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)", |
||||
defaultClassNames.month_caption |
||||
), |
||||
dropdowns: cn( |
||||
"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5", |
||||
defaultClassNames.dropdowns |
||||
), |
||||
dropdown_root: cn( |
||||
"relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md", |
||||
defaultClassNames.dropdown_root |
||||
), |
||||
dropdown: cn( |
||||
"absolute bg-popover inset-0 opacity-0", |
||||
defaultClassNames.dropdown |
||||
), |
||||
caption_label: cn( |
||||
"select-none font-medium", |
||||
captionLayout === "label" |
||||
? "text-sm" |
||||
: "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5", |
||||
defaultClassNames.caption_label |
||||
), |
||||
table: "w-full border-collapse", |
||||
weekdays: cn("flex", defaultClassNames.weekdays), |
||||
weekday: cn( |
||||
"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none", |
||||
defaultClassNames.weekday |
||||
), |
||||
week: cn("flex w-full mt-2", defaultClassNames.week), |
||||
week_number_header: cn( |
||||
"select-none w-(--cell-size)", |
||||
defaultClassNames.week_number_header |
||||
), |
||||
week_number: cn( |
||||
"text-[0.8rem] select-none text-muted-foreground", |
||||
defaultClassNames.week_number |
||||
), |
||||
day: cn( |
||||
"relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none", |
||||
defaultClassNames.day |
||||
), |
||||
range_start: cn( |
||||
"rounded-l-md bg-accent", |
||||
defaultClassNames.range_start |
||||
), |
||||
range_middle: cn("rounded-none", defaultClassNames.range_middle), |
||||
range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end), |
||||
today: cn( |
||||
"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none", |
||||
defaultClassNames.today |
||||
), |
||||
outside: cn( |
||||
"text-muted-foreground aria-selected:text-muted-foreground", |
||||
defaultClassNames.outside |
||||
), |
||||
disabled: cn( |
||||
"text-muted-foreground opacity-50", |
||||
defaultClassNames.disabled |
||||
), |
||||
hidden: cn("invisible", defaultClassNames.hidden), |
||||
...classNames, |
||||
}} |
||||
components={{ |
||||
Root: ({ className, rootRef, ...props }) => { |
||||
return ( |
||||
<div |
||||
data-slot="calendar" |
||||
ref={rootRef} |
||||
className={cn(className)} |
||||
{...props} |
||||
/> |
||||
) |
||||
}, |
||||
Chevron: ({ className, orientation, ...props }) => { |
||||
if (orientation === "left") { |
||||
return ( |
||||
<ChevronLeftIcon className={cn("size-4", className)} {...props} /> |
||||
) |
||||
} |
||||
|
||||
if (orientation === "right") { |
||||
return ( |
||||
<ChevronRightIcon |
||||
className={cn("size-4", className)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
return ( |
||||
<ChevronDownIcon className={cn("size-4", className)} {...props} /> |
||||
) |
||||
}, |
||||
DayButton: CalendarDayButton, |
||||
WeekNumber: ({ children, ...props }) => { |
||||
return ( |
||||
<td {...props}> |
||||
<div className="flex size-(--cell-size) items-center justify-center text-center"> |
||||
{children} |
||||
</div> |
||||
</td> |
||||
) |
||||
}, |
||||
...components, |
||||
}} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function CalendarDayButton({ |
||||
className, |
||||
day, |
||||
modifiers, |
||||
...props |
||||
}: React.ComponentProps<typeof DayButton>) { |
||||
const defaultClassNames = getDefaultClassNames() |
||||
|
||||
const ref = React.useRef<HTMLButtonElement>(null) |
||||
React.useEffect(() => { |
||||
if (modifiers.focused) ref.current?.focus() |
||||
}, [modifiers.focused]) |
||||
|
||||
return ( |
||||
<Button |
||||
ref={ref} |
||||
variant="ghost" |
||||
size="icon" |
||||
data-day={day.date.toLocaleDateString()} |
||||
data-selected-single={ |
||||
modifiers.selected && |
||||
!modifiers.range_start && |
||||
!modifiers.range_end && |
||||
!modifiers.range_middle |
||||
} |
||||
data-range-start={modifiers.range_start} |
||||
data-range-end={modifiers.range_end} |
||||
data-range-middle={modifiers.range_middle} |
||||
className={cn( |
||||
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70", |
||||
defaultClassNames.day, |
||||
className |
||||
)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
export { Calendar, CalendarDayButton } |
@ -0,0 +1,168 @@ |
||||
import * as React from "react" |
||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu" |
||||
import { cva } from "class-variance-authority" |
||||
import { ChevronDownIcon } from "lucide-react" |
||||
|
||||
import { cn } from "@/lib/utils" |
||||
|
||||
function NavigationMenu({ |
||||
className, |
||||
children, |
||||
viewport = true, |
||||
...props |
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & { |
||||
viewport?: boolean |
||||
}) { |
||||
return ( |
||||
<NavigationMenuPrimitive.Root |
||||
data-slot="navigation-menu" |
||||
data-viewport={viewport} |
||||
className={cn( |
||||
"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center", |
||||
className |
||||
)} |
||||
{...props} |
||||
> |
||||
{children} |
||||
{viewport && <NavigationMenuViewport />} |
||||
</NavigationMenuPrimitive.Root> |
||||
) |
||||
} |
||||
|
||||
function NavigationMenuList({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) { |
||||
return ( |
||||
<NavigationMenuPrimitive.List |
||||
data-slot="navigation-menu-list" |
||||
className={cn( |
||||
"group flex flex-1 list-none items-center justify-center gap-1", |
||||
className |
||||
)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function NavigationMenuItem({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) { |
||||
return ( |
||||
<NavigationMenuPrimitive.Item |
||||
data-slot="navigation-menu-item" |
||||
className={cn("relative", className)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
const navigationMenuTriggerStyle = cva( |
||||
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1" |
||||
) |
||||
|
||||
function NavigationMenuTrigger({ |
||||
className, |
||||
children, |
||||
...props |
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) { |
||||
return ( |
||||
<NavigationMenuPrimitive.Trigger |
||||
data-slot="navigation-menu-trigger" |
||||
className={cn(navigationMenuTriggerStyle(), "group", className)} |
||||
{...props} |
||||
> |
||||
{children}{" "} |
||||
<ChevronDownIcon |
||||
className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180" |
||||
aria-hidden="true" |
||||
/> |
||||
</NavigationMenuPrimitive.Trigger> |
||||
) |
||||
} |
||||
|
||||
function NavigationMenuContent({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) { |
||||
return ( |
||||
<NavigationMenuPrimitive.Content |
||||
data-slot="navigation-menu-content" |
||||
className={cn( |
||||
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto", |
||||
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none", |
||||
className |
||||
)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function NavigationMenuViewport({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) { |
||||
return ( |
||||
<div |
||||
className={cn( |
||||
"absolute top-full left-0 isolate z-50 flex justify-center" |
||||
)} |
||||
> |
||||
<NavigationMenuPrimitive.Viewport |
||||
data-slot="navigation-menu-viewport" |
||||
className={cn( |
||||
"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]", |
||||
className |
||||
)} |
||||
{...props} |
||||
/> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
function NavigationMenuLink({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) { |
||||
return ( |
||||
<NavigationMenuPrimitive.Link |
||||
data-slot="navigation-menu-link" |
||||
className={cn( |
||||
"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4", |
||||
className |
||||
)} |
||||
{...props} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
function NavigationMenuIndicator({ |
||||
className, |
||||
...props |
||||
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) { |
||||
return ( |
||||
<NavigationMenuPrimitive.Indicator |
||||
data-slot="navigation-menu-indicator" |
||||
className={cn( |
||||
"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden", |
||||
className |
||||
)} |
||||
{...props} |
||||
> |
||||
<div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" /> |
||||
</NavigationMenuPrimitive.Indicator> |
||||
) |
||||
} |
||||
|
||||
export { |
||||
NavigationMenu, |
||||
NavigationMenuList, |
||||
NavigationMenuItem, |
||||
NavigationMenuContent, |
||||
NavigationMenuTrigger, |
||||
NavigationMenuLink, |
||||
NavigationMenuIndicator, |
||||
NavigationMenuViewport, |
||||
navigationMenuTriggerStyle, |
||||
} |
Loading…
Reference in new issue