@fluentui/react-northstar
Version:
A themable React component library.
473 lines (467 loc) • 18.4 kB
JavaScript
"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