UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,037 lines (859 loc) • 36.7 kB
"use strict"; var $ = require("../../core/renderer"), eventsEngine = require("../../events/core/events_engine"), registerComponent = require("../../core/component_registrator"), commonUtils = require("../../core/utils/common"), getPublicElement = require("../../core/utils/dom").getPublicElement, each = require("../../core/utils/iterator").each, typeUtils = require("../../core/utils/type"), extend = require("../../core/utils/extend").extend, eventUtils = require("../../events/utils"), pointerEvents = require("../../events/pointer"), hoverEvents = require("../../events/hover"), MenuBase = require("../context_menu/ui.menu_base"), Overlay = require("../overlay"), Submenu = require("./ui.submenu"), Button = require("../button"), TreeView = require("../tree_view"); var DX_MENU_CLASS = "dx-menu", DX_MENU_VERTICAL_CLASS = DX_MENU_CLASS + "-vertical", DX_MENU_HORIZONTAL_CLASS = DX_MENU_CLASS + "-horizontal", DX_MENU_ITEM_CLASS = DX_MENU_CLASS + "-item", DX_MENU_ITEMS_CONTAINER_CLASS = DX_MENU_CLASS + "-items-container", DX_MENU_ITEM_EXPANDED_CLASS = DX_MENU_ITEM_CLASS + "-expanded", DX_CONTEXT_MENU_CLASS = "dx-context-menu", DX_CONTEXT_MENU_CONTAINER_BORDER_CLASS = DX_CONTEXT_MENU_CLASS + "-container-border", DX_CONTEXT_MENU_CONTENT_DELIMITER_CLASS = "dx-context-menu-content-delimiter", DX_SUBMENU_CLASS = "dx-submenu", DX_STATE_DISABLED_CLASS = "dx-state-disabled", DX_STATE_HOVER_CLASS = "dx-state-hover", DX_STATE_ACTIVE_CLASS = "dx-state-active", DX_ADAPTIVE_MODE_CLASS = DX_MENU_CLASS + "-adaptive-mode", DX_ADAPTIVE_HAMBURGER_BUTTON_CLASS = DX_MENU_CLASS + "-hamburger-button", FOCUS_UP = "up", FOCUS_DOWN = "down", FOCUS_LEFT = "left", FOCUS_RIGHT = "right", SHOW_SUBMENU_OPERATION = "showSubmenu", NEXTITEM_OPERATION = "nextItem", PREVITEM_OPERATION = "prevItem", DEFAULT_DELAY = { "show": 50, "hide": 300 }, ACTIONS = ["onSubmenuShowing", "onSubmenuShown", "onSubmenuHiding", "onSubmenuHidden", "onItemContextMenu", "onItemClick", "onSelectionChanged"]; var Menu = MenuBase.inherit({ _getDefaultOptions: function _getDefaultOptions() { return extend(this.callBase(), { /** * @name dxMenuOptions.items * @publicName items * @type Array<dxMenuItemTemplate> * @inheritdoc */ /** * @name dxMenuOptions.orientation * @publicName orientation * @type Enums.Orientation * @default "horizontal" */ orientation: "horizontal", /** * @name dxMenuOptions.submenuDirection * @publicName submenuDirection * @type Enums.SubmenuDirection * @default "auto" */ submenuDirection: "auto", /** * @name dxMenuOptions.showFirstSubmenuMode * @publicName showFirstSubmenuMode * @type Object|Enums.ShowSubmenuMode * @default { name: "onClick", delay: { show: 50, hide: 300 } } */ showFirstSubmenuMode: { /** * @name dxMenuOptions.showFirstSubmenuMode.name * @publicName name * @type Enums.ShowSubmenuMode * @default "onClick" */ name: "onClick", /** * @name dxMenuOptions.showFirstSubmenuMode.delay * @publicName delay * @type Object|number * @default { show: 50, hide: 300 } */ delay: { /** * @name dxMenuOptions.showFirstSubmenuMode.delay.show * @publicName show * @type number * @default 50 */ show: 50, /** * @name dxMenuOptions.showFirstSubmenuMode.delay.hide * @publicName hide * @type number * @default 300 */ hide: 300 } }, /** * @name dxMenuOptions.hideSubmenuOnMouseLeave * @publicName hideSubmenuOnMouseLeave * @type boolean * @default false */ hideSubmenuOnMouseLeave: false, /** * @name dxMenuOptions.onSubmenuShowing * @publicName onSubmenuShowing * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 rootItem:dxElement * @action */ onSubmenuShowing: null, /** * @name dxMenuOptions.onSubmenuShown * @publicName onSubmenuShown * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 rootItem:dxElement * @action */ onSubmenuShown: null, /** * @name dxMenuOptions.onSubmenuHiding * @publicName onSubmenuHiding * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 rootItem:dxElement * @type_function_param1_field5 cancel:boolean * @action */ onSubmenuHiding: null, /** * @name dxMenuOptions.onSubmenuHidden * @publicName onSubmenuHidden * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 rootItem:dxElement * @action */ onSubmenuHidden: null, /** * @name dxMenuOptions.adaptivityEnabled * @publicName adaptivityEnabled * @type boolean * @default false */ adaptivityEnabled: false /** * @name dxMenuOptions.selectedItems * @publicName selectedItems * @hidden * @inheritdoc */ /** * @name dxMenuOptions.onSelectionChange * @publicName onSelectionChange * @hidden * @action * @inheritdoc */ /** * @name dxMenuOptions.onItemReordered * @publicName onItemReordered * @hidden * @inheritdoc */ /** * @name dxMenuItemTemplate * @publicName dxMenuItemTemplate * @inherits dxMenuBaseItemTemplate * @type object * @inheritdoc */ /** * @name dxMenuItemTemplate.items * @publicName items * @type Array<dxMenuItemTemplate> * @inheritdoc */ }); }, _setOptionsByReference: function _setOptionsByReference() { this.callBase(); extend(this._optionsByReference, { animation: true, selectedItem: true }); }, _itemElements: function _itemElements() { var rootMenuElements = this.callBase(), submenuElements = this._submenuItemElements(); return rootMenuElements.add(submenuElements); }, _submenuItemElements: function _submenuItemElements() { var elements = [], itemSelector = "." + DX_MENU_ITEM_CLASS, currentSubmenu = this._submenus.length && this._submenus[0]; if (currentSubmenu && currentSubmenu.itemsContainer()) { elements = currentSubmenu.itemsContainer().find(itemSelector); } return elements; }, _focusTarget: function _focusTarget() { return this.$element(); }, _isMenuHorizontal: function _isMenuHorizontal() { return this.option("orientation") === "horizontal"; }, _moveFocus: function _moveFocus(location) { var $items = this._getAvailableItems(), isMenuHorizontal = this._isMenuHorizontal(), argument, $activeItem = this._getActiveItem(true), operation, navigationAction, $newTarget; switch (location) { case FOCUS_UP: operation = isMenuHorizontal ? SHOW_SUBMENU_OPERATION : this._getItemsNavigationOperation(PREVITEM_OPERATION); argument = isMenuHorizontal ? $activeItem : $items; navigationAction = this._getKeyboardNavigationAction(operation, argument); $newTarget = navigationAction(); break; case FOCUS_DOWN: operation = isMenuHorizontal ? SHOW_SUBMENU_OPERATION : this._getItemsNavigationOperation(NEXTITEM_OPERATION); argument = isMenuHorizontal ? $activeItem : $items; navigationAction = this._getKeyboardNavigationAction(operation, argument); $newTarget = navigationAction(); break; case FOCUS_RIGHT: operation = isMenuHorizontal ? this._getItemsNavigationOperation(NEXTITEM_OPERATION) : SHOW_SUBMENU_OPERATION; argument = isMenuHorizontal ? $items : $activeItem; navigationAction = this._getKeyboardNavigationAction(operation, argument); $newTarget = navigationAction(); break; case FOCUS_LEFT: operation = isMenuHorizontal ? this._getItemsNavigationOperation(PREVITEM_OPERATION) : SHOW_SUBMENU_OPERATION; argument = isMenuHorizontal ? $items : $activeItem; navigationAction = this._getKeyboardNavigationAction(operation, argument); $newTarget = navigationAction(); break; default: return this.callBase(location); } if ($newTarget && $newTarget.length !== 0) { this.option("focusedElement", getPublicElement($newTarget)); } }, _getItemsNavigationOperation: function _getItemsNavigationOperation(operation) { var navOperation = operation; if (this.option("rtlEnabled")) { navOperation = operation === PREVITEM_OPERATION ? NEXTITEM_OPERATION : PREVITEM_OPERATION; } return navOperation; }, _getKeyboardNavigationAction: function _getKeyboardNavigationAction(operation, argument) { var action = commonUtils.noop; switch (operation) { case SHOW_SUBMENU_OPERATION: if (!argument.hasClass(DX_STATE_DISABLED_CLASS)) { action = this._showSubmenu.bind(this, argument); } break; case NEXTITEM_OPERATION: action = this._nextItem.bind(this, argument); break; case PREVITEM_OPERATION: action = this._prevItem.bind(this, argument); break; } return action; }, _clean: function _clean() { this.callBase(); this.option("templatesRenderAsynchronously") && clearTimeout(this._resizeEventTimer); }, _visibilityChanged: function _visibilityChanged(visible) { if (visible) { this._dimensionChanged(); } }, _isAdaptivityEnabled: function _isAdaptivityEnabled() { return this.option("adaptivityEnabled") && this.option("orientation") === "horizontal"; }, _dimensionChanged: function _dimensionChanged() { if (!this._isAdaptivityEnabled()) { return; } var $menuItems = this.$element().find("ul").first().children("li").children("." + DX_MENU_ITEM_CLASS), menuItemsWidth = 0, containerWidth = this.$element().outerWidth(); $menuItems.each(function (_, menuItem) { menuItemsWidth += $(menuItem).outerWidth(true); }); this._toggleAdaptiveMode(menuItemsWidth > containerWidth); }, _init: function _init() { this.callBase(); this._submenus = []; }, _initActions: function _initActions() { this._actions = {}; each(ACTIONS, function (index, action) { this._actions[action] = this._createActionByOption(action); }.bind(this)); }, _initMarkup: function _initMarkup() { this._visibleSubmenu = null; this.$element().addClass(DX_MENU_CLASS); this.callBase(); this.setAria("role", "menubar"); }, _render: function _render() { this.callBase(); this._initAdaptivity(); }, _renderHamburgerButton: function _renderHamburgerButton() { this._hamburger = new Button($("<div>").addClass(DX_ADAPTIVE_HAMBURGER_BUTTON_CLASS), { icon: 'menu', activeStateEnabled: false, onClick: this._toggleTreeView.bind(this) }); return this._hamburger.$element(); }, _toggleTreeView: function _toggleTreeView(state) { if (typeUtils.isPlainObject(state)) { state = !this._overlay.option("visible"); } this._overlay.option("visible", state); this._toggleHamburgerActiveState(state); }, _toggleHamburgerActiveState: function _toggleHamburgerActiveState(state) { this._hamburger && this._hamburger.$element().toggleClass(DX_STATE_ACTIVE_CLASS, state); }, _toggleAdaptiveMode: function _toggleAdaptiveMode(state) { var $menuItemsContainer = this.$element().find("." + DX_MENU_HORIZONTAL_CLASS), $adaptiveElements = this.$element().find("." + DX_ADAPTIVE_MODE_CLASS); if (state) { this._hideVisibleSubmenu(); } else { this._treeView && this._treeView.collapseAll(); this._overlay && this._toggleTreeView(state); } $menuItemsContainer.toggle(!state); $adaptiveElements.toggle(state); }, _removeAdaptivity: function _removeAdaptivity() { if (!this._$adaptiveContainer) { return; } this._toggleAdaptiveMode(false); this._$adaptiveContainer.remove(); this._$adaptiveContainer = null; this._treeView = null; this._hamburger = null; this._overlay = null; }, _treeviewItemClickHandler: function _treeviewItemClickHandler(e) { this._actions["onItemClick"](e); if (!e.node.children.length) { this._toggleTreeView(false); } }, _getAdaptiveOverlayOptions: function _getAdaptiveOverlayOptions() { var rtl = this.option("rtlEnabled"), position = rtl ? "right" : "left"; return { deferRendering: false, shading: false, animation: false, closeOnTargetScroll: true, onHidden: function () { this._toggleHamburgerActiveState(false); }.bind(this), height: "auto", closeOnOutsideClick: function closeOnOutsideClick(e) { return !!!$(e.target).closest("." + DX_ADAPTIVE_HAMBURGER_BUTTON_CLASS).length; }, position: { collision: "flipfit", at: "bottom " + position, my: "top " + position, of: this._hamburger.$element() } }; }, _getTreeViewOptions: function _getTreeViewOptions() { var menuOptions = {}, that = this, optionsToTransfer = ["rtlEnabled", "width", "accessKey", "activeStateEnabled", "animation", "dataSource", "disabled", "displayExpr", "displayExpr", "focusStateEnabled", "hint", "hoverStateEnabled", "itemsExpr", "items", "itemTemplate", "selectedExpr", "selectionMode", "tabIndex", "visible"], actionsToTransfer = ["onItemContextMenu", "onSelectionChanged"]; each(optionsToTransfer, function (_, option) { menuOptions[option] = that.option(option); }); each(actionsToTransfer, function (_, actionName) { menuOptions[actionName] = function (e) { this._actions[actionName](e); }.bind(that); }); return extend(menuOptions, { animationEnabled: !!this.option("animation"), onItemClick: that._treeviewItemClickHandler.bind(that), onItemExpanded: function (e) { this._overlay.repaint(); this._actions["onSubmenuShown"](e); }.bind(that), onItemCollapsed: function (e) { this._overlay.repaint(); this._actions["onSubmenuHidden"](e); }.bind(that), selectNodesRecursive: false, selectByClick: this.option("selectByClick"), expandEvent: "click" }); }, _initAdaptivity: function _initAdaptivity() { if (!this._isAdaptivityEnabled()) return; this._$adaptiveContainer = $("<div>").addClass(DX_ADAPTIVE_MODE_CLASS); var $hamburger = this._renderHamburgerButton(); this._treeView = this._createComponent($("<div>"), TreeView, this._getTreeViewOptions()); this._overlay = this._createComponent($("<div>"), Overlay, this._getAdaptiveOverlayOptions()); this._overlay.$content().append(this._treeView.$element()).addClass(DX_ADAPTIVE_MODE_CLASS).addClass(this.option("cssClass")); this._$adaptiveContainer.append($hamburger); this._$adaptiveContainer.append(this._overlay.$element()); this.$element().append(this._$adaptiveContainer); this._dimensionChanged(); }, _getDelay: function _getDelay(delayType) { var delay = this.option("showFirstSubmenuMode").delay; if (!typeUtils.isDefined(delay)) { return DEFAULT_DELAY[delayType]; } else { return typeUtils.isObject(delay) ? delay[delayType] : delay; } }, _renderContainer: function _renderContainer() { var $wrapper = $("<div>"); $wrapper.appendTo(this.$element()).addClass(this._isMenuHorizontal() ? DX_MENU_HORIZONTAL_CLASS : DX_MENU_VERTICAL_CLASS); return this.callBase($wrapper); }, _renderSubmenuItems: function _renderSubmenuItems(node, $itemFrame) { var submenu = this._createSubmenu(node, $itemFrame); this._submenus.push(submenu); this._renderBorderElement($itemFrame); return submenu; }, _createSubmenu: function _createSubmenu(node, $rootItem) { var $submenuContainer = $("<div>").addClass(DX_CONTEXT_MENU_CLASS).appendTo($rootItem); var items = this._getChildNodes(node), result = this._createComponent($submenuContainer, Submenu, extend(this._getSubmenuOptions(), { _dataAdapter: this._dataAdapter, _parentKey: node.internalFields.key, items: items, onHoverStart: this._clearTimeouts.bind(this), position: this.getSubmenuPosition($rootItem) })); this._attachSubmenuHandlers($rootItem, result); return result; }, _getSubmenuOptions: function _getSubmenuOptions() { var $submenuTarget = $("<div>"), isMenuHorizontal = this._isMenuHorizontal(); return { itemTemplate: this.option("itemTemplate"), target: $submenuTarget, orientation: this.option("orientation"), selectionMode: this.option("selectionMode"), cssClass: this.option("cssClass"), selectByClick: this.option("selectByClick"), hoverStateEnabled: this.option("hoverStateEnabled"), activeStateEnabled: this.option("activeStateEnabled"), focusStateEnabled: this.option("focusStateEnabled"), animation: this.option("animation"), showSubmenuMode: this.option("showSubmenuMode"), displayExpr: this.option("displayExpr"), disabledExpr: this.option("disabledExpr"), selectedExpr: this.option("selectedExpr"), itemsExpr: this.option("itemsExpr"), onSelectionChanged: this._nestedItemOnSelectionChangedHandler.bind(this), onItemClick: this._nestedItemOnItemClickHandler.bind(this), onItemRendered: this.option("onItemRendered"), onLeftFirstItem: isMenuHorizontal ? null : this._moveMainMenuFocus.bind(this, PREVITEM_OPERATION), onLeftLastItem: isMenuHorizontal ? null : this._moveMainMenuFocus.bind(this, NEXTITEM_OPERATION), onCloseRootSubmenu: this._moveMainMenuFocus.bind(this, isMenuHorizontal ? PREVITEM_OPERATION : null), onExpandLastSubmenu: isMenuHorizontal ? this._moveMainMenuFocus.bind(this, NEXTITEM_OPERATION) : null }; }, _getShowFirstSubmenuMode: function _getShowFirstSubmenuMode() { if (!this._isDesktopDevice()) { return "onClick"; } var optionValue = this.option("showFirstSubmenuMode"); return typeUtils.isObject(optionValue) ? optionValue.name : optionValue; }, _moveMainMenuFocus: function _moveMainMenuFocus(direction) { var $items = this._getAvailableItems(), itemCount = $items.length, $currentItem = $items.filter("." + DX_MENU_ITEM_EXPANDED_CLASS).eq(0), itemIndex = $items.index($currentItem); itemIndex += direction === PREVITEM_OPERATION ? -1 : 1; if (itemIndex >= itemCount) { itemIndex = 0; } else if (itemIndex < 0) { itemIndex = itemCount - 1; } var $newItem = $items.eq(itemIndex); this._hideSubmenu(this._visibleSubmenu); this.focus(); this.option("focusedElement", getPublicElement($newItem)); }, _nestedItemOnSelectionChangedHandler: function _nestedItemOnSelectionChangedHandler(args) { var selectedItem = args.addedItems.length && args.addedItems[0], submenu = Submenu.getInstance(args.element), onSelectionChanged = this._actions["onSelectionChanged"]; onSelectionChanged(args); selectedItem && this._clearSelectionInSubmenus(selectedItem[0], submenu); this._clearRootSelection(); this._setOptionSilent("selectedItem", selectedItem); }, _clearSelectionInSubmenus: function _clearSelectionInSubmenus(item, targetSubmenu) { var that = this, cleanAllSubmenus = !arguments.length; each(this._submenus, function (index, submenu) { var $submenu = submenu._itemContainer(), isOtherItem = !$submenu.is(targetSubmenu && targetSubmenu._itemContainer()), $selectedItem = $submenu.find("." + that._selectedItemClass()); if (isOtherItem && $selectedItem.length || cleanAllSubmenus) { var selectedItemData; $selectedItem.removeClass(that._selectedItemClass()); selectedItemData = that._getItemData($selectedItem); if (selectedItemData) { selectedItemData.selected = false; } submenu._clearSelectedItems(); } }); }, _clearRootSelection: function _clearRootSelection() { var $prevSelectedItem = this.$element().find("." + DX_MENU_ITEMS_CONTAINER_CLASS).first().children().children().filter("." + this._selectedItemClass()); if ($prevSelectedItem.length) { var prevSelectedItemData; prevSelectedItemData = this._getItemData($prevSelectedItem); prevSelectedItemData.selected = false; $prevSelectedItem.removeClass(this._selectedItemClass()); } }, _nestedItemOnItemClickHandler: function _nestedItemOnItemClickHandler(e) { this._actions["onItemClick"](e); }, _attachSubmenuHandlers: function _attachSubmenuHandlers($rootItem, submenu) { var that = this, $submenuOverlayContent = submenu.getOverlayContent(), submenus = $submenuOverlayContent.find("." + DX_SUBMENU_CLASS), submenuMouseLeaveName = eventUtils.addNamespace(hoverEvents.end, this.NAME + "_submenu"); submenu.option({ onShowing: this._submenuOnShowingHandler.bind(this, $rootItem, submenu), onShown: this._submenuOnShownHandler.bind(this, $rootItem, submenu), onHiding: this._submenuOnHidingHandler.bind(this, $rootItem, submenu), onHidden: this._submenuOnHiddenHandler.bind(this, $rootItem, submenu) }); each(submenus, function (index, submenu) { eventsEngine.off(submenu, submenuMouseLeaveName); eventsEngine.on(submenu, submenuMouseLeaveName, null, that._submenuMouseLeaveHandler.bind(that, $rootItem)); }); }, _submenuOnShowingHandler: function _submenuOnShowingHandler($rootItem, submenu) { var $border = $rootItem.children("." + DX_CONTEXT_MENU_CONTAINER_BORDER_CLASS); this._actions.onSubmenuShowing({ rootItem: getPublicElement($rootItem), submenu: submenu }); $border.show(); $rootItem.addClass(DX_MENU_ITEM_EXPANDED_CLASS); }, _submenuOnShownHandler: function _submenuOnShownHandler($rootItem, submenu) { this._actions.onSubmenuShown({ rootItem: getPublicElement($rootItem), submenu: submenu }); }, _submenuOnHidingHandler: function _submenuOnHidingHandler($rootItem, submenu, eventArgs) { var $border = $rootItem.children("." + DX_CONTEXT_MENU_CONTAINER_BORDER_CLASS), args = eventArgs; args.rootItem = getPublicElement($rootItem); args.submenu = submenu; this._actions.onSubmenuHiding(args); eventArgs = args; if (!eventArgs.cancel) { if (this._visibleSubmenu === submenu) this._visibleSubmenu = null; $border.hide(); $rootItem.removeClass(DX_MENU_ITEM_EXPANDED_CLASS); } }, _submenuOnHiddenHandler: function _submenuOnHiddenHandler($rootItem, submenu) { this._actions.onSubmenuHidden({ rootItem: getPublicElement($rootItem), submenu: submenu }); }, _submenuMouseLeaveHandler: function _submenuMouseLeaveHandler($rootItem, eventArgs) { var that = this, target = $(eventArgs.relatedTarget).parents("." + DX_CONTEXT_MENU_CLASS)[0], contextMenu = that._getSubmenuByRootElement($rootItem).getOverlayContent()[0]; if (that.option("hideSubmenuOnMouseLeave") && target !== contextMenu) { that._clearTimeouts(); setTimeout(that._hideSubmenuAfterTimeout.bind(that), that._getDelay("hide")); } }, _hideSubmenuAfterTimeout: function _hideSubmenuAfterTimeout() { if (!this._visibleSubmenu) { return; } var isRootItemHovered = $(this._visibleSubmenu.$element().context).hasClass(DX_STATE_HOVER_CLASS), isSubmenuItemHovered = this._visibleSubmenu.getOverlayContent().find("." + DX_STATE_HOVER_CLASS).length; if (!isSubmenuItemHovered && !isRootItemHovered) { this._visibleSubmenu.hide(); } }, _getSubmenuByRootElement: function _getSubmenuByRootElement($rootItem) { if (!$rootItem) { return false; } var $submenu = $rootItem.children("." + DX_CONTEXT_MENU_CLASS); return $submenu.length && Submenu.getInstance($submenu); }, getSubmenuPosition: function getSubmenuPosition($rootItem) { var isHorizontalMenu = this._isMenuHorizontal(), submenuDirection = this.option("submenuDirection").toLowerCase(), rtlEnabled = this.option("rtlEnabled"), submenuPosition = { collision: "flip", of: $rootItem }; 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"; } break; } return submenuPosition; }, _renderBorderElement: function _renderBorderElement($item) { $("<div>").appendTo($item).addClass(DX_CONTEXT_MENU_CONTAINER_BORDER_CLASS).hide(); }, _itemPointerDownHandler: function _itemPointerDownHandler(e) { var $target = $(e.target), $closestItem = $target.closest(this._itemElements()); if ($closestItem.hasClass("dx-menu-item-has-submenu")) { this.option("focusedElement", null); return; } this.callBase(e); }, _hoverStartHandler: function _hoverStartHandler(e) { var mouseMoveEventName = eventUtils.addNamespace(pointerEvents.move, this.NAME), $item = this._getItemElementByEventArgs(e), node = this._dataAdapter.getNodeByItem(this._getItemData($item)), isSelectionActive = typeUtils.isDefined(e.buttons) && e.buttons === 1 || !typeUtils.isDefined(e.buttons) && e.which === 1; if (this._isItemDisabled($item)) { return; } eventsEngine.off($item, mouseMoveEventName); if (!this._hasChildren(node)) { this._showSubmenuTimer = setTimeout(this._hideSubmenuAfterTimeout.bind(this), this._getDelay("hide")); return; } if (this._getShowFirstSubmenuMode() === "onHover" && !isSelectionActive) { var submenu = this._getSubmenuByElement($item); this._clearTimeouts(); if (!submenu.isOverlayVisible()) { eventsEngine.on($item, mouseMoveEventName, this._itemMouseMoveHandler.bind(this)); this._showSubmenuTimer = this._getDelay("hide"); } } }, _hoverEndHandler: function _hoverEndHandler(eventArg) { var that = this, $item = that._getItemElementByEventArgs(eventArg), relatedTarget = $(eventArg.relatedTarget); that.callBase(eventArg); that._clearTimeouts(); if (that._isItemDisabled($item)) { return; } if (relatedTarget.hasClass(DX_CONTEXT_MENU_CONTENT_DELIMITER_CLASS)) { return; } if (that.option("hideSubmenuOnMouseLeave") && !relatedTarget.hasClass(DX_MENU_ITEMS_CONTAINER_CLASS)) { that._hideSubmenuTimer = setTimeout(function () { that._hideSubmenuAfterTimeout(); }, that._getDelay("hide")); } }, _hideVisibleSubmenu: function _hideVisibleSubmenu() { if (!this._visibleSubmenu) { return false; } this._hideSubmenu(this._visibleSubmenu); return true; }, _showSubmenu: function _showSubmenu($itemElement) { var submenu = this._getSubmenuByElement($itemElement); if (this._visibleSubmenu !== submenu) { this._hideVisibleSubmenu(); } submenu && submenu.show(); this._visibleSubmenu = submenu; this._hoveredRootItem = $itemElement; }, _hideSubmenu: function _hideSubmenu(submenu) { submenu && submenu.hide(); if (this._visibleSubmenu === submenu) { this._visibleSubmenu = null; } this._hoveredRootItem = null; }, _itemMouseMoveHandler: function _itemMouseMoveHandler(e) { // todo: replace mousemove with hover event if (e.pointers && e.pointers.length) { return; } var that = this, $item = $(e.currentTarget); if (!typeUtils.isDefined(that._showSubmenuTimer)) { return; } that._clearTimeouts(); that._showSubmenuTimer = setTimeout(function () { var submenu = that._getSubmenuByElement($item); if (submenu && !submenu.isOverlayVisible()) { that._showSubmenu($item); } }, that._getDelay("show")); }, _clearTimeouts: function _clearTimeouts() { clearTimeout(this._hideSubmenuTimer); clearTimeout(this._showSubmenuTimer); }, _getSubmenuByElement: function _getSubmenuByElement($itemElement, itemData) { var submenu = this._getSubmenuByRootElement($itemElement); if (submenu) { return submenu; } else { itemData = itemData || this._getItemData($itemElement); var node = this._dataAdapter.getNodeByItem(itemData); return this._hasChildren(node) && this._renderSubmenuItems(node, $itemElement); } }, _updateSubmenuVisibilityOnClick: function _updateSubmenuVisibilityOnClick(actionArgs) { var args = actionArgs.args.length && actionArgs.args[0], currentSubmenu; if (!args || this._disabledGetter(args.itemData)) { return; } var $itemElement = $(args.itemElement); currentSubmenu = this._getSubmenuByElement($itemElement, args.itemData); this._updateSelectedItemOnClick(actionArgs); if (this._visibleSubmenu) { if (this._visibleSubmenu === currentSubmenu) { if (this.option("showFirstSubmenuMode") === "onClick") this._hideSubmenu(this._visibleSubmenu); return; } else { this._hideSubmenu(this._visibleSubmenu); } } if (!currentSubmenu) { return; } if (!currentSubmenu.isOverlayVisible()) { this._showSubmenu($itemElement); return; } }, _optionChanged: function _optionChanged(args) { if (this._cancelOptionChange === args.name) { return; } switch (args.name) { case "orientation": case "submenuDirection": this._invalidate(); break; case "showFirstSubmenuMode": case "hideSubmenuOnMouseLeave": break; case "showSubmenuMode": this._changeSubmenusOption(args.name, args.value); break; case "onSubmenuShowing": case "onSubmenuShown": case "onSubmenuHiding": case "onSubmenuHidden": this._initActions(); break; case "adaptivityEnabled": args.value ? this._initAdaptivity() : this._removeAdaptivity(); break; case "width": if (this._isAdaptivityEnabled()) { this._treeView.option(args.name, args.value); this._overlay.option(args.name, args.value); } this.callBase(args); this._dimensionChanged(); break; case "animation": if (this._isAdaptivityEnabled()) { this._treeView.option("animationEnabled", !!args.value); } this.callBase(args); break; default: if (this._isAdaptivityEnabled()) { this._treeView.option(args.name, args.value); } this.callBase(args); } }, _changeSubmenusOption: function _changeSubmenusOption(name, value) { each(this._submenus, function (index, submenu) { submenu.option(name, value); }); }, selectItem: function selectItem(itemElement) { this._hideSubmenu(this._visibleSubmenu); this.callBase(itemElement); }, unselectItem: function unselectItem(itemElement) { this._hideSubmenu(this._visibleSubmenu); this.callBase(itemElement); } }); registerComponent("dxMenu", Menu); module.exports = Menu;