UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

735 lines • 40.4 kB
import * as tslib_1 from "tslib"; import * as React from 'react'; import { ContextualMenuItemType } from './ContextualMenu.types'; import { FocusZone, FocusZoneDirection } from '../../FocusZone'; import { assign, BaseComponent, classNamesFunction, css, getDocument, getFirstFocusable, getId, getLastFocusable, getRTL, getWindow, shouldWrapFocus } from '../../Utilities'; import { hasSubmenu, getIsChecked, isItemDisabled } from '../../utilities/contextualMenu/index'; import { withResponsiveMode, ResponsiveMode } from '../../utilities/decorators/withResponsiveMode'; import { Callout } from '../../Callout'; import { ContextualMenu } from './ContextualMenu'; import { ContextualMenuItem } from './ContextualMenuItem'; import { ContextualMenuSplitButton, ContextualMenuButton, ContextualMenuAnchor } from './ContextualMenuItemWrapper/index'; import { mergeStyleSets } from '../../Styling'; import { getItemClassNames as getItemStyles } from './ContextualMenu.classNames'; var getClassNames = classNamesFunction(); var getContextualMenuItemClassNames = classNamesFunction(); export function getSubmenuItems(item) { return item.subMenuProps ? item.subMenuProps.items : item.items; } /** * Returns true if a list of menu items can contain a checkbox */ export function canAnyMenuItemsCheck(items) { return items.some(function (item) { if (item.canCheck) { return true; } // If the item is a section, check if any of the items in the section can check. if (item.sectionProps && item.sectionProps.items.some(function (submenuItem) { return submenuItem.canCheck === true; })) { return true; } return false; }); } var NavigationIdleDelay = 250 /* ms */; var ContextualMenuBase = /** @class */ (function (_super) { tslib_1.__extends(ContextualMenuBase, _super); function ContextualMenuBase(props) { var _this = _super.call(this, props) || this; _this._mounted = false; _this.dismiss = function (ev, dismissAll) { var onDismiss = _this.props.onDismiss; if (onDismiss) { onDismiss(ev, dismissAll); } }; _this._onRenderMenuList = function (menuListProps, defaultRender) { var indexCorrection = 0; return (React.createElement("ul", { className: _this._classNames.list, onKeyDown: _this._onKeyDown, onKeyUp: _this._onKeyUp }, menuListProps.items.map(function (item, index) { var menuItem = _this._renderMenuItem(item, index, indexCorrection, menuListProps.totalItemCount, menuListProps.hasCheckmarks, menuListProps.hasIcons); if (item.itemType !== ContextualMenuItemType.Divider && item.itemType !== ContextualMenuItemType.Header) { var indexIncrease = item.customOnRenderListLength ? item.customOnRenderListLength : 1; indexCorrection += indexIncrease; } return menuItem; }))); }; _this._onKeyDown = function (ev) { // Take note if we are processing a altKey or metaKey keydown // so that the menu does not collapse if no other keys are pressed _this._processingExpandCollapseKeyOnly = _this._isExpandCollapseKey(ev); return _this._keyHandler(ev, _this._shouldHandleKeyDown); }; _this._shouldHandleKeyDown = function (ev) { return (ev.which === 27 /* escape */ || _this._shouldCloseSubMenu(ev) || (ev.which === 38 /* up */ && (ev.altKey || ev.metaKey))); }; _this._onMenuFocusCapture = function (ev) { if (_this.props.delayUpdateFocusOnHover) { _this._shouldUpdateFocusOnMouseEvent = true; } }; _this._onKeyUp = function (ev) { return _this._keyHandler(ev, _this._shouldHandleKeyUp, true /* dismissAllMenus */); }; _this._shouldHandleKeyUp = function (ev) { var shouldHandleKey = _this._processingExpandCollapseKeyOnly && _this._isExpandCollapseKey(ev); _this._processingExpandCollapseKeyOnly = false; return shouldHandleKey; }; _this._keyHandler = function (ev, shouldHandleKey, dismissAllMenus) { var handled = false; if (shouldHandleKey(ev)) { _this._isFocusingPreviousElement = true; ev.preventDefault(); ev.stopPropagation(); _this.dismiss(ev, dismissAllMenus); handled = true; } return handled; }; /** * Checks if the submenu should be closed */ _this._shouldCloseSubMenu = function (ev) { var submenuCloseKey = getRTL() ? 39 /* right */ : 37 /* left */; if (ev.which !== submenuCloseKey || !_this.props.isSubMenu) { return false; } return (_this._adjustedFocusZoneProps.direction === FocusZoneDirection.vertical || (!!_this._adjustedFocusZoneProps.checkForNoWrap && !shouldWrapFocus(ev.target, 'data-no-horizontal-wrap'))); }; _this._onMenuKeyDown = function (ev) { // Mark as handled if onKeyDown (for handling collapse cases) return true // or if we are attempting to expand a submenu (otherwise) var handled = _this._onKeyDown(ev); if (handled || !_this._host) { return; } // If we have a modifier key being pressed, we do not want to move focus. // Otherwise, handle up and down keys var elementToFocus = ev.altKey || ev.metaKey ? null : ev.which === 38 /* up */ ? getLastFocusable(_this._host, _this._host.lastChild, true) : ev.which === 40 /* down */ ? getFirstFocusable(_this._host, _this._host.firstChild, true) : null; if (elementToFocus) { elementToFocus.focus(); ev.preventDefault(); ev.stopPropagation(); } }; /** * Scroll handler for the callout to make sure the mouse events * for updating focus are not interacting during scroll */ _this._onScroll = function () { if (!_this._isScrollIdle && _this._scrollIdleTimeoutId !== undefined) { _this._async.clearTimeout(_this._scrollIdleTimeoutId); _this._scrollIdleTimeoutId = undefined; } else { _this._isScrollIdle = false; } _this._scrollIdleTimeoutId = _this._async.setTimeout(function () { _this._isScrollIdle = true; }, NavigationIdleDelay); }; _this._onItemMouseEnterBase = function (item, ev, target) { if (_this._shouldIgnoreMouseEvent()) { return; } _this._updateFocusOnMouseEvent(item, ev, target); }; _this._onItemMouseMoveBase = function (item, ev, target) { var targetElement = ev.currentTarget; // Always do this check to make sure we record // a mouseMove if needed (even if we are timed out) if (_this._shouldUpdateFocusOnMouseEvent) { _this._gotMouseMove = true; } else { return; } if (!_this._isScrollIdle || _this._enterTimerId !== undefined || targetElement === _this._targetWindow.document.activeElement) { return; } _this._updateFocusOnMouseEvent(item, ev, target); }; _this._onMouseItemLeave = function (item, ev) { if (_this._shouldIgnoreMouseEvent()) { return; } if (_this._enterTimerId !== undefined) { _this._async.clearTimeout(_this._enterTimerId); _this._enterTimerId = undefined; } if (_this.state.expandedMenuItemKey !== undefined) { return; } /** * IE11 focus() method forces parents to scroll to top of element. * Edge and IE expose a setActive() function for focusable divs that * sets the page focus but does not scroll the parent element. */ if (_this._host.setActive) { try { _this._host.setActive(); } catch (e) { /* no-op */ } } else { _this._host.focus(); } }; _this._onItemMouseDown = function (item, ev) { if (item.onMouseDown) { item.onMouseDown(item, ev); } }; _this._onItemClick = function (item, ev) { _this._onItemClickBase(item, ev, ev.currentTarget); }; _this._onItemClickBase = function (item, ev, target) { var items = getSubmenuItems(item); // Cancel a async menu item hover timeout action from being taken and instead // just trigger the click event instead. _this._cancelSubMenuTimer(); if (!hasSubmenu(item) && (!items || !items.length)) { // This is an item without a menu. Click it. _this._executeItemClick(item, ev); } else { if (item.key !== _this.state.expandedMenuItemKey) { // This has a collapsed sub menu. Expand it. _this.setState({ // When Edge + Narrator are used together (regardless of if the button is in a form or not), pressing // "Enter" fires this method and not _onMenuKeyDown. Checking ev.nativeEvent.detail differentiates // between a real click event and a keypress event. expandedByMouseClick: ev.nativeEvent.detail !== 0 }); _this._onItemSubMenuExpand(item, target); } } ev.stopPropagation(); ev.preventDefault(); }; _this._onAnchorClick = function (item, ev) { _this._executeItemClick(item, ev); ev.stopPropagation(); }; _this._executeItemClick = function (item, ev) { if (item.disabled || item.isDisabled) { return; } var dismiss = false; if (item.onClick) { dismiss = !!item.onClick(ev, item); } else if (_this.props.onItemClick) { dismiss = !!_this.props.onItemClick(ev, item); } (dismiss || !ev.defaultPrevented) && _this.dismiss(ev, true); }; _this._onItemKeyDown = function (item, ev) { var openKey = getRTL() ? 37 /* left */ : 39 /* right */; if (!item.disabled && (ev.which === openKey || ev.which === 13 /* enter */ || (ev.which === 40 /* down */ && (ev.altKey || ev.metaKey)))) { _this.setState({ expandedByMouseClick: false }); _this._onItemSubMenuExpand(item, ev.currentTarget); ev.preventDefault(); } }; // Cancel a async menu item hover timeout action from being taken and instead // do new upcoming behavior _this._cancelSubMenuTimer = function () { if (_this._enterTimerId !== undefined) { _this._async.clearTimeout(_this._enterTimerId); _this._enterTimerId = undefined; } }; _this._onItemSubMenuExpand = function (item, target) { if (_this.state.expandedMenuItemKey !== item.key) { if (_this.state.expandedMenuItemKey) { _this._onSubMenuDismiss(); } // Focus the target to ensure when we close it, we're focusing on the correct element. target.focus(); _this.setState({ expandedMenuItemKey: item.key, submenuTarget: target }); } }; /** * This function is called ASYNCHRONOUSLY, and so there is a chance it is called * after the component is unmounted. The _mounted property is added to prevent * from calling setState() after unmount. Do NOT copy this pattern in synchronous * code. */ _this._onSubMenuDismiss = function (ev, dismissAll) { if (dismissAll) { _this.dismiss(ev, dismissAll); } else if (_this._mounted) { _this.setState({ dismissedMenuItemKey: _this.state.expandedMenuItemKey, expandedMenuItemKey: undefined, submenuTarget: undefined }); } }; _this._getSubMenuId = function (item) { var subMenuId = _this.state.subMenuId; if (item.subMenuProps && item.subMenuProps.id) { subMenuId = item.subMenuProps.id; } return subMenuId; }; _this._onPointerAndTouchEvent = function (ev) { _this._cancelSubMenuTimer(); }; _this.state = { contextualMenuItems: undefined, subMenuId: getId('ContextualMenu') }; _this._warnDeprecations({ getMenuClassNames: 'styles' }); _this._isFocusingPreviousElement = false; _this._isScrollIdle = true; _this._processingExpandCollapseKeyOnly = false; _this._shouldUpdateFocusOnMouseEvent = !_this.props.delayUpdateFocusOnHover; _this._gotMouseMove = false; return _this; } ContextualMenuBase.prototype.componentWillUpdate = function (newProps) { if (newProps.target !== this.props.target) { var newTarget = newProps.target; this._setTargetWindowAndElement(newTarget); } if (newProps.hidden !== this.props.hidden) { if (newProps.hidden) { this._onMenuClosed(); } else { this._onMenuOpened(); this._previousActiveElement = this._targetWindow ? this._targetWindow.document.activeElement : null; } } if (newProps.delayUpdateFocusOnHover !== this.props.delayUpdateFocusOnHover) { // update shouldUpdateFocusOnMouseEvent to follow what was passed in this._shouldUpdateFocusOnMouseEvent = !newProps.delayUpdateFocusOnHover; // If shouldUpdateFocusOnMouseEvent is false, we need to reset gotMouseMove to false this._gotMouseMove = this._shouldUpdateFocusOnMouseEvent && this._gotMouseMove; } }; // Invoked once, both on the client and server, immediately before the initial rendering occurs. ContextualMenuBase.prototype.componentWillMount = function () { var target = this.props.target; this._setTargetWindowAndElement(target); if (!this.props.hidden) { this._previousActiveElement = this._targetWindow ? this._targetWindow.document.activeElement : null; } }; // Invoked once, only on the client (not on the server), immediately after the initial rendering occurs. ContextualMenuBase.prototype.componentDidMount = function () { if (!this.props.hidden) { this._onMenuOpened(); } this._mounted = true; }; // Invoked immediately before a component is unmounted from the DOM. ContextualMenuBase.prototype.componentWillUnmount = function () { var _this = this; if (this._isFocusingPreviousElement && this._previousActiveElement) { // This slight delay is required so that we can unwind the stack, const react try to mess with focus, and then // apply the correct focus. Without the setTimeout, we end up focusing the correct thing, and then React wants // to reset the focus back to the thing it thinks should have been focused. // Note: Cannot be replaced by this._async.setTimout because those will be removed by the time this is called. setTimeout(function () { _this._previousActiveElement && _this._previousActiveElement.focus(); }, 0); } if (this.props.onMenuDismissed) { this.props.onMenuDismissed(this.props); } this._events.dispose(); this._async.dispose(); this._mounted = false; }; ContextualMenuBase.prototype.render = function () { var _this = this; var isBeakVisible = this.props.isBeakVisible; var _a = this.props, items = _a.items, labelElementId = _a.labelElementId, id = _a.id, className = _a.className, beakWidth = _a.beakWidth, directionalHint = _a.directionalHint, directionalHintForRTL = _a.directionalHintForRTL, gapSpace = _a.gapSpace, coverTarget = _a.coverTarget, ariaLabel = _a.ariaLabel, doNotLayer = _a.doNotLayer, target = _a.target, bounds = _a.bounds, useTargetWidth = _a.useTargetWidth, useTargetAsMinWidth = _a.useTargetAsMinWidth, directionalHintFixed = _a.directionalHintFixed, shouldFocusOnMount = _a.shouldFocusOnMount, shouldFocusOnContainer = _a.shouldFocusOnContainer, title = _a.title, styles = _a.styles, theme = _a.theme, calloutProps = _a.calloutProps, _b = _a.onRenderSubMenu, onRenderSubMenu = _b === void 0 ? this._onRenderSubMenu : _b, _c = _a.onRenderMenuList, onRenderMenuList = _c === void 0 ? this._onRenderMenuList : _c, focusZoneProps = _a.focusZoneProps, getMenuClassNames = _a.getMenuClassNames; this._classNames = getMenuClassNames ? getMenuClassNames(theme, className) : getClassNames(styles, { theme: theme, className: className }); var hasIcons = itemsHaveIcons(items); function itemsHaveIcons(contextualMenuItems) { for (var _i = 0, contextualMenuItems_1 = contextualMenuItems; _i < contextualMenuItems_1.length; _i++) { var item = contextualMenuItems_1[_i]; if (!!item.iconProps) { return true; } if (item.itemType === ContextualMenuItemType.Section && item.sectionProps && itemsHaveIcons(item.sectionProps.items)) { return true; } } return false; } this._adjustedFocusZoneProps = tslib_1.__assign({}, focusZoneProps, { direction: this._getFocusZoneDirection() }); var hasCheckmarks = canAnyMenuItemsCheck(items); var submenuProps = this.state.expandedMenuItemKey ? this._getSubmenuProps() : null; isBeakVisible = isBeakVisible === undefined ? this.props.responsiveMode <= ResponsiveMode.medium : isBeakVisible; /** * When useTargetWidth is true, get the width of the target element and apply it for the context menu container */ var contextMenuStyle; var targetAsHtmlElement = this._target; if ((useTargetWidth || useTargetAsMinWidth) && targetAsHtmlElement && targetAsHtmlElement.offsetWidth) { var targetBoundingRect = targetAsHtmlElement.getBoundingClientRect(); var targetWidth = targetBoundingRect.width - 2 /* Accounts for 1px border */; if (useTargetWidth) { contextMenuStyle = { width: targetWidth }; } else if (useTargetAsMinWidth) { contextMenuStyle = { minWidth: targetWidth }; } } // The menu should only return if items were provided, if no items were provided then it should not appear. if (items && items.length > 0) { var totalItemCount = 0; for (var _i = 0, items_1 = items; _i < items_1.length; _i++) { var item = items_1[_i]; if (item.itemType !== ContextualMenuItemType.Divider && item.itemType !== ContextualMenuItemType.Header) { var itemCount = item.customOnRenderListLength ? item.customOnRenderListLength : 1; totalItemCount += itemCount; } } return (React.createElement(Callout, tslib_1.__assign({}, calloutProps, { target: target, isBeakVisible: isBeakVisible, beakWidth: beakWidth, directionalHint: directionalHint, directionalHintForRTL: directionalHintForRTL, gapSpace: gapSpace, coverTarget: coverTarget, doNotLayer: doNotLayer, className: css('ms-ContextualMenu-Callout', calloutProps ? calloutProps.className : undefined), setInitialFocus: shouldFocusOnMount, onDismiss: this.props.onDismiss, onScroll: this._onScroll, bounds: bounds, directionalHintFixed: directionalHintFixed, hidden: this.props.hidden }), React.createElement("div", { role: 'menu', "aria-label": ariaLabel, "aria-labelledby": labelElementId, style: contextMenuStyle, ref: function (host) { return (_this._host = host); }, id: id, className: this._classNames.container, tabIndex: shouldFocusOnContainer ? 0 : -1, onKeyDown: this._onMenuKeyDown, onKeyUp: this._onKeyUp, onFocusCapture: this._onMenuFocusCapture }, title && React.createElement("div", { className: this._classNames.title }, " ", title, " "), items && items.length ? (React.createElement(FocusZone, tslib_1.__assign({}, this._adjustedFocusZoneProps, { className: this._classNames.root, isCircularNavigation: true, handleTabKey: 1 /* all */ }), onRenderMenuList({ items: items, totalItemCount: totalItemCount, hasCheckmarks: hasCheckmarks, hasIcons: hasIcons }, this._onRenderMenuList))) : null, submenuProps && onRenderSubMenu(submenuProps, this._onRenderSubMenu)))); } else { return null; } }; ContextualMenuBase.prototype._onMenuOpened = function () { this._events.on(this._targetWindow, 'resize', this.dismiss); this._shouldUpdateFocusOnMouseEvent = !this.props.delayUpdateFocusOnHover; this._gotMouseMove = false; this.props.onMenuOpened && this.props.onMenuOpened(this.props); }; ContextualMenuBase.prototype._onMenuClosed = function () { var _this = this; this._events.off(this._targetWindow, 'resize', this.dismiss); this._previousActiveElement && this._async.setTimeout(function () { _this._previousActiveElement && _this._previousActiveElement.focus(); }, 0); this._shouldUpdateFocusOnMouseEvent = !this.props.delayUpdateFocusOnHover; }; /** * Gets the focusZoneDirection by using the arrowDirection if specified, * the direction specificed in the focusZoneProps, or defaults to FocusZoneDirection.vertical */ ContextualMenuBase.prototype._getFocusZoneDirection = function () { var focusZoneProps = this.props.focusZoneProps; return focusZoneProps && focusZoneProps.direction !== undefined ? focusZoneProps.direction : FocusZoneDirection.vertical; }; ContextualMenuBase.prototype._onRenderSubMenu = function (subMenuProps) { return React.createElement(ContextualMenu, tslib_1.__assign({}, subMenuProps)); }; ContextualMenuBase.prototype._renderMenuItem = function (item, index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons) { var renderedItems = []; var iconProps = item.iconProps || { iconName: 'None' }; var getItemClassNames = item.getItemClassNames, itemProps = item.itemProps; var styles = itemProps ? itemProps.styles : undefined; // We only send a dividerClassName when the item to be rendered is a divider. For all other cases, the default divider style is used. var dividerClassName = item.itemType === ContextualMenuItemType.Divider ? item.className : undefined; var subMenuIconClassName = item.submenuIconProps ? item.submenuIconProps.className : ''; var itemClassNames; // IContextualMenuItem#getItemClassNames for backwards compatibility // otherwise uses mergeStyles for class names. if (getItemClassNames) { itemClassNames = getItemClassNames(this.props.theme, isItemDisabled(item), this.state.expandedMenuItemKey === item.key, !!getIsChecked(item), !!item.href, iconProps.iconName !== 'None', item.className, dividerClassName, iconProps.className, subMenuIconClassName, item.primaryDisabled); } else { var itemStyleProps = { theme: this.props.theme, disabled: isItemDisabled(item), expanded: this.state.expandedMenuItemKey === item.key, checked: !!getIsChecked(item), isAnchorLink: !!item.href, knownIcon: iconProps.iconName !== 'None', itemClassName: item.className, dividerClassName: dividerClassName, iconClassName: iconProps.className, subMenuClassName: subMenuIconClassName, primaryDisabled: item.primaryDisabled }; // We need to generate default styles then override if styles are provided // since the ContextualMenu currently handles item classNames. itemClassNames = mergeStyleSets(getContextualMenuItemClassNames(getItemStyles, itemStyleProps), getContextualMenuItemClassNames(styles, itemStyleProps)); } if (item.text === '-' || item.name === '-') { item.itemType = ContextualMenuItemType.Divider; } switch (item.itemType) { case ContextualMenuItemType.Divider: renderedItems.push(this._renderSeparator(index, itemClassNames)); break; case ContextualMenuItemType.Header: renderedItems.push(this._renderSeparator(index, itemClassNames)); var headerItem = this._renderHeaderMenuItem(item, itemClassNames, index, hasCheckmarks, hasIcons); renderedItems.push(this._renderListItem(headerItem, item.key || index, itemClassNames, item.title)); break; case ContextualMenuItemType.Section: renderedItems.push(this._renderSectionItem(item, itemClassNames, index, hasCheckmarks, hasIcons)); break; default: var menuItem = this._renderNormalItem(item, itemClassNames, index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons); renderedItems.push(this._renderListItem(menuItem, item.key || index, itemClassNames, item.title)); break; } return renderedItems; }; ContextualMenuBase.prototype._renderSectionItem = function (item, menuClassNames, index, hasCheckmarks, hasIcons) { var _this = this; var section = item.sectionProps; if (!section) { return; } var headerItem; if (section.title) { var headerContextualMenuItem = { key: "section-" + section.title + "-title", itemType: ContextualMenuItemType.Header, text: section.title }; headerItem = this._renderHeaderMenuItem(headerContextualMenuItem, menuClassNames, index, hasCheckmarks, hasIcons); } if (section.items && section.items.length > 0) { return (React.createElement("li", { role: "presentation", key: section.key }, React.createElement("div", { role: "group" }, React.createElement("ul", { className: this._classNames.list }, section.topDivider && this._renderSeparator(index, menuClassNames, true, true), headerItem && this._renderListItem(headerItem, item.key || index, menuClassNames, item.title), section.items.map(function (contextualMenuItem, itemsIndex) { return _this._renderMenuItem(contextualMenuItem, itemsIndex, itemsIndex, section.items.length, hasCheckmarks, hasIcons); }), section.bottomDivider && this._renderSeparator(index, menuClassNames, false, true))))); } }; ContextualMenuBase.prototype._renderListItem = function (content, key, classNames, title) { return (React.createElement("li", { role: "presentation", title: title, key: key, className: classNames.item }, content)); }; ContextualMenuBase.prototype._renderSeparator = function (index, classNames, top, fromSection) { if (fromSection || index > 0) { return (React.createElement("li", { role: "separator", key: 'separator-' + index + (top === undefined ? '' : top ? '-top' : '-bottom'), className: classNames.divider })); } return null; }; ContextualMenuBase.prototype._renderNormalItem = function (item, classNames, index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons) { if (item.onRender) { return item.onRender(tslib_1.__assign({ 'aria-posinset': focusableElementIndex + 1, 'aria-setsize': totalItemCount }, item), this.dismiss); } if (item.href) { return this._renderAnchorMenuItem(item, classNames, index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons); } if (item.split && hasSubmenu(item)) { return this._renderSplitButton(item, classNames, index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons); } return this._renderButtonItem(item, classNames, index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons); }; ContextualMenuBase.prototype._renderHeaderMenuItem = function (item, classNames, index, hasCheckmarks, hasIcons) { var _a = this.props.contextualMenuItemAs, ChildrenRenderer = _a === void 0 ? ContextualMenuItem : _a; var itemProps = item.itemProps; return (React.createElement("div", { className: this._classNames.header, style: item.style }, React.createElement(ChildrenRenderer, tslib_1.__assign({ item: item, classNames: classNames, index: index, onCheckmarkClick: hasCheckmarks ? this._onItemClick : undefined, hasIcons: hasIcons }, itemProps)))); }; ContextualMenuBase.prototype._renderAnchorMenuItem = function (item, classNames, index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons) { var contextualMenuItemAs = this.props.contextualMenuItemAs; var expandedMenuItemKey = this.state.expandedMenuItemKey; return (React.createElement(ContextualMenuAnchor, { item: item, classNames: classNames, index: index, focusableElementIndex: focusableElementIndex, totalItemCount: totalItemCount, hasCheckmarks: hasCheckmarks, hasIcons: hasIcons, contextualMenuItemAs: contextualMenuItemAs, onItemMouseEnter: this._onItemMouseEnterBase, onItemMouseLeave: this._onMouseItemLeave, onItemMouseMove: this._onItemMouseMoveBase, onItemMouseDown: this._onItemMouseDown, executeItemClick: this._executeItemClick, onItemClick: this._onAnchorClick, onItemKeyDown: this._onItemKeyDown, getSubMenuId: this._getSubMenuId, expandedMenuItemKey: expandedMenuItemKey, openSubMenu: this._onItemSubMenuExpand, dismissSubMenu: this._onSubMenuDismiss, dismissMenu: this.dismiss })); }; ContextualMenuBase.prototype._renderButtonItem = function (item, classNames, index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons) { var contextualMenuItemAs = this.props.contextualMenuItemAs; var expandedMenuItemKey = this.state.expandedMenuItemKey; return (React.createElement(ContextualMenuButton, { item: item, classNames: classNames, index: index, focusableElementIndex: focusableElementIndex, totalItemCount: totalItemCount, hasCheckmarks: hasCheckmarks, hasIcons: hasIcons, contextualMenuItemAs: contextualMenuItemAs, onItemMouseEnter: this._onItemMouseEnterBase, onItemMouseLeave: this._onMouseItemLeave, onItemMouseMove: this._onItemMouseMoveBase, onItemMouseDown: this._onItemMouseDown, executeItemClick: this._executeItemClick, onItemClick: this._onItemClick, onItemClickBase: this._onItemClickBase, onItemKeyDown: this._onItemKeyDown, getSubMenuId: this._getSubMenuId, expandedMenuItemKey: expandedMenuItemKey, openSubMenu: this._onItemSubMenuExpand, dismissSubMenu: this._onSubMenuDismiss, dismissMenu: this.dismiss })); }; ContextualMenuBase.prototype._renderSplitButton = function (item, classNames, index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons) { var contextualMenuItemAs = this.props.contextualMenuItemAs; var expandedMenuItemKey = this.state.expandedMenuItemKey; return (React.createElement(ContextualMenuSplitButton, { item: item, classNames: classNames, index: index, focusableElementIndex: focusableElementIndex, totalItemCount: totalItemCount, hasCheckmarks: hasCheckmarks, hasIcons: hasIcons, contextualMenuItemAs: contextualMenuItemAs, onItemMouseEnter: this._onItemMouseEnterBase, onItemMouseLeave: this._onMouseItemLeave, onItemMouseMove: this._onItemMouseMoveBase, onItemMouseDown: this._onItemMouseDown, executeItemClick: this._executeItemClick, onItemClick: this._onItemClick, onItemClickBase: this._onItemClickBase, onItemKeyDown: this._onItemKeyDown, openSubMenu: this._onItemSubMenuExpand, dismissSubMenu: this._onSubMenuDismiss, dismissMenu: this.dismiss, expandedMenuItemKey: expandedMenuItemKey, onTap: this._onPointerAndTouchEvent })); }; ContextualMenuBase.prototype._isExpandCollapseKey = function (ev) { return ev.which === 18 /* alt */ || ev.key === 'Meta'; }; ContextualMenuBase.prototype._shouldIgnoreMouseEvent = function () { return !this._isScrollIdle || !this._gotMouseMove; }; /** * Handles updating focus when mouseEnter or mouseMove fire. * As part of updating focus, This function will also update * the expand/collapse state accordingly. */ ContextualMenuBase.prototype._updateFocusOnMouseEvent = function (item, ev, target) { var _this = this; var targetElement = target ? target : ev.currentTarget; var _a = this.props.subMenuHoverDelay, timeoutDuration = _a === void 0 ? NavigationIdleDelay : _a; if (item.key === this.state.expandedMenuItemKey) { return; } if (this._enterTimerId !== undefined) { this._async.clearTimeout(this._enterTimerId); this._enterTimerId = undefined; } // If the menu is not expanded we can update focus without any delay if (this.state.expandedMenuItemKey === undefined) { targetElement.focus(); } // Delay updating expanding/dismissing the submenu // and only set focus if we have not already done so if (hasSubmenu(item)) { ev.stopPropagation(); this._enterTimerId = this._async.setTimeout(function () { targetElement.focus(); _this.setState({ expandedByMouseClick: true }); _this._onItemSubMenuExpand(item, targetElement); _this._enterTimerId = undefined; }, timeoutDuration); } else { this._enterTimerId = this._async.setTimeout(function () { _this._onSubMenuDismiss(ev); targetElement.focus(); _this._enterTimerId = undefined; }, timeoutDuration); } }; ContextualMenuBase.prototype._getSubmenuProps = function () { var _a = this.state, submenuTarget = _a.submenuTarget, expandedMenuItemKey = _a.expandedMenuItemKey; var item = this._findItemByKey(expandedMenuItemKey); var submenuProps = null; if (item) { submenuProps = { items: getSubmenuItems(item), target: submenuTarget, onDismiss: this._onSubMenuDismiss, isSubMenu: true, id: this.state.subMenuId, shouldFocusOnMount: true, shouldFocusOnContainer: this.state.expandedByMouseClick, directionalHint: getRTL() ? 8 /* leftTopEdge */ : 11 /* rightTopEdge */, className: this.props.className, gapSpace: 0, isBeakVisible: false }; if (item.subMenuProps) { assign(submenuProps, item.subMenuProps); } } return submenuProps; }; ContextualMenuBase.prototype._findItemByKey = function (key) { var items = this.props.items; return this._findItemByKeyFromItems(key, items); }; /** * Returns the item that mathes a given key if any. * @param key The key of the item to match * @param items The items to look for the key */ ContextualMenuBase.prototype._findItemByKeyFromItems = function (key, items) { for (var _i = 0, items_2 = items; _i < items_2.length; _i++) { var item = items_2[_i]; if (item.itemType === ContextualMenuItemType.Section && item.sectionProps) { var match = this._findItemByKeyFromItems(key, item.sectionProps.items); if (match) { return match; } } else if (item.key && item.key === key) { return item; } } }; ContextualMenuBase.prototype._setTargetWindowAndElement = function (target) { if (target) { if (typeof target === 'string') { var currentDoc = getDocument(); this._target = currentDoc ? currentDoc.querySelector(target) : null; this._targetWindow = getWindow(); } else if (target.stopPropagation) { this._targetWindow = getWindow(target.toElement); this._target = target; } else if (target.x !== undefined && target.y !== undefined) { this._targetWindow = getWindow(); this._target = target; } else { var targetElement = target; this._targetWindow = getWindow(targetElement); this._target = target; } } else { this._targetWindow = getWindow(); } }; // The default ContextualMenu properties have no items and beak, the default submenu direction is right and top. ContextualMenuBase.defaultProps = { items: [], shouldFocusOnMount: true, gapSpace: 0, directionalHint: 7 /* bottomAutoEdge */, beakWidth: 16 }; ContextualMenuBase = tslib_1.__decorate([ withResponsiveMode ], ContextualMenuBase); return ContextualMenuBase; }(BaseComponent)); export { ContextualMenuBase }; //# sourceMappingURL=ContextualMenu.base.js.map