@yamada-ui/react
Version:
React UI components of the Yamada, by the Yamada, for the Yamada built with React and Emotion
487 lines (483 loc) • 16.1 kB
JavaScript
"use client";
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
const require_context = require('../../utils/context.cjs');
const require_dom = require('../../utils/dom.cjs');
const require_effect = require('../../utils/effect.cjs');
const require_ref = require('../../utils/ref.cjs');
const require_utils_index = require('../../utils/index.cjs');
const require_hooks_use_controllable_state_index = require('../../hooks/use-controllable-state/index.cjs');
const require_hooks_use_descendants_index = require('../../hooks/use-descendants/index.cjs');
const require_use_disclosure = require('../../hooks/use-disclosure/use-disclosure.cjs');
let react = require("react");
react = require_rolldown_runtime.__toESM(react);
//#region src/components/menu/use-menu.ts
const { DescendantsContext: MenuDescendantsContext, useDescendant: useMenuDescendant, useDescendantRegister: useMenuDescendantRegister, useDescendants: useMenuDescendants } = require_hooks_use_descendants_index.createDescendants();
const [MenuContext, useMenuContext] = require_context.createContext({ name: "MenuContext" });
const [MenuGroupContext, useMenuGroupContext] = require_context.createContext({ name: "MenuGroupContext" });
const [MainMenuContext, useMainMenuContext] = require_context.createContext({
name: "MainMenuContext",
strict: false
});
const [MenuOptionGroupContext, useMenuOptionGroupContext] = require_context.createContext({ name: "MenuOptionGroupContext" });
const useMenu = ({ closeOnSelect, defaultOpen, disabled = false, open: openProp, subMenuDirection = "end", onClose: onCloseProp, onOpen: onOpenProp, onSelect: onSelectProp } = {}) => {
const triggerId = (0, react.useId)();
const contentId = (0, react.useId)();
const descendants = useMenuDescendants();
const updateRef = (0, react.useRef)(require_utils_index.utils_exports.noop);
const onCloseRef = (0, react.useRef)(require_utils_index.utils_exports.noop);
const contentRef = (0, react.useRef)(null);
const { open, onClose, onOpen } = require_use_disclosure.useDisclosure({
defaultOpen,
open: openProp,
onClose: onCloseProp,
onOpen: onOpenProp
});
const onCloseSubMenu = (0, react.useCallback)(() => onCloseRef.current(), []);
const onActiveDescendant = (0, react.useCallback)((descendant, options = { preventScroll: true }) => {
if (!contentRef.current || !descendant || disabled) return;
contentRef.current.setAttribute("aria-activedescendant", descendant.id);
descendants.active(descendant, options);
}, [descendants, disabled]);
const { mainCloseOnSelect, subMenu, getSubMenuProps, onMainSelect } = useSubMenu({
descendants,
disabled,
open,
subMenuDirection,
onActiveDescendant,
onClose,
onOpen
});
closeOnSelect ??= mainCloseOnSelect ?? true;
const onSelect = (0, react.useCallback)((value, closeOnSelectProp = closeOnSelect) => {
if (disabled) return;
onSelectProp?.(value);
onMainSelect?.(value, closeOnSelectProp);
if (!closeOnSelectProp) return;
onClose();
}, [
closeOnSelect,
disabled,
onClose,
onMainSelect,
onSelectProp
]);
const onClick = (0, react.useCallback)((ev) => {
if (disabled) return;
ev.preventDefault();
if (!open) onOpen();
else onClose();
}, [
disabled,
onClose,
onOpen,
open
]);
const onContextMenu = (0, react.useCallback)((ev) => {
if (disabled) return;
ev.preventDefault();
onOpen();
updateRef.current();
}, [disabled, onOpen]);
const onKeyDown = (0, react.useCallback)((ev) => {
if (disabled) return;
require_dom.runKeyAction(ev, {
ArrowDown: () => {
onOpen();
setTimeout(() => {
onActiveDescendant(descendants.enabledFirstValue());
});
},
ArrowUp: () => {
onOpen();
setTimeout(() => {
onActiveDescendant(descendants.enabledLastValue());
});
},
Enter: () => {
onOpen();
setTimeout(() => {
onActiveDescendant(descendants.enabledFirstValue());
});
},
Space: () => {
onOpen();
setTimeout(() => {
onActiveDescendant(descendants.enabledFirstValue());
});
}
});
}, [
descendants,
disabled,
onActiveDescendant,
onOpen
]);
const getTriggerProps = (0, react.useCallback)((props = {}) => ({ ...getSubMenuProps({
id: triggerId,
"aria-controls": open ? contentId : void 0,
"aria-disabled": (0, require_utils_index.utils_exports.ariaAttr)(disabled),
"aria-expanded": open,
"aria-haspopup": "menu",
"data-trigger": (0, require_utils_index.utils_exports.dataAttr)(true),
role: "button",
tabIndex: disabled ? -1 : 0,
...props,
onClick: (0, require_utils_index.utils_exports.handlerAll)(props.onClick, onClick),
onKeyDown: (0, require_utils_index.utils_exports.handlerAll)(props.onKeyDown, onKeyDown)
}) }), [
contentId,
disabled,
getSubMenuProps,
onClick,
onKeyDown,
open,
triggerId
]);
const getContextTriggerProps = (0, react.useCallback)((props = {}) => ({
id: triggerId,
"aria-controls": open ? contentId : void 0,
"aria-disabled": (0, require_utils_index.utils_exports.ariaAttr)(disabled),
"aria-expanded": open,
"aria-haspopup": "menu",
"data-trigger": (0, require_utils_index.utils_exports.dataAttr)(true),
role: "application",
...props,
onContextMenu: (0, require_utils_index.utils_exports.handlerAll)(props.onContextMenu, onContextMenu)
}), [
contentId,
disabled,
onContextMenu,
open,
triggerId
]);
const getContentProps = (0, react.useCallback)(({ ref, "aria-labelledby": ariaLabelledby,...props } = {}) => ({
id: contentId,
"aria-labelledby": (0, require_utils_index.utils_exports.cx)(ariaLabelledby, triggerId),
role: "menu",
...props,
ref: require_ref.mergeRefs(ref, contentRef)
}), [contentId, triggerId]);
const getSeparatorProps = (0, react.useCallback)((props) => ({
role: "separator",
...props
}), []);
return {
closeOnSelect,
descendants,
open,
subMenu,
subMenuDirection,
updateRef,
getContentProps,
getContextTriggerProps,
getSeparatorProps,
getTriggerProps,
onActiveDescendant,
onClose,
onCloseRef,
onCloseSubMenu,
onOpen,
onSelect
};
};
const useSubMenu = ({ descendants, disabled = false, open, subMenuDirection = "end", onActiveDescendant, onClose, onOpen }) => {
const uuid = (0, react.useId)();
const { closeOnSelect: mainCloseOnSelect, descendants: mainDescendants, onActiveDescendant: onActiveMainDescendant, onCloseRef, onSelect: onMainSelect } = useMainMenuContext() ?? {};
const subMenu = !!mainDescendants && !!onActiveMainDescendant;
const createRegister = useMenuDescendantRegister(mainDescendants);
const triggerRef = (0, react.useRef)(null);
const dataDisabled = (0, react.useCallback)((node) => {
node ??= triggerRef.current;
if (!node) return false;
return (0, require_utils_index.utils_exports.isTruthyDataAttr)(node.getAttribute("data-disabled"));
}, []);
const ariaDisabled = (0, react.useCallback)((node) => {
node ??= triggerRef.current;
if (!node) return false;
return (0, require_utils_index.utils_exports.isTruthyDataAttr)(node.getAttribute("aria-disabled"));
}, []);
const onClick = (0, react.useCallback)((ev) => {
if (!subMenu) return;
ev.defaultPrevented = disabled || dataDisabled() || ariaDisabled();
}, [
ariaDisabled,
dataDisabled,
disabled,
subMenu
]);
const onMouseEnter = (0, react.useCallback)(() => {
if (!subMenu || disabled || dataDisabled() || ariaDisabled()) return;
onOpen();
}, [
ariaDisabled,
dataDisabled,
disabled,
onOpen,
subMenu
]);
const onMouseMove = (0, react.useCallback)((ev) => {
if (!subMenu || disabled || dataDisabled() || ariaDisabled()) return;
onActiveMainDescendant(descendants.value(triggerRef.current));
ev.defaultPrevented = true;
}, [
ariaDisabled,
dataDisabled,
descendants,
disabled,
onActiveMainDescendant,
subMenu
]);
const onKeyDown = (0, react.useCallback)((ev) => {
if (!subMenu || disabled) return;
const currentDescendant = open ? descendants : mainDescendants;
const onActiveCurrentDescendant = open ? onActiveDescendant : onActiveMainDescendant;
require_dom.runKeyAction(ev, {
ArrowDown: () => {
onActiveCurrentDescendant(currentDescendant.enabledNextValue(triggerRef.current));
ev.defaultPrevented = true;
},
ArrowUp: () => {
onActiveCurrentDescendant(currentDescendant.enabledPrevValue(triggerRef.current));
ev.defaultPrevented = true;
},
End: () => {
onActiveCurrentDescendant(currentDescendant.enabledLastValue());
ev.defaultPrevented = true;
},
Home: () => {
onActiveCurrentDescendant(currentDescendant.enabledFirstValue());
ev.defaultPrevented = true;
},
[subMenuDirection === "end" ? "ArrowRight" : "ArrowLeft"]: () => {
onOpen();
setTimeout(() => {
onActiveDescendant(descendants.enabledFirstValue());
});
ev.defaultPrevented = true;
}
});
}, [
subMenu,
disabled,
open,
descendants,
mainDescendants,
onActiveDescendant,
onActiveMainDescendant,
subMenuDirection,
onOpen
]);
require_ref.assignRef(onCloseRef, onClose);
return {
mainCloseOnSelect,
subMenu,
getSubMenuProps: (0, react.useCallback)(({ id = uuid, ref,...props } = {}) => {
const getDisabled = (node) => disabled || dataDisabled(node) || ariaDisabled(node);
const register = createRegister({
id,
disabled: getDisabled
});
return {
role: subMenu ? "menuitem" : "button",
...props,
ref: require_ref.mergeRefs(ref, triggerRef, register),
onClick: (0, require_utils_index.utils_exports.handlerAll)(onClick, props.onClick),
onKeyDown: (0, require_utils_index.utils_exports.handlerAll)(onKeyDown, props.onKeyDown),
onMouseEnter: (0, require_utils_index.utils_exports.handlerAll)(onMouseEnter, props.onMouseEnter),
onMouseMove: (0, require_utils_index.utils_exports.handlerAll)(onMouseMove, props.onMouseMove)
};
}, [
uuid,
subMenu,
createRegister,
onClick,
onKeyDown,
onMouseEnter,
onMouseMove,
disabled,
dataDisabled,
ariaDisabled
]),
onMainSelect
};
};
const useMenuGroup = ({ "aria-labelledby": ariaLabelledbyProp,...rest }) => {
const labelId = (0, react.useId)();
return {
getGroupProps: (0, react.useCallback)(({ "aria-labelledby": ariaLabelledby,...props } = {}) => ({
"aria-labelledby": (0, require_utils_index.utils_exports.cx)(ariaLabelledbyProp, ariaLabelledby, labelId),
role: "group",
...rest,
...props
}), [
ariaLabelledbyProp,
labelId,
rest
]),
getLabelProps: (0, react.useCallback)((props) => ({
id: labelId,
role: "presentation",
...props
}), [labelId])
};
};
const useMenuItem = ({ id, "aria-disabled": ariaDisabled, "data-disabled": dataDisabled, "data-trigger": dataTrigger, closeOnSelect, disabled = false, value,...rest }) => {
const trigger = (0, require_utils_index.utils_exports.isTruthyDataAttr)(dataTrigger);
const { subMenu, subMenuDirection, onActiveDescendant, onClose, onCloseSubMenu, onSelect } = useMenuContext();
const uuid = (0, react.useId)();
const itemRef = (0, react.useRef)(null);
const subMenuTrigger = subMenu && trigger;
id ??= uuid;
const { descendants, register } = useMenuDescendant({
id,
disabled: disabled || subMenuTrigger
});
const onActive = (0, react.useCallback)(() => {
if (disabled) return;
onActiveDescendant(descendants.value(itemRef.current));
}, [
descendants,
disabled,
onActiveDescendant
]);
const onKeyDown = (0, react.useCallback)((ev) => {
require_dom.runKeyAction(ev, {
ArrowDown: () => {
onActiveDescendant(descendants.enabledNextValue(itemRef.current));
},
ArrowUp: () => {
onActiveDescendant(descendants.enabledPrevValue(itemRef.current));
},
End: () => {
onActiveDescendant(descendants.enabledLastValue());
},
Enter: () => onSelect(value, closeOnSelect),
Home: () => {
onActiveDescendant(descendants.enabledFirstValue());
},
Space: () => onSelect(value, closeOnSelect),
[subMenuDirection === "end" ? "ArrowLeft" : "ArrowRight"]: () => {
if (!subMenu) return;
onClose();
descendants.firstValue()?.node.focus();
}
});
}, [
closeOnSelect,
descendants,
onActiveDescendant,
onClose,
onSelect,
subMenu,
subMenuDirection,
value
]);
return {
subMenuTrigger,
getItemProps: (0, react.useCallback)(({ ref,...props } = {}) => ({
id,
"aria-disabled": ariaDisabled ?? (0, require_utils_index.utils_exports.ariaAttr)(disabled),
"data-disabled": dataDisabled ?? (0, require_utils_index.utils_exports.dataAttr)(disabled),
role: "menuitem",
tabIndex: -1,
...rest,
...props,
ref: require_ref.mergeRefs(ref, rest.ref, itemRef, register),
onClick: (0, require_utils_index.utils_exports.handlerAll)(props.onClick, rest.onClick, () => onSelect(value, closeOnSelect)),
onFocus: (0, require_utils_index.utils_exports.handlerAll)(props.onFocus, rest.onFocus, onActive),
onKeyDown: (0, require_utils_index.utils_exports.handlerAll)(props.onKeyDown, rest.onKeyDown, onKeyDown),
onMouseMove: (0, require_utils_index.utils_exports.handlerAll)(props.onMouseMove, rest.onMouseMove, () => {
onCloseSubMenu();
onActive();
})
}), [
id,
ariaDisabled,
disabled,
dataDisabled,
rest,
register,
onActive,
onKeyDown,
onSelect,
value,
closeOnSelect,
onCloseSubMenu
])
};
};
const useMenuOptionGroup = ({ type = "checkbox", defaultValue = type === "checkbox" ? [] : "", value: valueProp, onChange: onChangeProp }) => {
const [value, setValue] = require_hooks_use_controllable_state_index.useControllableState({
defaultValue,
value: valueProp,
onChange: onChangeProp
});
const radio = type === "radio";
const onChange = (0, react.useCallback)((selectedValue) => {
setValue((prev) => {
if (radio && (0, require_utils_index.utils_exports.isString)(prev)) return selectedValue;
else if (!radio && (0, require_utils_index.utils_exports.isArray)(prev)) return prev.includes(selectedValue) ? prev.filter((value$1) => value$1 !== selectedValue) : prev.concat(selectedValue);
else return prev;
});
}, [radio, setValue]);
require_effect.useUpdateEffect(() => {
setValue(valueProp);
}, [valueProp]);
return {
type,
value,
onChange
};
};
const useMenuOptionItem = ({ disabled, value,...rest }) => {
const { type, value: selectedValue, onChange } = useMenuOptionGroupContext();
const { getItemProps } = useMenuItem({
disabled,
value,
...rest
});
const radio = type === "radio" && (0, require_utils_index.utils_exports.isString)(selectedValue);
const checkbox = type === "checkbox" && (0, require_utils_index.utils_exports.isArray)(selectedValue);
const selected = radio ? value === selectedValue : checkbox ? selectedValue.includes(value) : false;
return {
type,
selected,
getIndicatorProps: (0, react.useCallback)(({ style,...props } = {}) => ({
style: {
opacity: selected ? 1 : 0,
...style
},
...props
}), [selected]),
getOptionItemProps: (0, react.useCallback)((props = {}) => getItemProps({
role: radio ? "menuitemradio" : "menuitemcheckbox",
...props,
onClick: (0, require_utils_index.utils_exports.handlerAll)(props.onClick, () => !disabled ? onChange?.(value) : require_utils_index.utils_exports.noop)
}), [
disabled,
getItemProps,
onChange,
radio,
value
])
};
};
//#endregion
exports.MainMenuContext = MainMenuContext;
exports.MenuContext = MenuContext;
exports.MenuDescendantsContext = MenuDescendantsContext;
exports.MenuGroupContext = MenuGroupContext;
exports.MenuOptionGroupContext = MenuOptionGroupContext;
exports.useMainMenuContext = useMainMenuContext;
exports.useMenu = useMenu;
exports.useMenuContext = useMenuContext;
exports.useMenuDescendant = useMenuDescendant;
exports.useMenuDescendants = useMenuDescendants;
exports.useMenuGroup = useMenuGroup;
exports.useMenuGroupContext = useMenuGroupContext;
exports.useMenuItem = useMenuItem;
exports.useMenuOptionGroup = useMenuOptionGroup;
exports.useMenuOptionGroupContext = useMenuOptionGroupContext;
exports.useMenuOptionItem = useMenuOptionItem;
exports.useSubMenu = useSubMenu;
//# sourceMappingURL=use-menu.cjs.map