Menu

A menu displays a list of actions or options that a user can choose.

RAC Docs

Source

"use client";

import type { HTMLAttributes } from "react";

import {
	Header as AriaHeader,
	Menu as AriaMenu,
	MenuItem as AriaMenuItem,
	MenuSection as AriaMenuSection,
	MenuTrigger as AriaMenuTrigger,
	type MenuItemProps,
	type MenuProps,
	Popover,
	Separator,
} from "react-aria-components";
import { tv } from "tailwind-variants";

export const menu = tv({
	slots: {
		menuPopover:
			"data-[entering]:fade-in data-[exiting]:fade-out overflow-auto rounded-2xl border border-border/25 bg-surface shadow-xl data-[entering]:animate-in data-[exiting]:animate-out",
		header: "p-2 font-semibold",
		content: "flex h-fit min-w-56 flex-col gap-2 p-3 outline-none",
		item: "relative flex cursor-default justify-between rounded-lg p-3 font-semibold outline-none data-[disabled]:cursor-not-allowed data-[focused]:bg-secondary data-[disabled]:text-fg-disabled",
		separator: "h-[1px] bg-border",
	},
});

const { menuPopover, content, header, item, separator } = menu();

const MenuTrigger = AriaMenuTrigger;
const MenuSection = AriaMenuSection;

const MenuContent = <T extends object>({
	children,
	className,
	...props
}: MenuProps<T> & { className?: string }) => (
	<Popover className={menuPopover()}>
		<AriaMenu {...props} className={content({ className })}>
			{children}
		</AriaMenu>
	</Popover>
);

const MenuItem = ({
	children,
	className,
	...props
}: MenuItemProps & { className?: string }) => (
	<AriaMenuItem {...props} className={item({ className })}>
		{children}
	</AriaMenuItem>
);

const MenuHeader = ({
	children,
	className,
	...props
}: HTMLAttributes<HTMLElement> & { className?: string }) => (
	<AriaHeader {...props} className={header({ className })}>
		{children}
	</AriaHeader>
);

const MenuSeperator = ({
	className,
	...props
}: HTMLAttributes<HTMLElement> & { className?: string }) => (
	<Separator {...props} className={separator({ className })} />
);

export {
	MenuContent,
	MenuHeader,
	MenuItem,
	MenuSeperator,
	MenuTrigger,
	MenuSection,
};