/*! ratio-ui · design system for knowledge sharing · https://github.com/losol/ratio-ui · (c) 2026 Losol AS · MPL-2.0 */
import { cn } from "../../utils/cn.js";
import { Check, ChevronDown, Moon, Sun } from "../../icons/index.js";
import { Children, createContext, isValidElement, useContext, useEffect, useId, useRef } from "react";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { Button, Header, Menu, MenuItem, MenuSection, MenuTrigger, Popover, Separator } from "react-aria-components";
//#region src/core/Menu/Menu.tsx
var styles = {
	popover: cn("flex flex-col min-w-[280px] max-w-sm origin-top-right border border-border-1 rounded-xl overflow-hidden", "shadow-[0_1px_0_color-mix(in_oklch,var(--primary)_6%,transparent),0_12px_28px_color-mix(in_oklch,var(--primary)_18%,transparent),0_4px_8px_color-mix(in_oklch,var(--primary)_12%,transparent)]", "dark:shadow-[0_1px_0_rgb(255_255_255/0.08),0_16px_36px_rgb(0_0_0/0.6),0_4px_12px_rgb(0_0_0/0.4)]", "transition-[opacity,transform] duration-150 ease-out", "data-[entering]:opacity-0 data-[entering]:-translate-y-1 data-[entering]:scale-[0.98]", "data-[exiting]:opacity-0 data-[exiting]:-translate-y-1 data-[exiting]:scale-[0.98]"),
	menuItemsList: "bg-card focus:outline-hidden p-1.5 min-h-0 flex-1 overflow-y-auto overscroll-contain",
	menuItem: cn("cursor-pointer group relative flex w-full items-center gap-[11px] rounded-lg px-[11px] py-[9px]", "[font-size:var(--menu-font-size,14px)] [font-weight:var(--menu-font-weight,400)] text-(--text) transition-colors hover:bg-card-hover", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--focus-ring)", "data-[current]:text-(--primary) data-[current]:bg-[color-mix(in_oklch,var(--accent)_12%,transparent)]", "data-[current]:before:content-[\"\"] data-[current]:before:absolute data-[current]:before:left-[3px]", "data-[current]:before:top-2 data-[current]:before:bottom-2 data-[current]:before:w-[3px]", "data-[current]:before:rounded-[3px] data-[current]:before:bg-(--accent)"),
	menuItemDanger: cn("text-(--error-solid)", "hover:bg-[color-mix(in_oklch,var(--error-solid)_14%,transparent)] hover:text-(--error-solid)"),
	itemIcon: cn("shrink-0 text-(--text-subtle) transition-colors group-hover:text-(--text)", "group-data-[current]:text-(--primary)", "[&>svg]:h-[17px] [&>svg]:w-[17px]"),
	itemIconDanger: "text-(--error-solid) group-hover:text-(--error-solid)",
	triggerDefault: cn("group inline-flex min-w-[150px] cursor-pointer items-center justify-between gap-3", "rounded-full border-[1.5px] py-2 pl-4 pr-2.5 [font-size:var(--menu-font-size,14px)] font-semibold", "border-primary-400 text-primary-700 dark:border-primary-500 dark:text-primary-300 bg-transparent", "transition-all duration-200 active:scale-[0.96]", "hover:bg-[color-mix(in_oklch,var(--color-primary-400)_14%,transparent)] hover:text-primary-800 dark:hover:text-primary-200", "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-(--focus-ring)", "aria-expanded:bg-primary-300 aria-expanded:border-primary-300 aria-expanded:text-primary-950", "aria-expanded:shadow-[0_0_0_4px_color-mix(in_oklch,var(--color-primary-400)_30%,transparent),0_4px_14px_-2px_color-mix(in_oklch,var(--color-primary-400)_55%,transparent)]"),
	section: "",
	sectionLabel: "block px-3 pt-2 pb-1 text-[10.5px] font-bold uppercase tracking-[0.07em] text-(--text-subtle)"
};
var MenuActionsContext = createContext({
	register: () => {},
	unregister: () => {}
});
var MenuTrigger$1 = ({ children, className, testId }) => /* @__PURE__ */ jsx(Button, {
	"data-testid": testId ?? "logged-in-menu-button",
	className: className ?? styles.triggerDefault,
	children
});
MenuTrigger$1.displayName = "Menu.Trigger";
var MenuLink = ({ icon, isCurrent, variant, children, testId, ...rest }) => /* @__PURE__ */ jsxs(MenuItem, {
	...rest,
	"aria-current": isCurrent ? "page" : void 0,
	"data-current": isCurrent || void 0,
	"data-testid": testId,
	className: cn(styles.menuItem, variant === "danger" && styles.menuItemDanger),
	children: [icon && /* @__PURE__ */ jsx("span", {
		"aria-hidden": true,
		className: cn(styles.itemIcon, variant === "danger" && styles.itemIconDanger),
		children: icon
	}), /* @__PURE__ */ jsx("span", {
		className: "min-w-0 flex-1",
		children
	})]
});
var Menu$1 = ({ children, placement = "bottom end", isOpen, defaultOpen, onOpenChange, maxHeight }) => {
	const actionsRef = useRef(/* @__PURE__ */ new Map());
	const headerLabelId = useId();
	const api = useRef({
		register: (id, fn) => actionsRef.current.set(id, fn),
		unregister: (id) => actionsRef.current.delete(id)
	}).current;
	let trigger = null;
	let header = null;
	const items = [];
	for (const child of Children.toArray(children)) {
		if (isValidElement(child)) {
			if (child.type === MenuTrigger$1) {
				if (trigger === null) trigger = child;
				continue;
			}
			if (child.type === MenuHeader) {
				if (header === null) header = child;
				continue;
			}
		}
		items.push(child);
	}
	if (trigger === null) throw new Error("Menu requires a <Menu.Trigger> child. Wrap the trigger label/content in <Menu.Trigger>...</Menu.Trigger>.");
	return /* @__PURE__ */ jsxs(MenuTrigger, {
		isOpen,
		defaultOpen,
		onOpenChange,
		children: [trigger, /* @__PURE__ */ jsxs(Popover, {
			placement,
			maxHeight,
			className: styles.popover,
			children: [header && /* @__PURE__ */ jsx("div", {
				id: headerLabelId,
				className: "shrink-0 bg-card",
				children: header
			}), /* @__PURE__ */ jsx(MenuActionsContext.Provider, {
				value: api,
				children: /* @__PURE__ */ jsx(Menu, {
					className: styles.menuItemsList,
					"aria-labelledby": header ? headerLabelId : void 0,
					onAction: (key) => {
						const fn = actionsRef.current.get(key.toString());
						if (fn) fn();
					},
					children: items
				})
			})]
		})]
	});
};
var MenuButton = (props) => {
	const { register, unregister } = useContext(MenuActionsContext);
	useEffect(() => {
		register(props.id, props.onClick);
		return () => unregister(props.id);
	}, [
		props.id,
		props.onClick,
		register,
		unregister
	]);
	const { icon, variant, children, testId, onClick, ...rest } = props;
	return /* @__PURE__ */ jsxs(MenuItem, {
		...rest,
		className: cn(styles.menuItem, variant === "danger" && styles.menuItemDanger),
		"data-testid": testId,
		children: [icon && /* @__PURE__ */ jsx("span", {
			"aria-hidden": true,
			className: cn(styles.itemIcon, variant === "danger" && styles.itemIconDanger),
			children: icon
		}), /* @__PURE__ */ jsx("span", {
			className: "min-w-0 flex-1",
			children
		})]
	});
};
var MenuThemeToggle = ({ theme, onThemeChange, lightLabel = "Light theme", darkLabel = "Dark theme" }) => {
	const isDark = theme === "dark";
	const id = useId();
	const { register, unregister } = useContext(MenuActionsContext);
	const handlerRef = useRef(onThemeChange);
	handlerRef.current = onThemeChange;
	const themeRef = useRef(isDark);
	themeRef.current = isDark;
	useEffect(() => {
		register(id, () => handlerRef.current(themeRef.current ? "light" : "dark"));
		return () => unregister(id);
	}, [
		id,
		register,
		unregister
	]);
	return /* @__PURE__ */ jsx(MenuItem, {
		id,
		className: styles.menuItem,
		children: /* @__PURE__ */ jsxs("span", {
			className: "flex items-center gap-2",
			children: [isDark ? /* @__PURE__ */ jsx(Sun, {
				className: "w-5 h-5",
				"aria-hidden": "true"
			}) : /* @__PURE__ */ jsx(Moon, {
				className: "w-5 h-5",
				"aria-hidden": "true"
			}), isDark ? lightLabel : darkLabel]
		})
	});
};
/**
* Group of menu items with an optional uppercase eyebrow label. Pass
* `selectionMode` to make the section selectable — pair it with
* {@link MenuOption} rows, which render a check mark when selected
* (the language-picker / view-as pattern in user menus).
*
* @beta API may evolve before release.
*/
var MenuSection$1 = ({ label, children, className, ...selection }) => /* @__PURE__ */ jsxs(MenuSection, {
	...selection,
	className: cn(styles.section, className),
	children: [label && /* @__PURE__ */ jsx(Header, {
		className: styles.sectionLabel,
		children: label
	}), children]
});
MenuSection$1.displayName = "Menu.Section";
/**
* Selectable row for a `Menu.Section` with `selectionMode` — shows a
* primary-colored check mark when selected.
*
* @beta API may evolve before release.
*/
var MenuOption = ({ id, children, isDisabled, textValue, testId }) => /* @__PURE__ */ jsx(MenuItem, {
	id,
	isDisabled,
	textValue: textValue ?? (typeof children === "string" ? children : void 0),
	className: styles.menuItem,
	"data-testid": testId,
	children: ({ isSelected }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
		className: "min-w-0 flex-1 truncate",
		children
	}), isSelected && /* @__PURE__ */ jsx(Check, {
		"aria-hidden": "true",
		className: "ml-2 h-4 w-4 shrink-0 text-(--primary)"
	})] })
});
MenuOption.displayName = "Menu.Option";
/**
* Semantic divider between menu regions (announced as a separator, not just
* drawn) — e.g. before a "Log out" action. Wraps React Aria's `Separator`.
*
* @beta API may evolve before release.
*/
var MenuSeparator = ({ className }) => /* @__PURE__ */ jsx(Separator, { className: cn("mx-2 my-1.5 h-px border-0 bg-border-1 opacity-70", className) });
MenuSeparator.displayName = "Menu.Separator";
/**
* Convenience: the chevron used by the default trigger pattern. Exported
* so consumers can drop it into a custom `Menu.Trigger` without having to
* reach into `../icons` themselves.
*/
var MenuChevron = ({ className }) => /* @__PURE__ */ jsx("span", {
	"aria-hidden": "true",
	className: cn("flex h-6 w-6 shrink-0 items-center justify-center rounded-full", "bg-[color-mix(in_oklch,var(--color-primary-400)_22%,transparent)]", "transition-all duration-300 in-aria-expanded:rotate-180 in-aria-expanded:bg-white/30", className),
	children: /* @__PURE__ */ jsx(ChevronDown, {
		className: "h-[62.5%] w-[62.5%]",
		strokeWidth: 2.4
	})
});
MenuChevron.displayName = "Menu.Chevron";
/**
* Display name for the user-menu identity header. Serif, mid-weight,
* `--text` color, ellipsised when it overflows the meta column.
*
* @beta API may evolve before release.
*/
var MenuHeaderName = ({ children, className }) => /* @__PURE__ */ jsx("h4", {
	className: cn("font-serif font-semibold text-[16px] leading-tight tracking-tight", "text-(--text) m-0 truncate", className),
	children
});
MenuHeaderName.displayName = "Menu.Header.Name";
/**
* Secondary line in the identity header — typically the user's email.
* Sans, small, muted, ellipsised.
*
* @beta API may evolve before release.
*/
var MenuHeaderEmail = ({ children, className }) => /* @__PURE__ */ jsx("p", {
	className: cn("text-[12.5px] text-(--text-muted) m-0 truncate mt-0.5", className),
	children
});
MenuHeaderEmail.displayName = "Menu.Header.Email";
/**
* Optional role / permission chip beneath the name + email — an outline
* accent badge in caps. Use for short labels like "Admin", "Owner", or
* org-role descriptors.
*
* @beta API may evolve before release.
*/
var MenuHeaderRole = ({ children, className }) => /* @__PURE__ */ jsx("span", {
	className: cn("self-start mt-2 inline-flex items-center px-2.5 py-0.5 rounded-full", "text-[10px] uppercase tracking-[0.06em] font-bold", "text-(--accent) border border-(--accent)", className),
	children
});
MenuHeaderRole.displayName = "Menu.Header.Role";
var META_SLOTS = /* @__PURE__ */ new Set([
	MenuHeaderName,
	MenuHeaderEmail,
	MenuHeaderRole
]);
/**
* Identity block for the top of a user-menu dropdown. Composes a
* leading node (typically `<Avatar>`) with the meta slots
* `Menu.Header.Name`, `Menu.Header.Email`, and `Menu.Header.Role`.
*
* The component walks its children and groups the meta slots into a
* flex column to the right of any non-meta children, so consumers
* write the natural reading order without wrapping the meta column
* themselves.
*
* @beta API may evolve before release.
*
* @example
* ```tsx
* <Menu.Header>
*   <Avatar name="Leo Losen" size="lg" />
*   <Menu.Header.Name>Leo Losen</Menu.Header.Name>
*   <Menu.Header.Email>leo@losen.com</Menu.Header.Email>
*   <Menu.Header.Role>Admin</Menu.Header.Role>
* </Menu.Header>
* ```
*/
var MenuHeader = ({ children, className }) => {
	const childArray = Children.toArray(children);
	const meta = [];
	const lead = [];
	for (const child of childArray) if (isValidElement(child) && META_SLOTS.has(child.type)) meta.push(child);
	else lead.push(child);
	return /* @__PURE__ */ jsxs("div", {
		className: cn("flex items-center gap-3.5 px-4 py-4", "border-b border-border-1", className),
		children: [lead, meta.length > 0 && /* @__PURE__ */ jsx("div", {
			className: "flex flex-col min-w-0 flex-1",
			children: meta
		})]
	});
};
MenuHeader.displayName = "Menu.Header";
var Menu_default = Object.assign(Menu$1, {
	Trigger: MenuTrigger$1,
	Chevron: MenuChevron,
	Link: MenuLink,
	Button: MenuButton,
	Section: MenuSection$1,
	Option: MenuOption,
	Separator: MenuSeparator,
	ThemeToggle: MenuThemeToggle,
	Header: Object.assign(MenuHeader, {
		Name: MenuHeaderName,
		Email: MenuHeaderEmail,
		Role: MenuHeaderRole
	})
});
//#endregion
export { Menu_default as default };

//# sourceMappingURL=Menu.js.map