/*
* - LGPL
*
* menu
*
*/
/**
* @class Roo.bootstrap.Menu
* @extends Roo.bootstrap.Component
* Bootstrap Menu class - container for MenuItems
* @cfg {String} type (dropdown|treeview|submenu) type of menu
* @cfg {bool} hidden if the menu should be hidden when rendered.
* @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
* @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
* @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
*
* @constructor
* Create a new Menu
* @param {Object} config The config object
*/
Roo.bootstrap.Menu = function(config){
Roo.bootstrap.Menu.superclass.constructor.call(this, config);
if (this.registerMenu && this.type != 'treeview') {
Roo.bootstrap.MenuMgr.register(this);
}
this.addEvents({
/**
* @event beforeshow
* Fires before this menu is displayed (return false to block)
* @param {Roo.menu.Menu} this
*/
beforeshow : true,
/**
* @event beforehide
* Fires before this menu is hidden (return false to block)
* @param {Roo.menu.Menu} this
*/
beforehide : true,
/**
* @event show
* Fires after this menu is displayed
* @param {Roo.menu.Menu} this
*/
show : true,
/**
* @event hide
* Fires after this menu is hidden
* @param {Roo.menu.Menu} this
*/
hide : true,
/**
* @event click
* Fires when this menu is clicked (or when the enter key is pressed while it is active)
* @param {Roo.menu.Menu} this
* @param {Roo.menu.Item} menuItem The menu item that was clicked
* @param {Roo.EventObject} e
*/
click : true,
/**
* @event mouseover
* Fires when the mouse is hovering over this menu
* @param {Roo.menu.Menu} this
* @param {Roo.EventObject} e
* @param {Roo.menu.Item} menuItem The menu item that was clicked
*/
mouseover : true,
/**
* @event mouseout
* Fires when the mouse exits this menu
* @param {Roo.menu.Menu} this
* @param {Roo.EventObject} e
* @param {Roo.menu.Item} menuItem The menu item that was clicked
*/
mouseout : true,
/**
* @event itemclick
* Fires when a menu item contained in this menu is clicked
* @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
* @param {Roo.EventObject} e
*/
itemclick: true
});
this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
};
Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
/// html : false,
//align : '',
triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
type: false,
/**
* @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
*/
registerMenu : true,
menuItems :false, // stores the menu items..
hidden:true,
parentMenu : false,
stopEvent : true,
isLink : false,
container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
hideTrigger : false,
getChildContainer : function() {
return this.el;
},
getAutoCreate : function(){
//if (['right'].indexOf(this.align)!==-1) {
// cfg.cn[1].cls += ' pull-right'
//}
var cfg = {
tag : 'ul',
cls : 'dropdown-menu shadow' ,
style : 'z-index:1000'
};
if (this.type === 'submenu') {
cfg.cls = 'submenu active';
}
if (this.type === 'treeview') {
cfg.cls = 'treeview-menu';
}
return cfg;
},
initEvents : function() {
// Roo.log("ADD event");
// Roo.log(this.triggerEl.dom);
this.triggerEl.on('click', this.onTriggerClick, this);
this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
if (!this.hideTrigger) {
if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
// dropdown toggle on the 'a' in BS4?
this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
} else {
this.triggerEl.addClass('dropdown-toggle');
}
}
if (Roo.isTouch) {
this.el.on('touchstart' , this.onTouch, this);
}
this.el.on('click' , this.onClick, this);
this.el.on("mouseover", this.onMouseOver, this);
this.el.on("mouseout", this.onMouseOut, this);
},
findTargetItem : function(e)
{
var t = e.getTarget(".dropdown-menu-item", this.el, true);
if(!t){
return false;
}
//Roo.log(t); Roo.log(t.id);
if(t && t.id){
//Roo.log(this.menuitems);
return this.menuitems.get(t.id);
//return this.items.get(t.menuItemId);
}
return false;
},
onTouch : function(e)
{
Roo.log("menu.onTouch");
//e.stopEvent(); this make the user popdown broken
this.onClick(e);
},
onClick : function(e)
{
Roo.log("menu.onClick");
var t = this.findTargetItem(e);
if(!t || t.isContainer){
return;
}
Roo.log(e);
/*
if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
if(t == this.activeItem && t.shouldDeactivate(e)){
this.activeItem.deactivate();
delete this.activeItem;
return;
}
if(t.canActivate){
this.setActiveItem(t, true);
}
return;
}
*/
Roo.log('pass click event');
t.onClick(e);
this.fireEvent("click", this, t, e);
var _this = this;
if(!t.href.length || t.href == '#'){
(function() { _this.hide(); }).defer(100);
}
},
onMouseOver : function(e){
var t = this.findTargetItem(e);
//Roo.log(t);
//if(t){
// if(t.canActivate && !t.disabled){
// this.setActiveItem(t, true);
// }
//}
this.fireEvent("mouseover", this, e, t);
},
isVisible : function(){
return !this.hidden;
},
onMouseOut : function(e){
var t = this.findTargetItem(e);
//if(t ){
// if(t == this.activeItem && t.shouldDeactivate(e)){
// this.activeItem.deactivate();
// delete this.activeItem;
// }
//}
this.fireEvent("mouseout", this, e, t);
},
/**
* Displays this menu relative to another element
* @param {String/HTMLElement/Roo.Element} element The element to align to
* @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
* the element (defaults to this.defaultAlign)
* @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
*/
show : function(el, pos, parentMenu)
{
if (false === this.fireEvent("beforeshow", this)) {
Roo.log("show canceled");
return;
}
this.parentMenu = parentMenu;
if(!this.el){
this.render();
}
this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
},
/**
* Displays this menu at a specific xy position
* @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
* @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
*/
showAt : function(xy, parentMenu, /* private: */_e){
this.parentMenu = parentMenu;
if(!this.el){
this.render();
}
if(_e !== false){
this.fireEvent("beforeshow", this);
//xy = this.el.adjustForConstraints(xy);
}
//this.el.show();
this.hideMenuItems();
this.hidden = false;
this.triggerEl.addClass('open');
this.el.addClass('show');
// reassign x when hitting right
if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
}
// reassign y when hitting bottom
if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
}
// but the list may align on trigger left or trigger top... should it be a properity?
if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
this.el.setXY(xy);
}
this.focus();
this.fireEvent("show", this);
},
focus : function(){
return;
if(!this.hidden){
this.doFocus.defer(50, this);
}
},
doFocus : function(){
if(!this.hidden){
this.focusEl.focus();
}
},
/**
* Hides this menu and optionally all parent menus
* @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
*/
hide : function(deep)
{
if (false === this.fireEvent("beforehide", this)) {
Roo.log("hide canceled");
return;
}
this.hideMenuItems();
if(this.el && this.isVisible()){
if(this.activeItem){
this.activeItem.deactivate();
this.activeItem = null;
}
this.triggerEl.removeClass('open');;
this.el.removeClass('show');
this.hidden = true;
this.fireEvent("hide", this);
}
if(deep === true && this.parentMenu){
this.parentMenu.hide(true);
}
},
onTriggerClick : function(e)
{
Roo.log('trigger click');
var target = e.getTarget();
Roo.log(target.nodeName.toLowerCase());
if(target.nodeName.toLowerCase() === 'i'){
e.preventDefault();
}
},
onTriggerPress : function(e)
{
Roo.log('trigger press');
//Roo.log(e.getTarget());
// Roo.log(this.triggerEl.dom);
// trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
var pel = Roo.get(e.getTarget());
if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
Roo.log('is treeview or dropdown?');
return;
}
if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
return;
}
if (this.isVisible()) {
Roo.log('hide');
this.hide();
} else {
Roo.log('show');
this.show(this.triggerEl, '?', false);
}
if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
e.stopEvent();
}
},
hideMenuItems : function()
{
Roo.log("hide Menu Items");
if (!this.el) {
return;
}
this.el.select('.open',true).each(function(aa) {
aa.removeClass('open');
});
},
addxtypeChild : function (tree, cntr) {
var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
this.menuitems.add(comp);
return comp;
},
getEl : function()
{
Roo.log(this.el);
return this.el;
},
clear : function()
{
this.getEl().dom.innerHTML = '';
this.menuitems.clear();
}
});