@yamada-ui/menu
Version:
Yamada UI menu component
310 lines (307 loc) • 10.7 kB
JavaScript
"use client"
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/menu-item.tsx
var menu_item_exports = {};
__export(menu_item_exports, {
MenuCommand: () => MenuCommand,
MenuIcon: () => MenuIcon,
MenuItem: () => MenuItem,
MenuOptionItem: () => MenuOptionItem
});
module.exports = __toCommonJS(menu_item_exports);
var import_core = require("@yamada-ui/core");
var import_use_clickable = require("@yamada-ui/use-clickable");
var import_utils2 = require("@yamada-ui/utils");
var import_react = require("react");
// src/menu-context.ts
var import_use_descendant = require("@yamada-ui/use-descendant");
var import_utils = require("@yamada-ui/utils");
var {
DescendantsContextProvider,
useDescendant: useMenuDescendant,
useDescendants,
useDescendantsContext: useMenuDescendantsContext
} = (0, import_use_descendant.createDescendant)();
var [MenuProvider, useMenu] = (0, import_utils.createContext)({
name: "MenuContext",
errorMessage: `useMenu returned is 'undefined'. Seems you forgot to wrap the components in "<Menu />"`
});
var [ContextMenuProvider, useContextMenu] = (0, import_utils.createContext)({
name: "ContextMenuContext",
strict: false
});
var [UpstreamMenuProvider, useUpstreamMenu] = (0, import_utils.createContext)({
name: "UpstreamMenuContext",
strict: false
});
var [UpstreamMenuItemProvider, useUpstreamMenuItem] = (0, import_utils.createContext)({
name: "UpstreamMenuItemContext",
strict: false
});
// src/menu-item.tsx
var import_jsx_runtime = require("react/jsx-runtime");
var isTargetMenuItem = (target) => {
var _a;
return (0, import_utils2.isHTMLElement)(target) && !!((_a = target.getAttribute("role")) == null ? void 0 : _a.startsWith("menuitem"));
};
var MenuItem = (0, import_core.forwardRef)(
({
id,
className,
children,
closeOnSelect: customCloseOnSelect,
command,
isDisabled,
disabled = isDisabled,
isFocusable,
focusable = isFocusable,
icon,
...props
}, ref) => {
var _a, _b;
const {
closeOnSelect,
focusedIndex,
menuRef,
nested,
open,
requestAnimationFrameId,
setFocusedIndex,
styles,
onClose,
onUpstreamClose
} = useMenu();
const { onUpstreamRestoreFocus } = (_a = useUpstreamMenuItem()) != null ? _a : {};
const [downstreamOpen, setDownstreamOpen] = (0, import_react.useState)(false);
const uuid = (0, import_react.useId)();
const itemRef = (0, import_react.useRef)(null);
const hasDownstreamRef = (0, import_react.useRef)(false);
const onKeyDownRef = (0, import_react.useRef)(
() => void 0
);
const trulyDisabled = disabled && !focusable;
const type = (_b = itemRef.current) == null ? void 0 : _b.getAttribute("type");
const role = !!type ? type === "checkbox" ? "menuitemcheckbox" : "menuitemradio" : "menuitem";
const { index, register } = useMenuDescendant({ disabled: trulyDisabled });
const focused = index === focusedIndex;
const css = {
alignItems: "center",
color: "inherit",
display: "flex",
flex: "0 0 auto",
gap: "0.75rem",
outline: 0,
textAlign: "start",
textDecoration: "none",
userSelect: "none",
width: "100%",
...styles.item
};
id != null ? id : id = uuid;
const onMouseOver = (0, import_react.useCallback)(() => {
if (disabled) return;
setFocusedIndex(index);
}, [index, disabled, setFocusedIndex]);
const onMouseLeave = (0, import_react.useCallback)(() => {
if (disabled) return;
setFocusedIndex(-1);
}, [setFocusedIndex, disabled]);
const onClick = (0, import_react.useCallback)(
(ev) => {
if (!isTargetMenuItem(ev.currentTarget)) return;
const hasDownstream = hasDownstreamRef.current;
if (customCloseOnSelect != null ? customCloseOnSelect : !hasDownstream && closeOnSelect) {
onClose();
onUpstreamClose == null ? void 0 : onUpstreamClose();
}
},
[customCloseOnSelect, closeOnSelect, onClose, onUpstreamClose]
);
const onFocus = (0, import_react.useCallback)(() => {
setFocusedIndex(index);
}, [setFocusedIndex, index]);
const onRestoreFocus = (0, import_react.useCallback)(() => {
var _a2;
(_a2 = itemRef.current) == null ? void 0 : _a2.focus();
setFocusedIndex(index);
}, [setFocusedIndex, index]);
const onKeyDown = (0, import_react.useCallback)(
(ev) => {
if (ev.key === " ") ev.key = ev.code;
const actions = {
ArrowLeft: nested ? (0, import_utils2.funcAll)(onUpstreamRestoreFocus, onClose) : void 0,
Space: !hasDownstreamRef.current ? (0, import_utils2.funcAll)(onUpstreamClose, onClose) : void 0
};
const action = actions[ev.key];
if (!action) return;
ev.preventDefault();
ev.stopPropagation();
action();
},
[nested, onUpstreamRestoreFocus, onClose, onUpstreamClose]
);
const rest = (0, import_use_clickable.useClickable)({
clickOnSpace: false,
focusOnClick: false,
...props,
ref: (0, import_utils2.mergeRefs)(register, itemRef, ref),
disabled,
focusable,
onClick: (0, import_utils2.handlerAll)(props.onClick, onClick),
onFocus: (0, import_utils2.handlerAll)(props.onFocus, onFocus),
onKeyDown: (0, import_utils2.handlerAll)(props.onKeyDown, onKeyDown, onKeyDownRef.current),
onMouseLeave: (0, import_utils2.handlerAll)(props.onMouseLeave, onMouseLeave),
onMouseOver: (0, import_utils2.handlerAll)(props.onMouseOver, onMouseOver)
});
(0, import_utils2.useUpdateEffect)(() => {
if (!open) return;
const id2 = requestAnimationFrameId.current;
if (focused && !trulyDisabled && itemRef.current) {
if (id2) cancelAnimationFrame(id2);
requestAnimationFrameId.current = requestAnimationFrame(() => {
var _a2;
(_a2 = itemRef.current) == null ? void 0 : _a2.focus({ preventScroll: true });
requestAnimationFrameId.current = null;
});
} else if (menuRef.current && !(0, import_utils2.isActiveElement)(menuRef.current)) {
menuRef.current.focus({ preventScroll: true });
}
return () => {
if (id2) cancelAnimationFrame(id2);
};
}, [focused, trulyDisabled, menuRef, open]);
children = icon || command ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core.ui.span, { style: { flex: 1 }, children }) : children;
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
UpstreamMenuItemProvider,
{
value: {
hasDownstreamRef,
setDownstreamOpen,
onKeyDownRef,
onUpstreamRestoreFocus: onRestoreFocus
},
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
import_core.ui.div,
{
id,
className: (0, import_utils2.cx)("ui-menu__item", className),
"data-focus": (0, import_utils2.dataAttr)(downstreamOpen),
__css: css,
...rest,
role,
tabIndex: !downstreamOpen && focused ? 0 : -1,
children: [
icon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuIcon, { children: icon }) : null,
children,
command ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuCommand, { children: command }) : null
]
}
)
}
);
}
);
MenuItem.displayName = "MenuItem";
MenuItem.__ui__ = "MenuItem";
var MenuOptionItem = (0, import_core.forwardRef)(
({
className,
isChecked,
checked = isChecked,
children,
closeOnSelect = false,
icon,
...rest
}, ref) => {
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
MenuItem,
{
ref,
className: (0, import_utils2.cx)("ui-menu__item--option", className),
"aria-checked": checked,
closeOnSelect,
...rest,
children: [
icon !== null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuIcon, { opacity: checked ? 1 : 0, children: icon || /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckIcon, {}) }) : null,
children
]
}
);
}
);
MenuOptionItem.displayName = "MenuOptionItem";
MenuOptionItem.__ui__ = "MenuOptionItem";
var MenuIcon = (0, import_core.forwardRef)(
({ className, ...rest }, ref) => {
const { styles } = useMenu();
const css = {
alignItems: "center",
display: "inline-flex",
flexShrink: 0,
fontSize: "0.85em",
justifyContent: "center",
...styles.icon
};
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_core.ui.span,
{
ref,
className: (0, import_utils2.cx)("ui-menu__item__icon", className),
"aria-hidden": true,
__css: css,
...rest
}
);
}
);
MenuIcon.displayName = "MenuIcon";
MenuIcon.__ui__ = "MenuIcon";
var MenuCommand = (0, import_core.forwardRef)(
({ className, ...rest }, ref) => {
const { styles } = useMenu();
const css = { ...styles.command };
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_core.ui.span,
{
ref,
className: (0, import_utils2.cx)("ui-menu__item__command", className),
__css: css,
...rest
}
);
}
);
MenuCommand.displayName = "MenuCommand";
MenuCommand.__ui__ = "MenuCommand";
var CheckIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { height: "1em", viewBox: "0 0 14 14", width: "1em", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"polygon",
{
fill: "currentColor",
points: "5.5 11.9993304 14 3.49933039 12.5 2 5.5 8.99933039 1.5 4.9968652 0 6.49933039"
}
) });
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
MenuCommand,
MenuIcon,
MenuItem,
MenuOptionItem
});
//# sourceMappingURL=menu-item.js.map