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)
16 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
20 * @param {Object} config The config object
24 Roo.bootstrap.Menu = function(config){
25 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
26 if (this.registerMenu && this.type != 'treeview') {
27 Roo.bootstrap.MenuMgr.register(this);
34 * Fires before this menu is displayed (return false to block)
35 * @param {Roo.menu.Menu} this
40 * Fires before this menu is hidden (return false to block)
41 * @param {Roo.menu.Menu} this
46 * Fires after this menu is displayed
47 * @param {Roo.menu.Menu} this
52 * Fires after this menu is hidden
53 * @param {Roo.menu.Menu} this
58 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
59 * @param {Roo.menu.Menu} this
60 * @param {Roo.menu.Item} menuItem The menu item that was clicked
61 * @param {Roo.EventObject} e
66 * Fires when the mouse is hovering over this menu
67 * @param {Roo.menu.Menu} this
68 * @param {Roo.EventObject} e
69 * @param {Roo.menu.Item} menuItem The menu item that was clicked
74 * Fires when the mouse exits this menu
75 * @param {Roo.menu.Menu} this
76 * @param {Roo.EventObject} e
77 * @param {Roo.menu.Item} menuItem The menu item that was clicked
82 * Fires when a menu item contained in this menu is clicked
83 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
84 * @param {Roo.EventObject} e
88 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
91 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
95 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
98 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
102 menuItems :false, // stores the menu items..
112 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
117 getChildContainer : function() {
121 getAutoCreate : function(){
123 //if (['right'].indexOf(this.align)!==-1) {
124 // cfg.cn[1].cls += ' pull-right'
130 cls : 'dropdown-menu' ,
131 style : 'z-index:1000'
135 if (this.type === 'submenu') {
136 cfg.cls = 'submenu active';
138 if (this.type === 'treeview') {
139 cfg.cls = 'treeview-menu';
144 initEvents : function() {
146 // Roo.log("ADD event");
147 // Roo.log(this.triggerEl.dom);
149 this.triggerEl.on('click', this.onTriggerClick, this);
151 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
153 if (!this.hideTrigger) {
154 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
155 // dropdown toggle on the 'a' in BS4?
156 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
158 this.triggerEl.addClass('dropdown-toggle');
162 this.el.on('touchstart' , this.onTouch, this);
164 this.el.on('click' , this.onClick, this);
166 this.el.on("mouseover", this.onMouseOver, this);
167 this.el.on("mouseout", this.onMouseOut, this);
171 findTargetItem : function(e)
173 var t = e.getTarget(".dropdown-menu-item", this.el, true);
177 //Roo.log(t); Roo.log(t.id);
179 //Roo.log(this.menuitems);
180 return this.menuitems.get(t.id);
182 //return this.items.get(t.menuItemId);
188 onTouch : function(e)
190 Roo.log("menu.onTouch");
191 //e.stopEvent(); this make the user popdown broken
195 onClick : function(e)
197 Roo.log("menu.onClick");
199 var t = this.findTargetItem(e);
200 if(!t || t.isContainer){
205 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
206 if(t == this.activeItem && t.shouldDeactivate(e)){
207 this.activeItem.deactivate();
208 delete this.activeItem;
212 this.setActiveItem(t, true);
220 Roo.log('pass click event');
224 this.fireEvent("click", this, t, e);
228 if(!t.href.length || t.href == '#'){
229 (function() { _this.hide(); }).defer(100);
234 onMouseOver : function(e){
235 var t = this.findTargetItem(e);
238 // if(t.canActivate && !t.disabled){
239 // this.setActiveItem(t, true);
243 this.fireEvent("mouseover", this, e, t);
245 isVisible : function(){
248 onMouseOut : function(e){
249 var t = this.findTargetItem(e);
252 // if(t == this.activeItem && t.shouldDeactivate(e)){
253 // this.activeItem.deactivate();
254 // delete this.activeItem;
257 this.fireEvent("mouseout", this, e, t);
262 * Displays this menu relative to another element
263 * @param {String/HTMLElement/Roo.Element} element The element to align to
264 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
265 * the element (defaults to this.defaultAlign)
266 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
268 show : function(el, pos, parentMenu)
270 if (false === this.fireEvent("beforeshow", this)) {
271 Roo.log("show canceled");
274 this.parentMenu = parentMenu;
279 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
282 * Displays this menu at a specific xy position
283 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
284 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
286 showAt : function(xy, parentMenu, /* private: */_e){
287 this.parentMenu = parentMenu;
292 this.fireEvent("beforeshow", this);
293 //xy = this.el.adjustForConstraints(xy);
297 this.hideMenuItems();
299 this.triggerEl.addClass('open');
300 this.el.addClass('show');
302 // reassign x when hitting right
303 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
304 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
307 // reassign y when hitting bottom
308 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
309 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
312 // but the list may align on trigger left or trigger top... should it be a properity?
314 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
319 this.fireEvent("show", this);
325 this.doFocus.defer(50, this);
329 doFocus : function(){
331 this.focusEl.focus();
336 * Hides this menu and optionally all parent menus
337 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
339 hide : function(deep)
341 if (false === this.fireEvent("beforehide", this)) {
342 Roo.log("hide canceled");
345 this.hideMenuItems();
346 if(this.el && this.isVisible()){
349 this.activeItem.deactivate();
350 this.activeItem = null;
352 this.triggerEl.removeClass('open');;
353 this.el.removeClass('show');
355 this.fireEvent("hide", this);
357 if(deep === true && this.parentMenu){
358 this.parentMenu.hide(true);
362 onTriggerClick : function(e)
364 Roo.log('trigger click');
366 var target = e.getTarget();
368 Roo.log(target.nodeName.toLowerCase());
370 if(target.nodeName.toLowerCase() === 'i'){
376 onTriggerPress : function(e)
378 Roo.log('trigger press');
379 //Roo.log(e.getTarget());
380 // Roo.log(this.triggerEl.dom);
382 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
383 var pel = Roo.get(e.getTarget());
384 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
385 Roo.log('is treeview or dropdown?');
389 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
393 if (this.isVisible()) {
398 this.show(this.triggerEl, '?', false);
401 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
408 hideMenuItems : function()
410 Roo.log("hide Menu Items");
415 this.el.select('.open',true).each(function(aa) {
417 aa.removeClass('open');
421 addxtypeChild : function (tree, cntr) {
422 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
424 this.menuitems.add(comp);
436 this.getEl().dom.innerHTML = '';
437 this.menuitems.clear();