UNPKG

@fluentui/react-northstar

Version:
415 lines (410 loc) 15.6 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import _some from "lodash/some"; import _invoke from "lodash/invoke"; import { toolbarMenuItemBehavior, indicatorBehavior } from '@fluentui/accessibility'; import * as React from 'react'; import cx from 'classnames'; import * as PropTypes from 'prop-types'; import { EventListener } from '@fluentui/react-component-event-listener'; import { Ref } from '@fluentui/react-component-ref'; import * as customPropTypes from '@fluentui/react-proptypes'; import { compose, focusAsync, mergeVariablesOverrides, useTelemetry, useStyles, useAutoControlled, useFluentContext, getElementType, useUnhandledProps, useAccessibility, useContextSelectors } from '@fluentui/react-bindings'; import { Unstable_NestingAuto } from '@fluentui/react-component-nesting-registry'; import { createShorthand, commonPropTypes, childrenExist, doesNodeContainClick } from '../../utils'; import { partitionPopperPropsFromShorthand, Popper } from '../../utils/positioner'; import { Box } from '../Box/Box'; import { Popup } from '../Popup/Popup'; import { ToolbarMenu } from './ToolbarMenu'; import { ToolbarMenuItemIcon } from './ToolbarMenuItemIcon'; import { ToolbarVariablesContext, ToolbarVariablesProvider } from './toolbarVariablesContext'; import { ToolbarMenuItemSubmenuIndicator } from './ToolbarMenuItemSubmenuIndicator'; import { ToolbarMenuItemActiveIndicator } from './ToolbarMenuItemActiveIndicator'; import { ToolbarMenuContext } from './toolbarMenuContext'; import { ToolbarMenuItemContent } from './ToolbarMenuItemContent'; import { ChevronEndIcon } from '@fluentui/react-icons-northstar'; export var toolbarMenuItemClassName = 'ui-toolbar__menuitem'; export var toolbarMenuItemSlotClassNames = { wrapper: toolbarMenuItemClassName + "__wrapper", submenu: toolbarMenuItemClassName + "__submenu" }; /** * A ToolbarMenuItem renders ToolbarMenu item as button. */ export var ToolbarMenuItem = /*#__PURE__*/function () { var ToolbarMenuItem = compose(function (props, ref, composeOptions) { var context = useFluentContext(); var _useTelemetry = useTelemetry(composeOptions.displayName, context.telemetry), setStart = _useTelemetry.setStart, setEnd = _useTelemetry.setEnd; setStart(); var active = props.active, activeIndicator = props.activeIndicator, children = props.children, content = props.content, disabled = props.disabled, disabledFocusable = props.disabledFocusable, submenuIndicator = props.submenuIndicator, icon = props.icon, popup = props.popup, wrapper = props.wrapper, inSubmenu = props.inSubmenu, className = props.className, design = props.design, styles = props.styles, variables = props.variables; var _partitionPopperProps = partitionPopperPropsFromShorthand(props.menu), menu = _partitionPopperProps[0], menuPositioningProps = _partitionPopperProps[1]; var _useAutoControlled = useAutoControlled({ defaultValue: props.defaultMenuOpen, value: props.menuOpen, initialValue: false }), menuOpen = _useAutoControlled[0], setMenuOpen = _useAutoControlled[1]; var itemRef = React.useRef(); var menuRef = React.useRef(); var _ref = useContextSelectors(ToolbarMenuContext, { menuSlot: function menuSlot(v) { return v.slots.menu; } }), menuSlot = _ref.menuSlot; // TODO: we should improve typings for the useContextSelectors var parentVariables = React.useContext(ToolbarVariablesContext); var mergedVariables = mergeVariablesOverrides(parentVariables, variables); var ElementType = getElementType(props); var slotProps = composeOptions.resolveSlotProps(props); var unhandledProps = useUnhandledProps(composeOptions.handledProps, props); var getA11yProps = useAccessibility(props.accessibility, { debugName: composeOptions.displayName, mapPropsToBehavior: function mapPropsToBehavior() { return { hasMenu: !!menu, active: active, menuOpen: menuOpen, disabled: disabled, disabledFocusable: disabledFocusable, 'aria-label': props['aria-label'], 'aria-labelledby': props['aria-labelledby'], 'aria-describedby': props['aria-describedby'] }; }, actionHandlers: { performClick: function performClick(event) { event.preventDefault(); handleClick(event); }, openMenu: function (_openMenu) { function openMenu(_x) { return _openMenu.apply(this, arguments); } openMenu.toString = function () { return _openMenu.toString(); }; return openMenu; }(function (event) { return openMenu(event); }), closeAllMenusAndFocusNextParentItem: function closeAllMenusAndFocusNextParentItem(event) { return closeAllMenus(event); }, closeMenu: function (_closeMenu) { function closeMenu(_x2) { return _closeMenu.apply(this, arguments); } closeMenu.toString = function () { return _closeMenu.toString(); }; return closeMenu; }(function (event) { return closeMenu(event); }), closeMenuAndFocusTrigger: function closeMenuAndFocusTrigger(event) { return closeMenu(event); }, doNotNavigateNextParentItem: function doNotNavigateNextParentItem(event) { event.stopPropagation(); }, closeAllMenus: function (_closeAllMenus) { function closeAllMenus(_x3) { return _closeAllMenus.apply(this, arguments); } closeAllMenus.toString = function () { return _closeAllMenus.toString(); }; return closeAllMenus; }(function (event) { return closeAllMenus(event); }) }, rtl: context.rtl }); var _useStyles = useStyles(composeOptions.displayName, { className: composeOptions.className, composeOptions: composeOptions, mapPropsToStyles: function mapPropsToStyles() { return { disabled: disabled, disabledFocusable: disabledFocusable, hasContent: !!content }; }, mapPropsToInlineStyles: function mapPropsToInlineStyles() { return { className: className, design: design, styles: styles, variables: mergedVariables }; }, rtl: context.rtl, unstable_props: props }), classes = _useStyles.classes, resolvedStyles = _useStyles.styles; var openMenu = function openMenu(e) { if (menu && !menuOpen) { trySetMenuOpen(true, e); e.stopPropagation(); e.preventDefault(); } }; var closeMenu = function closeMenu(e) { if (!isSubmenuOpen()) { return; } trySetMenuOpen(false, e, function () { focusAsync(itemRef.current); }); e.stopPropagation(); }; var closeAllMenus = function closeAllMenus(e) { if (!isSubmenuOpen()) { return; } trySetMenuOpen(false, e, function () { if (!inSubmenu) { focusAsync(itemRef.current); } }); // avoid spacebar scrolling the page if (!inSubmenu) { e.preventDefault(); } }; var isSubmenuOpen = function isSubmenuOpen() { return !!(menu && menuOpen); }; 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(); _invoke(props, 'onMenuOpenChange', e, Object.assign({}, props, { menuOpen: newValue })); }; var outsideClickHandler = function outsideClickHandler(getRefs) { return function (e) { var isItemClick = doesNodeContainClick(itemRef.current, e, context.target); var isNestedClick = _some(getRefs(), function (childRef) { return doesNodeContainClick(childRef.current, e, context.target); }); var isInside = isItemClick || isNestedClick; if (!isInside) { trySetMenuOpen(false, e); } }; }; var handleMenuOverrides = function handleMenuOverrides(predefinedProps) { return { onItemClick: function onItemClick(e, itemProps) { var popup = itemProps.popup, menuOpen = itemProps.menuOpen; _invoke(predefinedProps, 'onItemClick', e, itemProps); if (popup) { return; } trySetMenuOpen(menuOpen, e); if (!menuOpen) { _invoke(itemRef.current, 'focus'); } } }; }; var handleClick = function handleClick(e) { if (disabled) { e.preventDefault(); return; } if (menu) { // the menuItem element was clicked => toggle the open/close and stop propagation trySetMenuOpen(!menuOpen, e); e.stopPropagation(); e.preventDefault(); } if (popup) { e.stopPropagation(); e.preventDefault(); return; } _invoke(props, 'onClick', e, props); }; var element = /*#__PURE__*/React.createElement(ElementType, getA11yProps('root', Object.assign({ className: classes.root, onClick: handleClick, disabled: disabled, ref: ref }, unhandledProps)), childrenExist(children) ? children : /*#__PURE__*/React.createElement(React.Fragment, null, createShorthand(composeOptions.slots.icon, icon, { defaultProps: function defaultProps() { return slotProps.icon; } }), createShorthand(composeOptions.slots.content, content, { defaultProps: function defaultProps() { return getA11yProps('content', slotProps.content); } }), active && createShorthand(composeOptions.slots.activeIndicator, activeIndicator, { defaultProps: function defaultProps() { return slotProps.activeIndicator; } }), menu && createShorthand(composeOptions.slots.submenuIndicator, submenuIndicator, { defaultProps: function defaultProps() { return slotProps.submenuIndicator; } }))); var hasChildren = childrenExist(children); if (popup && !hasChildren) { var popupElement = createShorthand(composeOptions.slots.popup, popup, { defaultProps: function defaultProps() { return Object.assign({}, slotProps.popup, { onOpenChange: function onOpenChange(e) { e.stopPropagation(); } }); }, overrideProps: { trigger: element, children: undefined // force-reset `children` defined for `Popup` as it collides with the `trigger` } }); setEnd(); return popupElement; } var menuItemInner = hasChildren ? children : /*#__PURE__*/React.createElement(Ref, { innerRef: itemRef }, element); var maybeSubmenu = menu && menuOpen ? /*#__PURE__*/React.createElement(Unstable_NestingAuto, null, function (getRefs, nestingRef) { return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Ref, { innerRef: function innerRef(node) { nestingRef.current = node; menuRef.current = node; } }, /*#__PURE__*/React.createElement(Popper, _extends({ align: "top", position: context.rtl ? 'before' : 'after', targetRef: itemRef }, menuPositioningProps), /*#__PURE__*/React.createElement(ToolbarVariablesProvider, { value: mergedVariables }, createShorthand(composeOptions.slots.menu || menuSlot || ToolbarMenu, menu, { defaultProps: function defaultProps() { return Object.assign({ className: toolbarMenuItemSlotClassNames.submenu, styles: resolvedStyles.menu, submenu: true, submenuIndicator: submenuIndicator }, slotProps.menu); }, overrideProps: handleMenuOverrides })))), /*#__PURE__*/React.createElement(EventListener, { capture: true, listener: outsideClickHandler(getRefs), target: context.target, type: "click" })); }) : null; if (!wrapper) { setEnd(); return menuItemInner; } var wrapperElement = Box.create(wrapper, { defaultProps: function defaultProps() { return getA11yProps('wrapper', { className: cx(toolbarMenuItemSlotClassNames.wrapper, classes.wrapper) }); }, overrideProps: function overrideProps() { return { children: /*#__PURE__*/React.createElement(React.Fragment, null, menuItemInner, maybeSubmenu) }; } }); setEnd(); return wrapperElement; }, { className: toolbarMenuItemClassName, displayName: 'ToolbarMenuItem', slots: { icon: ToolbarMenuItemIcon, submenuIndicator: ToolbarMenuItemSubmenuIndicator, activeIndicator: ToolbarMenuItemActiveIndicator, popup: Popup, content: ToolbarMenuItemContent }, slotProps: function slotProps(props) { return { icon: { hasContent: !!props.content }, submenuIndicator: { accessibility: indicatorBehavior }, activeIndicator: { accessibility: indicatorBehavior }, popup: { trapFocus: true }, content: {} }; }, shorthandConfig: { mappedProp: 'content' }, handledProps: ['accessibility', 'as', 'children', 'className', 'content', 'design', 'styles', 'variables', 'disabledFocusable', 'active', 'activeIndicator', 'defaultMenuOpen', 'disabled', 'icon', 'index', 'submenuIndicator', 'inSubmenu', 'menu', 'menuOpen', 'onClick', 'onMenuOpenChange', 'popup', 'wrapper'] }); ToolbarMenuItem.propTypes = Object.assign({}, commonPropTypes.createCommon(), { active: PropTypes.bool, activeIndicator: customPropTypes.shorthandAllowingChildren, defaultMenuOpen: PropTypes.bool, disabled: PropTypes.bool, disabledFocusable: PropTypes.bool, icon: customPropTypes.shorthandAllowingChildren, index: PropTypes.number, submenuIndicator: customPropTypes.shorthandAllowingChildren, inSubmenu: PropTypes.bool, menu: PropTypes.oneOfType([customPropTypes.itemShorthand, customPropTypes.collectionShorthand]), menuOpen: PropTypes.bool, onClick: PropTypes.func, onMenuOpenChange: PropTypes.func, popup: PropTypes.oneOfType([PropTypes.shape(Object.assign({}, Popup.propTypes, { trigger: customPropTypes.never, children: customPropTypes.never })), PropTypes.string]), wrapper: customPropTypes.itemShorthand }); ToolbarMenuItem.defaultProps = { as: 'button', accessibility: toolbarMenuItemBehavior, activeIndicator: {}, submenuIndicator: /*#__PURE__*/React.createElement(ChevronEndIcon, { outline: true }), wrapper: { as: 'li' } }; return ToolbarMenuItem; }(); //# sourceMappingURL=ToolbarMenuItem.js.map