snapshot of https://github.com/shadcn-ui/ui.git (main, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
267
apps/v4/registry/bases/aria/ui/context-menu.tsx
Normal file
267
apps/v4/registry/bases/aria/ui/context-menu.tsx
Normal file
@@ -0,0 +1,267 @@
|
||||
"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,
|
||||
}
|
||||
Reference in New Issue
Block a user