UNPKG

@fluentui/react-northstar

Version:
473 lines (467 loc) 18.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.menuItemSlotClassNames = exports.menuItemClassName = exports.MenuItem = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _invoke2 = _interopRequireDefault(require("lodash/invoke")); var _accessibility = require("@fluentui/accessibility"); var _reactComponentEventListener = require("@fluentui/react-component-event-listener"); var _reactBindings = require("@fluentui/react-bindings"); var _reactComponentRef = require("@fluentui/react-component-ref"); var customPropTypes = _interopRequireWildcard(require("@fluentui/react-proptypes")); var PropTypes = _interopRequireWildcard(require("prop-types")); var React = _interopRequireWildcard(require("react")); var _utils = require("../../utils"); var _Menu = require("./Menu"); var _MenuItemIcon = require("./MenuItemIcon"); var _MenuItemContent = require("./MenuItemContent"); var _MenuItemIndicator = require("./MenuItemIndicator"); var _MenuItemWrapper = require("./MenuItemWrapper"); var _positioner = require("../../utils/positioner"); var _menuContext = require("./menuContext"); var _reactIconsNorthstar = require("@fluentui/react-icons-northstar"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } var menuItemClassName = 'ui-menu__item'; exports.menuItemClassName = menuItemClassName; var menuItemSlotClassNames = { submenu: menuItemClassName + "__submenu" }; /** * A MenuItem is an actionable item within a Menu. */ exports.menuItemSlotClassNames = menuItemSlotClassNames; var MenuItem = /*#__PURE__*/React.forwardRef(function (inputProps, ref) { var context = (0, _reactBindings.useFluentContext)(); var _useTelemetry = (0, _reactBindings.useTelemetry)(MenuItem.displayName, context.telemetry), setStart = _useTelemetry.setStart, setEnd = _useTelemetry.setEnd; setStart(); var parentProps = (0, _reactBindings.useContextSelectors)(_menuContext.MenuContext, { active: function active(v) { return v.activeIndex === inputProps.index; }, onItemClick: function onItemClick(v) { return v.onItemClick; }, onItemSelect: function onItemSelect(v) { return v.onItemSelect; }, variables: function variables(v) { return v.variables; }, slotProps: function slotProps(v) { return v.slotProps.item; }, accessibility: function accessibility(v) { return v.behaviors.item; } }); // TODO: we should improve typings for the useContextSelectors var props = Object.assign({}, parentProps.slotProps, { active: parentProps.active, variables: parentProps.variables, accessibility: parentProps.accessibility }, inputProps); var _props$accessibility = props.accessibility, accessibility = _props$accessibility === void 0 ? _accessibility.menuItemBehavior : _props$accessibility, children = props.children, content = props.content, icon = props.icon, wrapper = props.wrapper, primary = props.primary, secondary = props.secondary, active = props.active, vertical = props.vertical, indicator = props.indicator, disabled = props.disabled, underlined = props.underlined, iconOnly = props.iconOnly, inSubmenu = props.inSubmenu, pills = props.pills, pointing = props.pointing, className = props.className, design = props.design, styles = props.styles, variables = props.variables, on = props.on, index = props.index; var _partitionPopperProps = (0, _positioner.partitionPopperPropsFromShorthand)(props.menu), menu = _partitionPopperProps[0], positioningProps = _partitionPopperProps[1]; var _useAutoControlled = (0, _reactBindings.useAutoControlled)({ defaultValue: props.defaultMenuOpen, value: props.menuOpen, initialValue: false }), menuOpen = _useAutoControlled[0], setMenuOpen = _useAutoControlled[1]; (0, _reactBindings.useOnIFrameFocus)(menuOpen, context.target, function (e) { setMenuOpen(function (__) { (0, _invoke2.default)(props, 'onMenuOpenChange', e, Object.assign({}, props, { menuOpen: false })); return false; }); }); var _React$useState = React.useState(false), isFromKeyboard = _React$useState[0], setIsFromKeyboard = _React$useState[1]; var ElementType = (0, _reactBindings.getElementType)(props); var unhandledProps = (0, _reactBindings.useUnhandledProps)(MenuItem.handledProps, props); var getA11yProps = (0, _reactBindings.useAccessibility)(accessibility, { debugName: _Menu.Menu.displayName, actionHandlers: { performClick: function performClick(event) { return !event.defaultPrevented && handleClick(event); }, openMenu: function openMenu(event) { return _openMenu(event); }, closeAllMenusAndFocusNextParentItem: function closeAllMenusAndFocusNextParentItem(event) { return _closeAllMenus(event); }, closeMenu: function closeMenu(event) { return _closeMenu(event); }, closeMenuAndFocusTrigger: function closeMenuAndFocusTrigger(event) { return _closeMenu(event, true); }, doNotNavigateNextParentItem: function doNotNavigateNextParentItem(event) { event.stopPropagation(); }, closeAllMenus: function closeAllMenus(event) { return _closeAllMenus(event); } }, mapPropsToBehavior: function mapPropsToBehavior() { return { menuOpen: menuOpen, hasMenu: !!menu, disabled: disabled, vertical: vertical, active: active // for tabBehavior }; }, rtl: context.rtl }); var _useStyles = (0, _reactBindings.useStyles)(MenuItem.displayName, { className: menuItemClassName, mapPropsToStyles: function mapPropsToStyles() { return { primary: primary, underlined: underlined, active: active, vertical: vertical, pointing: pointing, secondary: secondary, disabled: disabled, iconOnly: iconOnly, pills: pills, inSubmenu: inSubmenu, isFromKeyboard: isFromKeyboard }; }, mapPropsToInlineStyles: function mapPropsToInlineStyles() { return { className: className, design: design, styles: styles, variables: (0, _reactBindings.mergeVariablesOverrides)(parentProps.variables, variables) }; }, rtl: context.rtl }), classes = _useStyles.classes, resolvedStyles = _useStyles.styles; var menuRef = React.useRef(); var itemRef = React.useRef(); var handleWrapperBlur = function handleWrapperBlur(e) { if (!props.inSubmenu && !e.currentTarget.contains(e.relatedTarget)) { trySetMenuOpen(false, e); } }; var dismissOnScroll = function dismissOnScroll(e) { if (!isSubmenuOpen()) return; // we only need to dismiss if the scroll happens outside the menu if (!menuRef.current.contains(e.target)) { trySetMenuOpen(false, e); } }; var outsideClickHandler = function outsideClickHandler(e) { if (!isSubmenuOpen()) return; if (!(0, _utils.doesNodeContainClick)(itemRef.current, e, context.target) && !(0, _utils.doesNodeContainClick)(menuRef.current, e, context.target)) { trySetMenuOpen(false, e); } }; var performClick = function performClick(e) { if (menu) { if ((0, _utils.doesNodeContainClick)(menuRef.current, e, context.target)) { // submenu was clicked => close it and propagate trySetMenuOpen(false, e, function () { return (0, _reactBindings.focusAsync)(itemRef.current); }); } else { // the menuItem element was clicked => toggle the open/close and stop propagation trySetMenuOpen(active && on !== 'hover' ? !menuOpen : true, e); e.stopPropagation(); e.preventDefault(); } } }; var handleClick = function handleClick(e) { if (disabled) { e.preventDefault(); return; } performClick(e); (0, _invoke2.default)(props, 'onClick', e, props); (0, _invoke2.default)(parentProps, 'onItemClick', e, props); }; var handleBlur = function handleBlur(e) { setIsFromKeyboard(false); (0, _invoke2.default)(props, 'onBlur', e, props); }; var handleFocus = function handleFocus(e) { setIsFromKeyboard((0, _utils.isFromKeyboard)()); (0, _invoke2.default)(props, 'onFocus', e, props); }; var isSubmenuOpen = function isSubmenuOpen() { return !!(menu && menuOpen); }; var _closeAllMenus = function _closeAllMenus(e) { if (!isSubmenuOpen()) { return; } trySetMenuOpen(false, e, function () { if (!inSubmenu) { (0, _reactBindings.focusAsync)(itemRef.current); } }); // avoid spacebar scrolling the page if (!inSubmenu) { e.preventDefault(); } }; var _closeMenu = function _closeMenu(e, forceTriggerFocus) { if (!isSubmenuOpen()) { return; } var shouldStopPropagation = inSubmenu || props.vertical; trySetMenuOpen(false, e, function () { if (forceTriggerFocus || shouldStopPropagation) { (0, _reactBindings.focusAsync)(itemRef.current); } }); if (forceTriggerFocus || shouldStopPropagation) { e.stopPropagation(); } }; var _openMenu = function _openMenu(e) { if (menu && !menuOpen) { trySetMenuOpen(true, e); (0, _invoke2.default)(parentProps, 'onItemSelect', e, index); (0, _invoke2.default)(props, 'onActiveChanged', e, Object.assign({}, props, { active: true })); e.stopPropagation(); e.preventDefault(); } }; var rootHandlers = Object.assign({}, !wrapper && Object.assign({ onClick: handleClick }, on === 'hover' && { onMouseEnter: function onMouseEnter(e) { (0, _utils.setWhatInputSource)(context.target, 'mouse'); trySetMenuOpen(true, e); (0, _invoke2.default)(props, 'onMouseEnter', e, props); (0, _invoke2.default)(parentProps, 'onItemSelect', e, index); }, onMouseLeave: function onMouseLeave(e) { trySetMenuOpen(false, e); (0, _invoke2.default)(props, 'onMouseLeave', e, props); } })); var trySetMenuOpen = function trySetMenuOpen(newValue, e, onStateChanged) { setMenuOpen(newValue); // The reason why post-effect is not passed as callback to trySetState method // is that in 'controlled' mode the post-effect is applied before final re-rendering // which cause a broken behavior: for e.g. when it is needed to focus submenu trigger on ESC. // TODO: all DOM post-effects should be applied at componentDidMount & componentDidUpdated stages. onStateChanged && onStateChanged(); (0, _invoke2.default)(props, 'onMenuOpenChange', e, Object.assign({}, props, { menuOpen: newValue })); }; var menuItemInner = /*#__PURE__*/React.createElement(_reactComponentRef.Ref, { innerRef: function innerRef(node) { itemRef.current = node; (0, _reactComponentRef.handleRef)(ref, node); } }, /*#__PURE__*/React.createElement(ElementType, (0, _extends2.default)({}, getA11yProps('root', Object.assign({ className: classes.root, disabled: disabled, onBlur: handleBlur, onFocus: handleFocus, onClick: handleClick }, unhandledProps)), rootHandlers), (0, _utils.childrenExist)(children) ? children : /*#__PURE__*/React.createElement(React.Fragment, null, (0, _utils.createShorthand)(_MenuItemIcon.MenuItemIcon, icon, { defaultProps: function defaultProps() { return getA11yProps('icon', { hasContent: !!content, iconOnly: iconOnly }); } }), (0, _utils.createShorthand)(_MenuItemContent.MenuItemContent, content, { defaultProps: function defaultProps() { return getA11yProps('content', { hasIcon: !!icon, hasMenu: !!menu, inSubmenu: inSubmenu, vertical: vertical }); } }), menu && (0, _utils.createShorthand)(_MenuItemIndicator.MenuItemIndicator, indicator, { defaultProps: function defaultProps() { return getA11yProps('indicator', { iconOnly: iconOnly, vertical: vertical, inSubmenu: inSubmenu, active: active, primary: primary, underlined: underlined }); } })))); var handleWrapperOverrides = function handleWrapperOverrides(predefinedProps) { return Object.assign({ onBlur: function onBlur(e) { handleWrapperBlur(e); (0, _invoke2.default)(predefinedProps, 'onBlur', e, props); } }, on === 'hover' && { onMouseEnter: function onMouseEnter(e) { (0, _utils.setWhatInputSource)(context.target, 'mouse'); trySetMenuOpen(true, e); (0, _invoke2.default)(predefinedProps, 'onMouseEnter', e, props); (0, _invoke2.default)(parentProps, 'onItemSelect', e, index); }, onMouseLeave: function onMouseLeave(e) { trySetMenuOpen(false, e); (0, _invoke2.default)(predefinedProps, 'onMouseLeave', e, props); } }); }; var maybeSubmenu = menu && menuOpen ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_reactComponentRef.Ref, { innerRef: menuRef }, /*#__PURE__*/React.createElement(_positioner.Popper, (0, _extends2.default)({ align: vertical ? 'top' : context.rtl ? 'end' : 'start', position: vertical ? context.rtl ? 'before' : 'after' : 'below', targetRef: itemRef }, positioningProps), (0, _utils.createShorthand)(_Menu.Menu, menu, { defaultProps: function defaultProps() { return { accessibility: _accessibility.submenuBehavior, className: menuItemSlotClassNames.submenu, vertical: true, primary: props.primary, secondary: props.secondary, submenu: true, styles: resolvedStyles.menu, indicator: props.indicator }; }, overrideProps: function overrideProps(predefinedProps) { return { onClick: function onClick(e) { handleClick(e); (0, _invoke2.default)(predefinedProps, 'onClick', e, props); } }; } }))), /*#__PURE__*/React.createElement(_reactComponentEventListener.EventListener, { listener: outsideClickHandler, target: context.target, type: "click" }), /*#__PURE__*/React.createElement(_reactComponentEventListener.EventListener, { listener: dismissOnScroll, target: context.target, type: "wheel", capture: true }), /*#__PURE__*/React.createElement(_reactComponentEventListener.EventListener, { listener: dismissOnScroll, target: context.target, type: "touchmove", capture: true })) : null; if (wrapper) { var wrapperElement = (0, _utils.createShorthand)(_MenuItemWrapper.MenuItemWrapper, wrapper, { defaultProps: function defaultProps() { return getA11yProps('wrapper', { active: active, disabled: disabled, iconOnly: iconOnly, isFromKeyboard: isFromKeyboard, pills: pills, pointing: pointing, secondary: secondary, underlined: underlined, vertical: vertical, primary: primary, on: on, variables: variables }); }, overrideProps: function overrideProps(predefinedProps) { return Object.assign({ children: /*#__PURE__*/React.createElement(React.Fragment, null, menuItemInner, maybeSubmenu) }, handleWrapperOverrides(predefinedProps)); } }); setEnd(); return wrapperElement; } setEnd(); return menuItemInner; }); exports.MenuItem = MenuItem; MenuItem.displayName = 'MenuItem'; MenuItem.propTypes = Object.assign({}, _utils.commonPropTypes.createCommon({ content: 'shorthand' }), { active: PropTypes.bool, disabled: PropTypes.bool, icon: customPropTypes.shorthandAllowingChildren, on: PropTypes.oneOf(['hover']), iconOnly: PropTypes.bool, index: PropTypes.number, itemPosition: PropTypes.number, itemsCount: PropTypes.number, onClick: PropTypes.func, onFocus: PropTypes.func, onBlur: PropTypes.func, pills: PropTypes.bool, pointing: PropTypes.oneOf(['start', 'end', true, false]), primary: customPropTypes.every([customPropTypes.disallow(['secondary']), PropTypes.bool]), secondary: customPropTypes.every([customPropTypes.disallow(['primary']), PropTypes.bool]), underlined: PropTypes.bool, vertical: PropTypes.bool, wrapper: PropTypes.oneOfType([PropTypes.node, PropTypes.object]), menu: PropTypes.oneOfType([customPropTypes.itemShorthand, customPropTypes.collectionShorthand]), menuOpen: PropTypes.bool, defaultMenuOpen: PropTypes.bool, onActiveChanged: PropTypes.func, inSubmenu: PropTypes.bool, indicator: customPropTypes.shorthandAllowingChildren, onMenuOpenChange: PropTypes.func }); MenuItem.handledProps = Object.keys(MenuItem.propTypes); MenuItem.shorthandConfig = { mappedProp: 'content' }; MenuItem.defaultProps = { as: 'a', wrapper: {}, indicator: /*#__PURE__*/React.createElement(_reactIconsNorthstar.ChevronEndIcon, { outline: true }) }; //# sourceMappingURL=MenuItem.js.map