devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
923 lines (921 loc) • 42.2 kB
JavaScript
/**
* DevExtreme (cjs/__internal/ui/menu/menu.js)
* Version: 25.2.3
* Build date: Fri Dec 12 2025
*
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.DX_MENU_ITEM_CLASS = void 0;
var _events_engine = _interopRequireDefault(require("../../../common/core/events/core/events_engine"));
var _hover = require("../../../common/core/events/hover");
var _pointer = _interopRequireDefault(require("../../../common/core/events/pointer"));
var _utils = require("../../../common/core/events/utils");
var _component_registrator = _interopRequireDefault(require("../../../core/component_registrator"));
var _element = require("../../../core/element");
var _renderer = _interopRequireDefault(require("../../../core/renderer"));
var _common = require("../../../core/utils/common");
var _extend = require("../../../core/utils/extend");
var _iterator = require("../../../core/utils/iterator");
var _size = require("../../../core/utils/size");
var _type = require("../../../core/utils/type");
var _button = _interopRequireDefault(require("../../../ui/button"));
var _ui = _interopRequireDefault(require("../../../ui/overlay/ui.overlay"));
var _tree_view = _interopRequireDefault(require("../../../ui/tree_view"));
var _menu_base = _interopRequireDefault(require("../../ui/context_menu/menu_base"));
var _utils2 = require("../../ui/overlay/utils");
var _submenu = _interopRequireDefault(require("./submenu"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : {
default: e
}
}
const DX_MENU_CLASS = "dx-menu";
const DX_MENU_VERTICAL_CLASS = "dx-menu-vertical";
const DX_MENU_HORIZONTAL_CLASS = "dx-menu-horizontal";
const DX_MENU_ITEM_CLASS = exports.DX_MENU_ITEM_CLASS = "dx-menu-item";
const DX_MENU_ITEMS_CONTAINER_CLASS = "dx-menu-items-container";
const DX_MENU_ITEM_EXPANDED_CLASS = `${DX_MENU_ITEM_CLASS}-expanded`;
const DX_CONTEXT_MENU_CLASS = "dx-context-menu";
const DX_CONTEXT_MENU_CONTAINER_BORDER_CLASS = "dx-context-menu-container-border";
const DX_CONTEXT_MENU_CONTENT_DELIMITER_CLASS = "dx-context-menu-content-delimiter";
const DX_SUBMENU_CLASS = "dx-submenu";
const DX_STATE_DISABLED_CLASS = "dx-state-disabled";
const DX_STATE_HOVER_CLASS = "dx-state-hover";
const DX_STATE_ACTIVE_CLASS = "dx-state-active";
const DX_ADAPTIVE_MODE_CLASS = "dx-menu-adaptive-mode";
const DX_ADAPTIVE_HAMBURGER_BUTTON_CLASS = "dx-menu-hamburger-button";
const DX_ADAPTIVE_MODE_OVERLAY_WRAPPER_CLASS = `${DX_ADAPTIVE_MODE_CLASS}-overlay-wrapper`;
const FOCUS_UP = "up";
const FOCUS_DOWN = "down";
const FOCUS_LEFT = "left";
const FOCUS_RIGHT = "right";
const SHOW_SUBMENU_OPERATION = "showSubmenu";
const NEXT_ITEM_OPERATION = "nextItem";
const PREV_ITEM_OPERATION = "prevItem";
const DEFAULT_DELAY = {
show: 50,
hide: 300
};
const ACTIONS = ["onSubmenuShowing", "onSubmenuShown", "onSubmenuHiding", "onSubmenuHidden", "onItemContextMenu", "onItemClick", "onSelectionChanged", "onItemRendered"];
class Menu extends _menu_base.default {
_getDefaultOptions() {
return Object.assign({}, super._getDefaultOptions(), {
orientation: "horizontal",
submenuDirection: "auto",
showFirstSubmenuMode: {
name: "onClick",
delay: {
show: 50,
hide: 300
}
},
hideSubmenuOnMouseLeave: false,
onSubmenuShowing: null,
onSubmenuShown: null,
onSubmenuHiding: null,
onSubmenuHidden: null,
adaptivityEnabled: false
})
}
_setOptionsByReference() {
super._setOptionsByReference();
(0, _extend.extend)(this._optionsByReference, {
animation: true,
selectedItem: true
})
}
_itemElements() {
const rootMenuElements = super._itemElements();
const submenuElements = this._submenuItemElements();
return rootMenuElements.add(submenuElements)
}
_submenuItemElements() {
const itemSelector = `.${DX_MENU_ITEM_CLASS}`;
const currentSubmenu = this._submenus.length && this._submenus[0];
if (currentSubmenu && currentSubmenu.itemsContainer()) {
var _currentSubmenu$items;
return (null === (_currentSubmenu$items = currentSubmenu.itemsContainer()) || void 0 === _currentSubmenu$items ? void 0 : _currentSubmenu$items.find(itemSelector)) ?? (0, _renderer.default)()
}
return (0, _renderer.default)()
}
_focusTarget() {
return this.$element()
}
_isMenuHorizontal() {
const {
orientation: orientation
} = this.option();
return "horizontal" === orientation
}
_moveFocus(location) {
const $items = this._getAvailableItems();
const isMenuHorizontal = this._isMenuHorizontal();
const $activeItem = this._getActiveItem(true);
let $argument;
let operation;
switch (location) {
case "up":
operation = isMenuHorizontal ? "showSubmenu" : this._getItemsNavigationOperation("prevItem");
$argument = isMenuHorizontal ? $activeItem : $items;
break;
case "down":
operation = isMenuHorizontal ? "showSubmenu" : this._getItemsNavigationOperation("nextItem");
$argument = isMenuHorizontal ? $activeItem : $items;
break;
case "right":
operation = isMenuHorizontal ? this._getItemsNavigationOperation("nextItem") : "showSubmenu";
$argument = isMenuHorizontal ? $items : $activeItem;
break;
case "left":
operation = isMenuHorizontal ? this._getItemsNavigationOperation("prevItem") : "showSubmenu";
$argument = isMenuHorizontal ? $items : $activeItem;
break;
default:
return super._moveFocus(location)
}
const navigationAction = this._getKeyboardNavigationAction(operation, $argument);
const $newTarget = navigationAction();
if ($newTarget && 0 !== $newTarget.length) {
this.option("focusedElement", (0, _element.getPublicElement)($newTarget))
}
}
_getItemsNavigationOperation(operation) {
const {
rtlEnabled: rtlEnabled
} = this.option();
if (rtlEnabled) {
return "prevItem" === operation ? "nextItem" : "prevItem"
}
return operation
}
_getKeyboardNavigationAction(operation, argument) {
let action = _common.noop;
switch (operation) {
case "showSubmenu":
if (!argument.hasClass("dx-state-disabled")) {
action = this._showSubmenu.bind(this, argument)
}
break;
case "nextItem":
action = this._nextItem.bind(this, argument);
break;
case "prevItem":
action = this._prevItem.bind(this, argument)
}
return action
}
_clean() {
super._clean();
const {
templatesRenderAsynchronously: templatesRenderAsynchronously
} = this.option();
if (templatesRenderAsynchronously) {
clearTimeout(this._resizeEventTimer)
}
}
_visibilityChanged(visible) {
if (visible) {
if (!this._menuItemsWidth) {
this._updateItemsWidthCache()
}
this._dimensionChanged()
}
}
_isAdaptivityEnabled() {
const {
adaptivityEnabled: adaptivityEnabled,
orientation: orientation
} = this.option();
return !!adaptivityEnabled && "horizontal" === orientation
}
_updateItemsWidthCache() {
const $menuItems = this.$element().find("ul").first().children("li").children(`.${DX_MENU_ITEM_CLASS}`);
this._menuItemsWidth = this._getSummaryItemsSize("width", $menuItems, true)
}
_dimensionChanged() {
if (!this._isAdaptivityEnabled()) {
return
}
const containerWidth = (0, _size.getOuterWidth)(this.$element());
this._toggleAdaptiveMode(this._menuItemsWidth > containerWidth)
}
_init() {
super._init();
this._submenus = []
}
_initActions() {
this._actions = {};
(0, _iterator.each)(ACTIONS, ((_index, action) => {
this._actions[action] = this._createActionByOption(action)
}))
}
_initMarkup() {
this._visibleSubmenu = null;
this.$element().addClass("dx-menu");
super._initMarkup();
this._addCustomCssClass(this.$element());
this.setAria("role", "menubar")
}
_setAriaRole(state) {
const role = this._isAdaptivityEnabled() && state ? void 0 : "menubar";
this.setAria({
role: role
})
}
_render() {
super._render();
this._initAdaptivity()
}
_isTargetOutOfComponent(relatedTarget) {
const isInsideRootMenu = 0 !== (0, _renderer.default)(relatedTarget).closest(".dx-menu").length;
const isInsideContextMenu = 0 !== (0, _renderer.default)(relatedTarget).closest(".dx-context-menu").length;
return !(isInsideRootMenu || isInsideContextMenu)
}
_focusOutHandler(e) {
const {
relatedTarget: relatedTarget
} = e;
if (relatedTarget) {
const isTargetOutside = this._isTargetOutOfComponent(relatedTarget);
if (isTargetOutside) {
this._hideVisibleSubmenu()
}
}
super._focusOutHandler(e)
}
_renderHamburgerButton() {
this._hamburger = new _button.default((0, _renderer.default)("<div>").addClass("dx-menu-hamburger-button"), {
icon: "menu",
activeStateEnabled: false,
onClick: () => {
this._toggleTreeView()
}
});
return this._hamburger.$element()
}
_toggleTreeView(visible) {
var _this$_overlay, _this$_overlay2;
const isTreeViewVisible = visible ?? !(null !== (_this$_overlay = this._overlay) && void 0 !== _this$_overlay && null !== (_this$_overlay = _this$_overlay.option()) && void 0 !== _this$_overlay && _this$_overlay.visible);
null === (_this$_overlay2 = this._overlay) || void 0 === _this$_overlay2 || _this$_overlay2.option("visible", isTreeViewVisible);
if (isTreeViewVisible) {
var _this$_treeView;
null === (_this$_treeView = this._treeView) || void 0 === _this$_treeView || _this$_treeView.focus()
}
this._toggleHamburgerActiveState(isTreeViewVisible)
}
_toggleHamburgerActiveState(isActive) {
var _this$_hamburger;
null === (_this$_hamburger = this._hamburger) || void 0 === _this$_hamburger || _this$_hamburger.$element().toggleClass("dx-state-active", isActive)
}
_toggleAdaptiveMode(isAdaptive) {
const $menuItemsContainer = this.$element().find(".dx-menu-horizontal");
const $adaptiveElements = this.$element().find(`.${DX_ADAPTIVE_MODE_CLASS}`);
if (isAdaptive) {
this._hideVisibleSubmenu()
} else {
var _this$_treeView2;
null === (_this$_treeView2 = this._treeView) || void 0 === _this$_treeView2 || _this$_treeView2.collapseAll();
if (this._overlay) {
this._toggleTreeView(isAdaptive)
}
}
this._setAriaRole(isAdaptive);
$menuItemsContainer.toggle(!isAdaptive);
$adaptiveElements.toggle(isAdaptive)
}
_removeAdaptivity() {
if (!this._$adaptiveContainer) {
return
}
this._toggleAdaptiveMode(false);
this._$adaptiveContainer.remove();
this._$adaptiveContainer = null;
this._treeView = null;
this._hamburger = null;
this._overlay = null
}
_treeviewItemClickHandler(e) {
var _e$node;
this._actions.onItemClick(e);
if (!(null !== (_e$node = e.node) && void 0 !== _e$node && null !== (_e$node = _e$node.children) && void 0 !== _e$node && _e$node.length)) {
this._toggleTreeView(false)
}
}
_getAdaptiveOverlayOptions() {
var _this$_hamburger2;
const {
rtlEnabled: rtlEnabled
} = this.option();
const position = rtlEnabled ? "right" : "left";
return {
_ignoreFunctionValueDeprecation: true,
maxHeight: () => (0, _utils2.getElementMaxHeightByWindow)(this.$element()),
deferRendering: false,
shading: false,
animation: false,
hideOnParentScroll: true,
onHidden: () => {
this._toggleHamburgerActiveState(false)
},
height: "auto",
hideOnOutsideClick: e => !(0, _renderer.default)(e.target).closest(".dx-menu-hamburger-button").length,
position: {
collision: "flipfit",
at: `bottom ${position}`,
my: `top ${position}`,
of: null === (_this$_hamburger2 = this._hamburger) || void 0 === _this$_hamburger2 ? void 0 : _this$_hamburger2.$element()
}
}
}
_getTreeViewOptions() {
const menuOptions = {};
(0, _iterator.each)(["rtlEnabled", "width", "accessKey", "activeStateEnabled", "animation", "dataSource", "disabled", "displayExpr", "displayExpr", "focusStateEnabled", "hint", "hoverStateEnabled", "itemsExpr", "items", "itemTemplate", "selectedExpr", "selectionMode", "tabIndex", "visible"], ((_index, option) => {
menuOptions[option] = this.option(option)
}));
(0, _iterator.each)(["onItemContextMenu", "onSelectionChanged", "onItemRendered"], ((_index, actionName) => {
menuOptions[actionName] = e => {
this._actions[actionName](e)
}
}));
const {
animation: animation,
selectByClick: selectByClick
} = this.option();
return Object.assign({}, menuOptions, {
dataSource: this.getDataSource(),
animationEnabled: !!animation,
onItemClick: this._treeviewItemClickHandler.bind(this),
onItemExpanded: e => {
var _this$_overlay3, _this$_actions$onSubm, _this$_actions;
null === (_this$_overlay3 = this._overlay) || void 0 === _this$_overlay3 || _this$_overlay3.repaint();
null === (_this$_actions$onSubm = (_this$_actions = this._actions).onSubmenuShown) || void 0 === _this$_actions$onSubm || _this$_actions$onSubm.call(_this$_actions, e)
},
onItemCollapsed: e => {
var _this$_overlay4, _this$_actions$onSubm2, _this$_actions2;
null === (_this$_overlay4 = this._overlay) || void 0 === _this$_overlay4 || _this$_overlay4.repaint();
null === (_this$_actions$onSubm2 = (_this$_actions2 = this._actions).onSubmenuHidden) || void 0 === _this$_actions$onSubm2 || _this$_actions$onSubm2.call(_this$_actions2, e)
},
selectNodesRecursive: false,
selectByClick: selectByClick,
expandEvent: "click",
_supportItemUrl: true
})
}
_initAdaptivity() {
var _this$_overlay$$conte, _this$_overlay$$wrapp;
if (!this._isAdaptivityEnabled()) {
return
}
const {
cssClass: cssClass
} = this.option();
const $hamburger = this._renderHamburgerButton();
this._treeView = this._createComponent((0, _renderer.default)("<div>"), _tree_view.default, this._getTreeViewOptions());
this._overlay = this._createComponent((0, _renderer.default)("<div>"), _ui.default, this._getAdaptiveOverlayOptions());
null === (_this$_overlay$$conte = this._overlay.$content()) || void 0 === _this$_overlay$$conte || _this$_overlay$$conte.append(this._treeView.$element()).addClass(DX_ADAPTIVE_MODE_CLASS).addClass(cssClass);
null === (_this$_overlay$$wrapp = this._overlay.$wrapper()) || void 0 === _this$_overlay$$wrapp || _this$_overlay$$wrapp.addClass(DX_ADAPTIVE_MODE_OVERLAY_WRAPPER_CLASS);
this._$adaptiveContainer = (0, _renderer.default)("<div>").addClass(DX_ADAPTIVE_MODE_CLASS);
this._$adaptiveContainer.append($hamburger);
this._$adaptiveContainer.append(this._overlay.$element());
this.$element().append(this._$adaptiveContainer);
this._updateItemsWidthCache();
this._dimensionChanged()
}
_getDelay(delayType) {
const {
showFirstSubmenuMode: showFirstSubmenuMode
} = this.option();
const delay = (0, _type.isObject)(showFirstSubmenuMode) ? showFirstSubmenuMode.delay : void 0;
if (!(0, _type.isDefined)(delay)) {
return DEFAULT_DELAY[delayType]
}
return (0, _type.isObject)(delay) ? delay[delayType] ?? DEFAULT_DELAY[delayType] : delay
}
_keyboardHandler(e) {
return super._keyboardHandler(e, !!this._visibleSubmenu)
}
_renderContainer() {
const $wrapper = (0, _renderer.default)("<div>");
$wrapper.appendTo(this.$element()).addClass(this._isMenuHorizontal() ? "dx-menu-horizontal" : "dx-menu-vertical");
return super._renderContainer($wrapper)
}
_renderSubmenuItems(node, $itemFrame) {
const submenu = this._createSubmenu(node, $itemFrame);
this._submenus.push(submenu);
this._renderBorderElement($itemFrame);
return submenu
}
_getKeyboardListeners() {
return super._getKeyboardListeners().concat(this._visibleSubmenu)
}
_createSubmenu(node, $rootItem) {
const $submenuContainer = (0, _renderer.default)("<div>").addClass("dx-context-menu").appendTo($rootItem);
const items = this._getChildNodes(node);
const subMenu = this._createComponent($submenuContainer, _submenu.default, Object.assign({}, this._getSubmenuOptions(), {
_dataAdapter: this._dataAdapter,
_parentKey: node.internalFields.key,
items: items,
onHoverStart: this._clearTimeouts.bind(this),
position: this.getSubmenuPosition($rootItem)
}));
this._attachSubmenuHandlers($rootItem, subMenu);
return subMenu
}
_getSubmenuOptions() {
const $submenuTarget = (0, _renderer.default)("<div>");
const isMenuHorizontal = this._isMenuHorizontal();
const {
itemTemplate: itemTemplate,
orientation: orientation,
selectionMode: selectionMode,
cssClass: cssClass,
selectByClick: selectByClick,
hoverStateEnabled: hoverStateEnabled,
activeStateEnabled: activeStateEnabled,
focusStateEnabled: focusStateEnabled,
animation: animation,
showSubmenuMode: showSubmenuMode,
displayExpr: displayExpr,
disabledExpr: disabledExpr,
selectedExpr: selectedExpr,
itemsExpr: itemsExpr
} = this.option();
return {
itemTemplate: itemTemplate,
target: $submenuTarget,
orientation: orientation,
selectionMode: selectionMode,
cssClass: cssClass,
selectByClick: selectByClick,
hoverStateEnabled: hoverStateEnabled,
activeStateEnabled: activeStateEnabled,
focusStateEnabled: focusStateEnabled,
animation: animation,
showSubmenuMode: showSubmenuMode,
displayExpr: displayExpr,
disabledExpr: disabledExpr,
selectedExpr: selectedExpr,
itemsExpr: itemsExpr,
onFocusedItemChanged: e => {
const {
visible: visible,
focusedElement: focusedElement
} = e.component.option();
if (!visible) {
return
}
this.option("focusedElement", focusedElement)
},
onSelectionChanged: this._nestedItemOnSelectionChangedHandler.bind(this),
onItemClick: this._nestedItemOnItemClickHandler.bind(this),
onItemRendered: this._nestedItemOnItemRenderedHandler.bind(this),
onLeftFirstItem: isMenuHorizontal ? null : this._moveMainMenuFocus.bind(this, "prevItem"),
onLeftLastItem: isMenuHorizontal ? null : this._moveMainMenuFocus.bind(this, "nextItem"),
onCloseRootSubmenu: this._moveMainMenuFocus.bind(this, isMenuHorizontal ? "prevItem" : null),
onExpandLastSubmenu: isMenuHorizontal ? this._moveMainMenuFocus.bind(this, "nextItem") : null
}
}
_getShowFirstSubmenuMode() {
if (!this._isDesktopDevice()) {
return "onClick"
}
const {
showFirstSubmenuMode: optionValue
} = this.option();
return (0, _type.isObject)(optionValue) ? optionValue.name : optionValue
}
_moveMainMenuFocus(direction) {
const $items = this._getAvailableItems();
const itemCount = $items.length;
const $currentItem = $items.filter(`.${DX_MENU_ITEM_EXPANDED_CLASS}`).eq(0);
let itemIndex = $items.index($currentItem);
this._hideSubmenu(this._visibleSubmenu);
itemIndex += "prevItem" === direction ? -1 : 1;
if (itemIndex >= itemCount) {
itemIndex = 0
} else if (itemIndex < 0) {
itemIndex = itemCount - 1
}
const $newItem = $items.eq(itemIndex);
this.option("focusedElement", (0, _element.getPublicElement)($newItem))
}
_nestedItemOnSelectionChangedHandler(args) {
const selectedItem = args.addedItems.length && args.addedItems[0];
const submenu = _submenu.default.getInstance(args.element);
const {
onSelectionChanged: onSelectionChanged
} = this._actions;
null === onSelectionChanged || void 0 === onSelectionChanged || onSelectionChanged(args);
if (selectedItem) {
this._clearSelectionInSubmenus(submenu)
}
this._clearRootSelection();
this._setOptionWithoutOptionChange("selectedItem", selectedItem)
}
_clearSelectionInSubmenus(targetSubmenu) {
const cleanAllSubmenus = !arguments.length;
(0, _iterator.each)(this._submenus, ((_index, submenu) => {
const $submenu = submenu._itemContainer();
const isOtherItem = !$submenu.is(null === targetSubmenu || void 0 === targetSubmenu ? void 0 : targetSubmenu._itemContainer());
const $selectedItem = $submenu.find(`.${this._selectedItemClass()}`);
if (isOtherItem && $selectedItem.length || cleanAllSubmenus) {
$selectedItem.removeClass(this._selectedItemClass());
const selectedItemData = this._getItemData($selectedItem);
if (selectedItemData) {
selectedItemData.selected = false
}
submenu._clearSelectedItems()
}
}))
}
_clearRootSelection() {
const $prevSelectedItem = this.$element().find(".dx-menu-items-container").first().children().children().filter(`.${this._selectedItemClass()}`);
if ($prevSelectedItem.length) {
const prevSelectedItemData = this._getItemData($prevSelectedItem);
prevSelectedItemData.selected = false;
$prevSelectedItem.removeClass(this._selectedItemClass())
}
}
_nestedItemOnItemClickHandler(e) {
var _this$_actions$onItem, _this$_actions3;
null === (_this$_actions$onItem = (_this$_actions3 = this._actions).onItemClick) || void 0 === _this$_actions$onItem || _this$_actions$onItem.call(_this$_actions3, e)
}
_nestedItemOnItemRenderedHandler(e) {
var _this$_actions$onItem2, _this$_actions4;
null === (_this$_actions$onItem2 = (_this$_actions4 = this._actions).onItemRendered) || void 0 === _this$_actions$onItem2 || _this$_actions$onItem2.call(_this$_actions4, e)
}
_attachSubmenuHandlers($menuAnchorItem, submenu) {
const $submenuOverlayContent = submenu.getOverlayContent();
const submenus = null === $submenuOverlayContent || void 0 === $submenuOverlayContent ? void 0 : $submenuOverlayContent.find(".dx-submenu");
const submenuMouseLeaveName = (0, _utils.addNamespace)(_hover.end, `${this.NAME}_submenu`);
submenu.option({
onShowing: this._submenuOnShowingHandler.bind(this, $menuAnchorItem, submenu),
onShown: this._submenuOnShownHandler.bind(this, $menuAnchorItem, submenu),
onHiding: this._submenuOnHidingHandler.bind(this, $menuAnchorItem, submenu),
onHidden: this._submenuOnHiddenHandler.bind(this, $menuAnchorItem, submenu)
});
(0, _iterator.each)(submenus, ((_index, subMenu) => {
_events_engine.default.off(subMenu, submenuMouseLeaveName);
_events_engine.default.on(subMenu, submenuMouseLeaveName, null, this._submenuMouseLeaveHandler.bind(this, $menuAnchorItem))
}))
}
_submenuOnShowingHandler($menuAnchorItem, submenu, _ref) {
var _this$_actions$onSubm3, _this$_actions5;
let {
rootItem: rootItem
} = _ref;
const $border = $menuAnchorItem.children(".dx-context-menu-container-border");
const params = this._getVisibilityChangeEventParams(rootItem, submenu, $menuAnchorItem);
null === (_this$_actions$onSubm3 = (_this$_actions5 = this._actions).onSubmenuShowing) || void 0 === _this$_actions$onSubm3 || _this$_actions$onSubm3.call(_this$_actions5, params);
$border.show();
$menuAnchorItem.addClass(DX_MENU_ITEM_EXPANDED_CLASS)
}
_submenuOnShownHandler($menuAnchorItem, submenu, _ref2) {
var _this$_actions$onSubm4, _this$_actions6;
let {
rootItem: rootItem
} = _ref2;
const params = this._getVisibilityChangeEventParams(rootItem, submenu, $menuAnchorItem);
null === (_this$_actions$onSubm4 = (_this$_actions6 = this._actions).onSubmenuShown) || void 0 === _this$_actions$onSubm4 || _this$_actions$onSubm4.call(_this$_actions6, params)
}
_submenuOnHidingHandler($menuAnchorItem, submenu, eventArgs) {
var _this$_actions$onSubm5, _this$_actions7;
const $border = $menuAnchorItem.children(".dx-context-menu-container-border");
const params = this._getVisibilityChangeEventParams(eventArgs.rootItem, submenu, $menuAnchorItem, true);
eventArgs.itemData = params.itemData;
eventArgs.rootItem = params.rootItem;
eventArgs.submenuContainer = params.submenuContainer;
eventArgs.submenu = params.submenu;
null === (_this$_actions$onSubm5 = (_this$_actions7 = this._actions).onSubmenuHiding) || void 0 === _this$_actions$onSubm5 || _this$_actions$onSubm5.call(_this$_actions7, eventArgs);
const {
focusedElement: focusedElement
} = this.option();
const {
focusedElement: submenuFocusedElement
} = submenu.option();
const isVisibleSubmenuHiding = this._visibleSubmenu === submenu;
const isFocusedElementHiding = focusedElement === submenuFocusedElement;
if (isVisibleSubmenuHiding && isFocusedElementHiding) {
this.option("focusedElement", (0, _element.getPublicElement)($menuAnchorItem))
}
if (!eventArgs.cancel) {
if (isVisibleSubmenuHiding) {
this._visibleSubmenu = null
}
$border.hide();
$menuAnchorItem.removeClass(DX_MENU_ITEM_EXPANDED_CLASS)
}
}
_submenuOnHiddenHandler($menuAnchorItem, submenu, _ref3) {
var _this$_actions$onSubm6, _this$_actions8;
let {
rootItem: rootItem
} = _ref3;
const params = this._getVisibilityChangeEventParams(rootItem, submenu, $menuAnchorItem, true);
null === (_this$_actions$onSubm6 = (_this$_actions8 = this._actions).onSubmenuHidden) || void 0 === _this$_actions$onSubm6 || _this$_actions$onSubm6.call(_this$_actions8, params)
}
_getVisibilityChangeEventParams(submenuItem, submenu, $menuAnchorItem, isHide) {
let itemData;
let $submenuContainer;
if (submenuItem) {
const anchor = isHide ? (0, _renderer.default)(submenuItem).closest(`.${DX_MENU_ITEM_CLASS}`)[0] : submenuItem;
itemData = this._getItemData(anchor);
$submenuContainer = (0, _renderer.default)(anchor).find(".dx-submenu").first()
} else {
var _submenu$_overlay;
const $overlayContent = (0, _renderer.default)(null === (_submenu$_overlay = submenu._overlay) || void 0 === _submenu$_overlay ? void 0 : _submenu$_overlay.content());
itemData = this._getItemData($menuAnchorItem);
$submenuContainer = $overlayContent.find(".dx-submenu").first()
}
return {
itemData: itemData,
rootItem: (0, _element.getPublicElement)($menuAnchorItem),
submenuContainer: (0, _element.getPublicElement)($submenuContainer),
submenu: submenu
}
}
_submenuMouseLeaveHandler($rootItem, eventArgs) {
var _submenu$getOverlayCo;
const target = (0, _renderer.default)(eventArgs.relatedTarget).parents(".dx-context-menu")[0];
const submenu = this._getSubmenuByRootElement($rootItem);
const contextMenu = null === submenu || void 0 === submenu || null === (_submenu$getOverlayCo = submenu.getOverlayContent()) || void 0 === _submenu$getOverlayCo ? void 0 : _submenu$getOverlayCo[0];
const {
hideSubmenuOnMouseLeave: hideSubmenuOnMouseLeave
} = this.option();
if (hideSubmenuOnMouseLeave && target !== contextMenu) {
this._clearTimeouts();
setTimeout(this._hideSubmenuAfterTimeout.bind(this), this._getDelay("hide"))
}
}
_hideSubmenuAfterTimeout() {
var _this$_visibleSubmenu, _this$_visibleSubmenu2;
if (!this._visibleSubmenu) {
return
}
const isRootItemHovered = (0, _renderer.default)(this._visibleSubmenu.$element().context).hasClass("dx-state-hover");
const isSubmenuItemHovered = null === (_this$_visibleSubmenu = this._visibleSubmenu.getOverlayContent()) || void 0 === _this$_visibleSubmenu ? void 0 : _this$_visibleSubmenu.find(".dx-state-hover").length;
const hoveredElementFromSubMenu = null === (_this$_visibleSubmenu2 = this._visibleSubmenu.getOverlayContent()) || void 0 === _this$_visibleSubmenu2 ? void 0 : _this$_visibleSubmenu2.get(0).querySelector(":hover");
if (!hoveredElementFromSubMenu && !isSubmenuItemHovered && !isRootItemHovered) {
this._visibleSubmenu.hide()
}
}
_getSubmenuByRootElement($rootItem) {
if (!$rootItem) {
return
}
const $submenu = $rootItem.children(".dx-context-menu");
if (!$submenu.length) {
return
}
return _submenu.default.getInstance($submenu)
}
getSubmenuPosition($rootItem) {
const {
submenuDirection: submenuDirectionOption,
rtlEnabled: rtlEnabled
} = this.option();
const isHorizontalMenu = this._isMenuHorizontal();
const submenuDirection = null === submenuDirectionOption || void 0 === submenuDirectionOption ? void 0 : submenuDirectionOption.toLowerCase();
const submenuPosition = {
collision: "flip",
of: $rootItem,
precise: true
};
switch (submenuDirection) {
case "leftortop":
submenuPosition.at = "left top";
submenuPosition.my = isHorizontalMenu ? "left bottom" : "right top";
break;
case "rightorbottom":
submenuPosition.at = isHorizontalMenu ? "left bottom" : "right top";
submenuPosition.my = "left top";
break;
default:
if (isHorizontalMenu) {
submenuPosition.at = rtlEnabled ? "right bottom" : "left bottom";
submenuPosition.my = rtlEnabled ? "right top" : "left top"
} else {
submenuPosition.at = rtlEnabled ? "left top" : "right top";
submenuPosition.my = rtlEnabled ? "right top" : "left top"
}
}
return submenuPosition
}
_renderBorderElement($item) {
(0, _renderer.default)("<div>").appendTo($item).addClass("dx-context-menu-container-border").hide()
}
_itemPointerHandler(e) {
const $target = (0, _renderer.default)(e.target);
const $closestItem = $target.closest(this._itemElements());
if ($closestItem.hasClass("dx-menu-item-has-submenu")) {
this.option("focusedElement", null);
return
}
super._itemPointerHandler(e)
}
_hoverStartHandler(e) {
const mouseMoveEventName = (0, _utils.addNamespace)(_pointer.default.move, this.NAME);
const $item = this._getItemElementByEventArgs(e);
if (!$item || this._isItemDisabled($item)) {
return
}
const node = this._dataAdapter.getNodeByItem(this._getItemData($item));
const isSelectionActive = (0, _type.isDefined)(e.buttons) && 1 === e.buttons || !(0, _type.isDefined)(e.buttons) && 1 === e.which;
_events_engine.default.off($item, mouseMoveEventName);
if (!this._hasChildren(node)) {
this._showSubmenuTimer = setTimeout(this._hideSubmenuAfterTimeout.bind(this), this._getDelay("hide"));
return
}
if ("onHover" === this._getShowFirstSubmenuMode() && !isSelectionActive) {
const submenu = this._getSubmenuByElement($item);
this._clearTimeouts();
if (!(null !== submenu && void 0 !== submenu && submenu.isOverlayVisible())) {
_events_engine.default.on($item, mouseMoveEventName, this._itemMouseMoveHandler.bind(this));
this._showSubmenuTimer = this._getDelay("hide")
}
}
}
_hoverEndHandler(eventArg) {
const $item = this._getItemElementByEventArgs(eventArg);
const relatedTarget = (0, _renderer.default)(eventArg.relatedTarget);
super._hoverEndHandler(eventArg);
this._clearTimeouts();
if (!$item || this._isItemDisabled($item)) {
return
}
if (relatedTarget.hasClass("dx-context-menu-content-delimiter")) {
return
}
const {
hideSubmenuOnMouseLeave: hideSubmenuOnMouseLeave
} = this.option();
if (hideSubmenuOnMouseLeave && !relatedTarget.hasClass("dx-menu-items-container")) {
this._hideSubmenuTimer = setTimeout((() => {
this._hideSubmenuAfterTimeout()
}), this._getDelay("hide"))
}
}
_hideVisibleSubmenu() {
if (!this._visibleSubmenu) {
return false
}
this._hideSubmenu(this._visibleSubmenu);
return true
}
_showSubmenu($itemElement) {
const submenu = this._getSubmenuByElement($itemElement);
if (this._visibleSubmenu !== submenu) {
this._hideVisibleSubmenu()
}
if (submenu) {
this._clearTimeouts();
this.focus();
submenu.show();
const {
focusedElement: focusedElement
} = submenu.option();
this.option("focusedElement", focusedElement)
}
this._visibleSubmenu = submenu;
this._hoveredRootItem = $itemElement
}
_hideSubmenu(submenu) {
if (submenu) {
submenu.hide()
}
if (this._visibleSubmenu === submenu) {
this._visibleSubmenu = null
}
this._hoveredRootItem = null
}
_itemMouseMoveHandler(e) {
var _e$pointers;
if (null !== (_e$pointers = e.pointers) && void 0 !== _e$pointers && _e$pointers.length) {
return
}
const $item = (0, _renderer.default)(e.currentTarget);
if (!(0, _type.isDefined)(this._showSubmenuTimer)) {
return
}
this._clearTimeouts();
this._showSubmenuTimer = setTimeout((() => {
const submenu = this._getSubmenuByElement($item);
if (submenu && !submenu.isOverlayVisible()) {
this._showSubmenu($item)
}
}), this._getDelay("show"))
}
_clearTimeouts() {
clearTimeout(this._hideSubmenuTimer);
clearTimeout(this._showSubmenuTimer)
}
_getSubmenuByElement($itemElement, itemData) {
const submenu = this._getSubmenuByRootElement($itemElement);
if (submenu) {
return submenu
}
const node = this._dataAdapter.getNodeByItem(itemData ?? this._getItemData($itemElement));
if (node && this._hasChildren(node)) {
return this._renderSubmenuItems(node, $itemElement)
}
return
}
_updateSubmenuVisibilityOnClick(actionArgs) {
var _actionArgs$args;
const args = null === (_actionArgs$args = actionArgs.args) || void 0 === _actionArgs$args ? void 0 : _actionArgs$args[0];
if (!args || this._disabledGetter(args.itemData)) {
return
}
const $itemElement = (0, _renderer.default)(args.itemElement);
const currentSubmenu = this._getSubmenuByElement($itemElement, args.itemData);
this._updateSelectedItemOnClick(actionArgs);
if (this._visibleSubmenu) {
if (this._visibleSubmenu === currentSubmenu) {
const {
showFirstSubmenuMode: showFirstSubmenuMode
} = this.option();
if ("onClick" === showFirstSubmenuMode) {
this._hideSubmenu(this._visibleSubmenu)
}
return
}
this._hideSubmenu(this._visibleSubmenu)
}
if (!currentSubmenu) {
return
}
if (!currentSubmenu.isOverlayVisible()) {
this._showSubmenu($itemElement)
}
}
_optionChanged(args) {
if (ACTIONS.includes(args.name)) {
this._initActions();
return
}
switch (args.name) {
case "orientation":
case "submenuDirection":
this._invalidate();
break;
case "showFirstSubmenuMode":
case "hideSubmenuOnMouseLeave":
break;
case "showSubmenuMode":
this._changeSubmenusOption(args);
break;
case "adaptivityEnabled":
if (args.value) {
this._initAdaptivity()
} else {
this._removeAdaptivity()
}
break;
case "width":
if (this._isAdaptivityEnabled()) {
var _this$_treeView3, _this$_overlay5;
null === (_this$_treeView3 = this._treeView) || void 0 === _this$_treeView3 || _this$_treeView3.option(args.name, args.value);
null === (_this$_overlay5 = this._overlay) || void 0 === _this$_overlay5 || _this$_overlay5.option(args.name, args.value)
}
super._optionChanged(args);
this._dimensionChanged();
break;
case "animation":
if (this._isAdaptivityEnabled()) {
var _this$_treeView4;
null === (_this$_treeView4 = this._treeView) || void 0 === _this$_treeView4 || _this$_treeView4.option("animationEnabled", !!args.value)
}
super._optionChanged(args);
break;
default:
if (this._isAdaptivityEnabled() && (args.name === args.fullName || "items" === args.name)) {
var _this$_treeView5;
null === (_this$_treeView5 = this._treeView) || void 0 === _this$_treeView5 || _this$_treeView5.option(args.fullName, args.value)
}
super._optionChanged(args)
}
}
_changeSubmenusOption(_ref4) {
let {
name: name,
value: value
} = _ref4;
(0, _iterator.each)(this._submenus, ((_index, submenu) => {
submenu.option(name, value)
}))
}
selectItem(itemElement) {
this._hideSubmenu(this._visibleSubmenu);
super.selectItem(itemElement)
}
unselectItem(itemElement) {
this._hideSubmenu(this._visibleSubmenu);
super.unselectItem(itemElement)
}
}(0, _component_registrator.default)("dxMenu", Menu);
var _default = exports.default = Menu;