src/mixin/element/menu.js
import _ from 'lodash';
import { event as xevent } from 'xblocks-core';
import lazyFocus from 'utils/lazyFocus';
import initialDefinitionSelected from 'utils/initialDefinitionSelected';
import isParent from 'dom/isParent';
import ConstantMenu from 'constants/menu';
import ConstantMenuitem from 'constants/menuitem';
/**
* Common interface for elements xb-menu and xb-menu-inline.
*/
export default {
lifecycle: {
created: function () {
if (this.selectable) {
this[ ConstantMenu.SELECTED ] = initialDefinitionSelected(this);
}
}
},
events: {
/**
* Оpen the submenu or menu item is selected
* @this xb.Menuitem
*/
'click:delegate(xb-menuitem:not([disabled]))': function () {
if (this.submenuInstance) {
this.submenuInstance.open();
} else if (this.menuInstance) {
this.menuInstance.menuitemSelect();
}
},
/**
* Оpen the submenu
* @this xb.Menu
*/
'keydown:keypass(13,39)': function () {
const item = this[ ConstantMenu.TABLE ].getItem();
if (item && item.submenuInstance) {
item.submenuInstance.open();
}
},
/**
* The menu item is selected
* @this xb.Menu
*/
'keydown:keypass(32)': function () {
this.menuitemSelect();
},
/**
* Restore focus
* @param {Event} event
* @this xb.Menu
*/
'jsx-scroll-throttle': function (event) {
// close all submenu
event.stopImmediatePropagation();
lazyFocus(this);
}
},
accessors: {
/**
* The menu contains the open submenu
* @prop {boolean} hasOpenSubmenu
*/
hasOpenSubmenu: {
get: function () {
return Boolean(this.querySelector('.xb-menu-target.xb-menu-enabled'));
}
},
selectable: {
attribute: {
boolean: true
}
},
multiple: {
attribute: {
boolean: true
}
},
alwaysselected: {
attribute: {
boolean: true
}
},
/**
* @prop {boolean} autoclose closing the menu after selecting
*/
autoclose: {
attribute: {
boolean: true
}
},
/**
* @prop {xb.Menu|null} parentMenu menu-ancestor
* @readonly
*/
parentMenu: {
get: function () {
const parentMenu = _.get(this, 'core.target.menuInstance', null);
return (parentMenu === this) ? null : parentMenu;
}
},
/**
* @prop {xb.Menu} [firstParentMenu] the first menu ancestor
* @readonly
*/
firstParentMenu: {
get: function () {
const parentMenu = this.parentMenu;
if (parentMenu) {
return parentMenu.firstParentMenu || parentMenu;
}
return this;
}
},
/**
* @prop {string[]} value the values of the selected menu item
* @readonly
*/
value: {
get: function () {
return _.map(this.firstParentMenu[ ConstantMenu.SELECTED ], 'value');
}
},
/**
* @prop {HTMLElement[]} selectedItems selected menu items
* @readonly
*/
selectedItems: {
get: function () {
return _.values(this.firstParentMenu[ ConstantMenu.SELECTED ]);
}
},
/**
* @prop {Object[]} selectedObjects the data of selected items
* @readonly
*/
selectedObjects: {
get: function () {
return _.map(this.firstParentMenu[ ConstantMenu.SELECTED ], item => {
return {
label: item.getAttribute('label'),
value: item.value
};
});
}
}
},
methods: {
/**
* @param {xb.Menuitem} menuitem
*/
scrollIntoItem: function (menuitem) {
if (!isParent(this, menuitem)) {
return;
}
const component = this.getComponent();
if (component) {
component.scrollIntoItem(menuitem);
}
},
menuitemSelect: function () {
if (!this.selectable) {
return;
}
const selectedAttr = ConstantMenuitem.SELECTED_ATTR;
const item = this[ ConstantMenu.TABLE ].getItem();
const selected = !item.selected;
const uid = item.getAttribute(selectedAttr) || _.uniqueId('selected');
const firstParentMenu = this.firstParentMenu;
// сброс выбранных пунктов, если не мультиселект и текущий пункт будет выбран
if (!this.multiple && selected) {
_(firstParentMenu[ ConstantMenu.SELECTED ])
.chain()
.keys()
.join(`"],xb-menuitem[${selectedAttr}="`)
.thru(value => this.ownerDocument.querySelectorAll(`xb-menuitem[${selectedAttr}="${value}"]`))
.forEach(node => {
node.selected = false;
node.removeAttribute(selectedAttr);
})
.value();
firstParentMenu[ ConstantMenu.SELECTED ] = {};
}
let changed = false;
if (selected) {
changed = true;
item.selected = true;
item.setAttribute(selectedAttr, uid);
_.set(firstParentMenu, [ ConstantMenu.SELECTED, uid ], item);
} else if (!this.alwaysselected || _.size(firstParentMenu[ ConstantMenu.SELECTED ]) > 1) {
changed = true;
item.selected = false;
item.removeAttribute(selectedAttr);
_.unset(firstParentMenu, [ ConstantMenu.SELECTED, uid ]);
}
if (changed) {
if (this.autoclose) {
firstParentMenu.close();
}
xevent.dispatch(firstParentMenu, 'change', {
detail: { item }
});
}
}
}
};