/**
* combination of menu and button ,similar to native select
* @ignore
* @author yiminghe@gmail.com
*/
KISSY.add("menubutton/control", function (S, Node, Button, MenuButtonRender, Menu, undefined) {
var KeyCode = Node.KeyCode;
* A menu button component, consist of a button and a drop down popup menu.
* xclass: 'menu-button'.
* @class KISSY.MenuButton
* @extends KISSY.Button
*/
return Button.extend({
isMenuButton: 1,
_onSetCollapsed: function (v) {
var self = this,
menu = self.get("menu");
if (v) {
menu.hide();
} else {
var el = self.$el;
if (!menu.get("visible")) {
// same as submenu
// in case menu is changed after menubutton is rendered
var align = {
node: el,
points: ["bl", "tl"],
overflow: {
adjustX: 1,
adjustY: 1
}
};
S.mix(menu.get('align'), align, false);
if (self.get("matchElWidth")) {
menu.render();
var menuEl = menu.get('el');
var borderWidth =
(parseInt(menuEl.css('borderLeftWidth')) || 0) +
(parseInt(menuEl.css('borderRightWidth')) || 0);
menu.set("width", menu.get("align").node[0].offsetWidth - borderWidth);
}
menu.show();
el.attr("aria-haspopup", menu.get('el').attr("id"));
}
}
},
bindUI: function () {
var self = this;
self.on('afterHighlightedItemChange',
onMenuAfterHighlightedItemChange, self);
self.on('click', onMenuItemClick, self);
},
/**
* Handle keydown/up event.
* If drop down menu is visible then handle event to menu.
* Returns true if the event was handled, falsy otherwise.
* Protected, should only be overridden by subclasses.
* @param {KISSY.Event.DomEvent.Object} e key event to handle.
* @return {Boolean|undefined} True Whether the key event was handled.
* @protected
*/
handleKeyDownInternal: function (e) {
var self = this,
keyCode = e.keyCode,
type = String(e.type),
menu = self.get("menu");
// space 只在 keyup 时处理
if (keyCode == KeyCode.SPACE) {
// Prevent page scrolling in Chrome.
e.preventDefault();
if (type != "keyup") {
return undefined;
}
} else if (type != "keydown") {
return undefined;
}
//转发给 menu 处理
if (menu.get('rendered') && menu.get("visible")) {
var handledByMenu = menu.handleKeyDownInternal(e);
// esc
if (keyCode == KeyCode.ESC) {
self.set("collapsed", true);
return true;
}
return handledByMenu;
}
// Menu is closed, and the user hit the down/up/space key; open menu.
if (keyCode == KeyCode.SPACE ||
keyCode == KeyCode.DOWN ||
keyCode == KeyCode.UP) {
self.set("collapsed", false);
return true;
}
return undefined;
},
/**
* Perform default action for menubutton.
* Toggle the drop down menu to show or hide.
* Protected, should only be overridden by subclasses.
* @protected
*
*/
handleClickInternal: function () {
var self = this;
self.set("collapsed", !self.get("collapsed"));
},
/**
* Handles blur event.
* When it loses keyboard focus, close the drop dow menu.
* @param {KISSY.Event.DomEvent.Object} e Blur event.
* Protected, should only be overridden by subclasses.
* @protected
*
*/
handleBlurInternal: function (e) {
var self = this;
self.callSuper(e);
// such as : click the document
self.set("collapsed", true);
},
* Adds a new menu item at the end of the menu.
* @param {KISSY.Menu.Item} item Menu item to add to the menu.
* @param {Number} index position to insert
*/
addItem: function (item, index) {
var menu = this.get("menu");
menu.addChild(item, index);
},
* Remove a existing menu item from drop down menu.
* @param c {KISSY.Menu.Item} Existing menu item.
* @param [destroy=true] {Boolean} Whether destroy removed menu item.
*/
removeItem: function (c, destroy) {
var menu = this.get("menu");
menu.removeChild(c, destroy);
},
* Remove all menu items from drop down menu.
* @param [destroy] {Boolean} Whether destroy removed menu items.
*/
removeItems: function (destroy) {
var menu = this.get("menu");
if (menu) {
if (menu.removeChildren) {
menu.removeChildren(destroy);
} else if (menu.children) {
menu.children = [];
}
}
},
* Returns the child menu item of drop down menu at the given index, or null if the index is out of bounds.
* @param {Number} index 0-based index.
*/
getItemAt: function (index) {
var menu = self.get("menu");
return menu.get('rendered') && menu.getChildAt(index);
},
// 禁用时关闭已显示菜单
_onSetDisabled: function (v) {
var self = this;
!v && self.set("collapsed", true);
},
destructor: function () {
this.get('menu').destroy();
}
}, {
ATTRS: {
* Whether drop down menu is same width with button.
* Defaults to: true.
* @cfg {Boolean} matchElWidth
*/
/**
* @ignore
*/
matchElWidth: {
value: true
},
* Whether hide drop down menu when click drop down menu item.
* eg: u do not want to set true when menu has checked menuitem.
* Defaults to: false
* @cfg {Boolean} collapseOnClick
*/
/**
* @ignore
*/
collapseOnClick: {
value: false
},
* Drop down menu associated with this menubutton.
* @cfg {KISSY.Menu|Object} menu
*/
* Drop down menu associated with this menubutton.
* @property {KISSY.Menu} menu
*/
* @ignore
*/
menu: {
value: {},
getter: function (v) {
if (!v.isControl) {
v.xclass = v.xclass || 'popupmenu';
v = this.createComponent(v);
this.setInternal('menu', v);
}
return v;
},
setter: function (m) {
if (m.isControl) {
m.setInternal('parent', this);
}
}
},
* Whether drop menu is shown.
* @property {Boolean} collapsed
*/
/**
* @ignore
*/
collapsed: {
value: false,
view: 1
},
xrender: {
value: MenuButtonRender
}
},
xclass: 'menu-button'
});
function onMenuItemClick(e) {
if (e.target.isMenuItem && this.get('collapseOnClick')) {
this.set("collapsed", true);
}
}
function onMenuAfterHighlightedItemChange(e) {
if (e.target.isMenu) {
var el = this.el,
menuItem = e.newVal;
el.setAttribute("aria-activedescendant", menuItem && menuItem.el.id || '');
}
}
}, {
requires: [ "node", "button", "./render", "menu"]
});