9 * @class Roo.bootstrap.Menu
10 * @extends Roo.bootstrap.Component
11 * Bootstrap Menu class - container for MenuItems
12 * @cfg {String} type (dropdown|treeview|submenu) type of menu
13 * @cfg {bool} hidden if the menu should be hidden when rendered.
14 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
15 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
19 * @param {Object} config The config object
23 Roo.bootstrap.Menu = function(config){
24 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
25 if (this.registerMenu && this.type != 'treeview') {
26 Roo.bootstrap.MenuMgr.register(this);
33 * Fires before this menu is displayed
34 * @param {Roo.menu.Menu} this
39 * Fires before this menu is hidden
40 * @param {Roo.menu.Menu} this
45 * Fires after this menu is displayed
46 * @param {Roo.menu.Menu} this
51 * Fires after this menu is hidden
52 * @param {Roo.menu.Menu} this
57 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
58 * @param {Roo.menu.Menu} this
59 * @param {Roo.menu.Item} menuItem The menu item that was clicked
60 * @param {Roo.EventObject} e
65 * Fires when the mouse is hovering over this menu
66 * @param {Roo.menu.Menu} this
67 * @param {Roo.EventObject} e
68 * @param {Roo.menu.Item} menuItem The menu item that was clicked
73 * Fires when the mouse exits this menu
74 * @param {Roo.menu.Menu} this
75 * @param {Roo.EventObject} e
76 * @param {Roo.menu.Item} menuItem The menu item that was clicked
81 * Fires when a menu item contained in this menu is clicked
82 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
83 * @param {Roo.EventObject} e
87 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
90 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
94 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
97 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
101 menuItems :false, // stores the menu items..
111 getChildContainer : function() {
115 getAutoCreate : function(){
117 //if (['right'].indexOf(this.align)!==-1) {
118 // cfg.cn[1].cls += ' pull-right'
124 cls : 'dropdown-menu' ,
125 style : 'z-index:1000'
129 if (this.type === 'submenu') {
130 cfg.cls = 'submenu active';
132 if (this.type === 'treeview') {
133 cfg.cls = 'treeview-menu';
138 initEvents : function() {
140 // Roo.log("ADD event");
141 // Roo.log(this.triggerEl.dom);
143 this.triggerEl.on('click', this.onTriggerClick, this);
145 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
148 if (this.triggerEl.hasClass('nav-item')) {
149 // dropdown toggle on the 'a' in BS4?
150 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
152 this.triggerEl.addClass('dropdown-toggle');
155 this.el.on('touchstart' , this.onTouch, this);
157 this.el.on('click' , this.onClick, this);
159 this.el.on("mouseover", this.onMouseOver, this);
160 this.el.on("mouseout", this.onMouseOut, this);
164 findTargetItem : function(e)
166 var t = e.getTarget(".dropdown-menu-item", this.el, true);
170 //Roo.log(t); Roo.log(t.id);
172 //Roo.log(this.menuitems);
173 return this.menuitems.get(t.id);
175 //return this.items.get(t.menuItemId);
181 onTouch : function(e)
183 Roo.log("menu.onTouch");
184 //e.stopEvent(); this make the user popdown broken
188 onClick : function(e)
190 Roo.log("menu.onClick");
192 var t = this.findTargetItem(e);
193 if(!t || t.isContainer){
198 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
199 if(t == this.activeItem && t.shouldDeactivate(e)){
200 this.activeItem.deactivate();
201 delete this.activeItem;
205 this.setActiveItem(t, true);
213 Roo.log('pass click event');
217 this.fireEvent("click", this, t, e);
221 if(!t.href.length || t.href == '#'){
222 (function() { _this.hide(); }).defer(100);
227 onMouseOver : function(e){
228 var t = this.findTargetItem(e);
231 // if(t.canActivate && !t.disabled){
232 // this.setActiveItem(t, true);
236 this.fireEvent("mouseover", this, e, t);
238 isVisible : function(){
241 onMouseOut : function(e){
242 var t = this.findTargetItem(e);
245 // if(t == this.activeItem && t.shouldDeactivate(e)){
246 // this.activeItem.deactivate();
247 // delete this.activeItem;
250 this.fireEvent("mouseout", this, e, t);
255 * Displays this menu relative to another element
256 * @param {String/HTMLElement/Roo.Element} element The element to align to
257 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
258 * the element (defaults to this.defaultAlign)
259 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
261 show : function(el, pos, parentMenu){
262 this.parentMenu = parentMenu;
266 this.fireEvent("beforeshow", this);
267 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
270 * Displays this menu at a specific xy position
271 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
272 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
274 showAt : function(xy, parentMenu, /* private: */_e){
275 this.parentMenu = parentMenu;
280 this.fireEvent("beforeshow", this);
281 //xy = this.el.adjustForConstraints(xy);
285 this.hideMenuItems();
287 this.triggerEl.addClass('open');
288 this.el.addClass('show');
290 // reassign x when hitting right
291 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
292 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
295 // reassign y when hitting bottom
296 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
297 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
300 // but the list may align on trigger left or trigger top... should it be a properity?
302 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
307 this.fireEvent("show", this);
313 this.doFocus.defer(50, this);
317 doFocus : function(){
319 this.focusEl.focus();
324 * Hides this menu and optionally all parent menus
325 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
327 hide : function(deep)
330 this.hideMenuItems();
331 if(this.el && this.isVisible()){
332 this.fireEvent("beforehide", this);
334 this.activeItem.deactivate();
335 this.activeItem = null;
337 this.triggerEl.removeClass('open');;
338 this.el.removeClass('show');
340 this.fireEvent("hide", this);
342 if(deep === true && this.parentMenu){
343 this.parentMenu.hide(true);
347 onTriggerClick : function(e)
349 Roo.log('trigger click');
351 var target = e.getTarget();
353 Roo.log(target.nodeName.toLowerCase());
355 if(target.nodeName.toLowerCase() === 'i'){
361 onTriggerPress : function(e)
363 Roo.log('trigger press');
364 //Roo.log(e.getTarget());
365 // Roo.log(this.triggerEl.dom);
367 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
368 var pel = Roo.get(e.getTarget());
369 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
370 Roo.log('is treeview or dropdown?');
374 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
378 if (this.isVisible()) {
383 this.show(this.triggerEl, '?', false);
386 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
393 hideMenuItems : function()
395 Roo.log("hide Menu Items");
399 //$(backdrop).remove()
400 this.el.select('.open',true).each(function(aa) {
402 aa.removeClass('open');
403 //var parent = getParent($(this))
404 //var relatedTarget = { relatedTarget: this }
406 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
407 //if (e.isDefaultPrevented()) return
408 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
411 addxtypeChild : function (tree, cntr) {
412 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
414 this.menuitems.add(comp);
426 this.getEl().dom.innerHTML = '';
427 this.menuitems.clear();