Files
upstream-shadcn-ui/apps/v4/registry/bases/aria/ui/context-menu.tsx
upstream-mirror 1416db152c
All checks were successful
Release / Create Version PR or Publish Stable Release (push) Has been skipped
Release / Publish Prerelease to NPM (push) Has been skipped
Monitor Registries / Monitor registry health (push) Has been skipped
snapshot of https://github.com/shadcn-ui/ui.git (main, 2026-09-15; blobs > 131072B omitted)
2026-09-15 22:44:43 +05:00

268 lines
6.6 KiB
TypeScript

"use client"
import * as React from "react"
import { cva } from "class-variance-authority"
import { cn } from "cn"
import {
composeRenderProps,
Header as HeaderPrimitive,
MenuItem as MenuItemPrimitive,
Menu as MenuPrimitive,
MenuSection as MenuSectionPrimitive,
MenuTrigger as MenuTriggerPrimitive,
Popover as PopoverPrimitive,
Separator as SeparatorPrimitive,
SubmenuTrigger as SubmenuTriggerPrimitive,
type MenuItemProps as MenuItemPrimitiveProps,
type MenuSectionProps as MenuSectionPrimitiveProps,
} from "react-aria-components"
import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"
function ContextMenu({
"data-slot": dataSlot = "context-menu-content",
placement = "bottom start",
offset = 4,
crossOffset = 0,
className,
children,
...props
}: Omit<
React.ComponentProps<typeof MenuPrimitive<object>>,
"children" | "className"
> &
Pick<
React.ComponentProps<typeof PopoverPrimitive>,
"placement" | "offset" | "crossOffset"
> & {
"data-slot"?: string
className?: string
children?: React.ReactNode
}) {
return (
<PopoverPrimitive
data-slot={dataSlot}
placement={placement}
offset={offset}
crossOffset={crossOffset}
className={cn(
"cn-context-menu-content-aria cn-menu-target cn-menu-translucent cn-menu-translucent-aria z-50 w-(--trigger-width) origin-(--trigger-anchor-point) overflow-x-hidden overflow-y-auto outline-none data-exiting:overflow-hidden",
className
)}
>
<MenuPrimitive
className="max-h-[inherit] overflow-x-hidden overflow-y-auto outline-hidden"
{...props}
>
{children}
</MenuPrimitive>
</PopoverPrimitive>
)
}
function ContextMenuTrigger({
...props
}: Omit<React.ComponentProps<typeof MenuTriggerPrimitive>, "trigger">) {
return (
<MenuTriggerPrimitive
data-slot="context-menu"
trigger="contextMenu"
{...props}
/>
)
}
function ContextMenuGroup({
...props
}: Omit<MenuSectionPrimitiveProps<object>, "children"> & {
children?: React.ReactNode
}) {
return <MenuSectionPrimitive data-slot="context-menu-group" {...props} />
}
function ContextMenuLabel({
className,
inset,
...props
}: React.ComponentProps<typeof HeaderPrimitive> & {
inset?: boolean
}) {
return (
<HeaderPrimitive
data-slot="context-menu-label"
data-inset={inset}
className={cn("cn-context-menu-label", className)}
{...props}
/>
)
}
const contextMenuItemVariants = cva(
"group/context-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
selectionMode: {
none: "cn-context-menu-item",
single: "cn-context-menu-radio-item",
multiple: "cn-context-menu-checkbox-item",
},
},
}
)
function ContextMenuItem({
className,
inset,
variant = "default",
children,
...props
}: MenuItemPrimitiveProps<object> & {
inset?: boolean
variant?: "default" | "destructive"
}) {
return (
<MenuItemPrimitive
data-slot="context-menu-item"
data-inset={inset}
data-variant={variant}
textValue={typeof children === "string" ? children : props.textValue}
className={composeRenderProps(className, (className, { selectionMode }) =>
cn(contextMenuItemVariants({ selectionMode }), className)
)}
{...props}
>
{composeRenderProps(
children,
(children, { isSelected, selectionMode }) => (
<>
{selectionMode !== "none" ? (
<span
className="cn-context-menu-item-indicator pointer-events-none"
data-slot={
selectionMode === "single"
? "context-menu-radio-item-indicator"
: "context-menu-checkbox-item-indicator"
}
>
{isSelected ? (
<IconPlaceholder
lucide="CheckIcon"
tabler="IconCheck"
hugeicons="Tick02Icon"
phosphor="CheckIcon"
remixicon="RiCheckLine"
/>
) : null}
</span>
) : null}
{children}
</>
)
)}
</MenuItemPrimitive>
)
}
function ContextMenuSub({
...props
}: React.ComponentProps<typeof SubmenuTriggerPrimitive>) {
return <SubmenuTriggerPrimitive data-slot="context-menu-sub" {...props} />
}
function ContextMenuSubTrigger({
className,
inset,
children,
...props
}: MenuItemPrimitiveProps<object> & {
inset?: boolean
}) {
return (
<MenuItemPrimitive
data-slot="context-menu-sub-trigger"
data-inset={inset}
textValue={typeof children === "string" ? children : props.textValue}
className={cn(
"cn-context-menu-sub-trigger flex cursor-default items-center outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
)}
{...props}
>
{composeRenderProps(children, (children) => (
<>
{children}
<IconPlaceholder
lucide="ChevronRightIcon"
tabler="IconChevronRight"
hugeicons="ArrowRight01Icon"
phosphor="CaretRightIcon"
remixicon="RiArrowRightSLine"
className="cn-rtl-flip ml-auto"
/>
</>
))}
</MenuItemPrimitive>
)
}
function ContextMenuSubContent({
placement = "end top",
crossOffset = -3,
offset = 0,
className,
...props
}: React.ComponentProps<typeof ContextMenu>) {
return (
<ContextMenu
data-slot="context-menu-sub-content"
className={cn(
"cn-context-menu-sub-content-aria cn-menu-target cn-menu-translucent w-auto",
className
)}
placement={placement}
crossOffset={crossOffset}
offset={offset}
{...props}
/>
)
}
function ContextMenuSeparator({
className,
...props
}: React.ComponentProps<typeof SeparatorPrimitive>) {
return (
<SeparatorPrimitive
data-slot="context-menu-separator"
className={cn("cn-context-menu-separator", className)}
{...props}
/>
)
}
function ContextMenuShortcut({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
data-slot="context-menu-shortcut"
className={cn("cn-context-menu-shortcut", className)}
{...props}
/>
)
}
export {
ContextMenu,
ContextMenuTrigger,
ContextMenuItem,
ContextMenuLabel,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuGroup,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
}