Fix #6833 - print layer map
[roojs1] / Roo / bootstrap / Menu.js
1 /*
2  * - LGPL
3  *
4  * menu
5  * 
6  */
7
8 /**
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.
17   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
18  
19  * @constructor
20  * Create a new Menu
21  * @param {Object} config The config object
22  */
23
24
25 Roo.bootstrap.Menu = function(config){
26     
27     if (config.type == 'treeview') {
28         // normally menu's are drawn attached to the document to handle layering etc..
29         // however treeview (used by the docs menu is drawn into the parent element)
30         this.container_method = 'getChildContainer'; 
31     }
32     
33     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
34     if (this.registerMenu && this.type != 'treeview')  {
35         Roo.bootstrap.MenuMgr.register(this);
36     }
37     
38     
39     this.addEvents({
40         /**
41          * @event beforeshow
42          * Fires before this menu is displayed (return false to block)
43          * @param {Roo.menu.Menu} this
44          */
45         beforeshow : true,
46         /**
47          * @event beforehide
48          * Fires before this menu is hidden (return false to block)
49          * @param {Roo.menu.Menu} this
50          */
51         beforehide : true,
52         /**
53          * @event show
54          * Fires after this menu is displayed
55          * @param {Roo.menu.Menu} this
56          */
57         show : true,
58         /**
59          * @event hide
60          * Fires after this menu is hidden
61          * @param {Roo.menu.Menu} this
62          */
63         hide : true,
64         /**
65          * @event click
66          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
67          * @param {Roo.menu.Menu} this
68          * @param {Roo.menu.Item} menuItem The menu item that was clicked
69          * @param {Roo.EventObject} e
70          */
71         click : true,
72         /**
73          * @event mouseover
74          * Fires when the mouse is hovering over 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
78          */
79         mouseover : true,
80         /**
81          * @event mouseout
82          * Fires when the mouse exits this menu
83          * @param {Roo.menu.Menu} this
84          * @param {Roo.EventObject} e
85          * @param {Roo.menu.Item} menuItem The menu item that was clicked
86          */
87         mouseout : true,
88         /**
89          * @event itemclick
90          * Fires when a menu item contained in this menu is clicked
91          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
92          * @param {Roo.EventObject} e
93          */
94         itemclick: true
95     });
96     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
97 };
98
99 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
100     
101    /// html : false,
102    
103     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
104     type: false,
105     /**
106      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
107      */
108     registerMenu : true,
109     
110     menuItems :false, // stores the menu items..
111     
112     hidden:true,
113         
114     parentMenu : false,
115     
116     stopEvent : true,
117     
118     isLink : false,
119     
120     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
121     
122     hideTrigger : false,
123     
124     align : 'tl-bl?',
125     
126     
127     getChildContainer : function() {
128         return this.el;  
129     },
130     
131     getAutoCreate : function(){
132          
133         //if (['right'].indexOf(this.align)!==-1) {
134         //    cfg.cn[1].cls += ' pull-right'
135         //}
136          
137         var cfg = {
138             tag : 'ul',
139             cls : 'dropdown-menu shadow' ,
140             style : 'z-index:1000'
141             
142         };
143         
144         if (this.type === 'submenu') {
145             cfg.cls = 'submenu active';
146         }
147         if (this.type === 'treeview') {
148             cfg.cls = 'treeview-menu';
149         }
150         
151         return cfg;
152     },
153     initEvents : function() {
154         
155        // Roo.log("ADD event");
156        // Roo.log(this.triggerEl.dom);
157         if (this.triggerEl) {
158             
159             this.triggerEl.on('click', this.onTriggerClick, this);
160             
161             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
162             
163             if (!this.hideTrigger) {
164                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
165                     // dropdown toggle on the 'a' in BS4?
166                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
167                 } else {
168                     this.triggerEl.addClass('dropdown-toggle');
169                 }
170             }
171         }
172         
173         if (Roo.isTouch) {
174             this.el.on('touchstart'  , this.onTouch, this);
175         }
176         this.el.on('click' , this.onClick, this);
177
178         this.el.on("mouseover", this.onMouseOver, this);
179         this.el.on("mouseout", this.onMouseOut, this);
180         
181     },
182     
183     findTargetItem : function(e)
184     {
185         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
186         if(!t){
187             return false;
188         }
189         //Roo.log(t);         Roo.log(t.id);
190         if(t && t.id){
191             //Roo.log(this.menuitems);
192             return this.menuitems.get(t.id);
193             
194             //return this.items.get(t.menuItemId);
195         }
196         
197         return false;
198     },
199     
200     onTouch : function(e) 
201     {
202         Roo.log("menu.onTouch");
203         //e.stopEvent(); this make the user popdown broken
204         this.onClick(e);
205     },
206     
207     onClick : function(e)
208     {
209         Roo.log("menu.onClick");
210         
211         var t = this.findTargetItem(e);
212         if(!t || t.isContainer){
213             return;
214         }
215         Roo.log(e);
216         /*
217         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
218             if(t == this.activeItem && t.shouldDeactivate(e)){
219                 this.activeItem.deactivate();
220                 delete this.activeItem;
221                 return;
222             }
223             if(t.canActivate){
224                 this.setActiveItem(t, true);
225             }
226             return;
227             
228             
229         }
230         */
231        
232         Roo.log('pass click event');
233         
234         t.onClick(e);
235         
236         this.fireEvent("click", this, t, e);
237         
238         var _this = this;
239         
240         if(!t.href.length || t.href == '#'){
241             (function() { _this.hide(); }).defer(100);
242         }
243         
244     },
245     
246     onMouseOver : function(e){
247         var t  = this.findTargetItem(e);
248         //Roo.log(t);
249         //if(t){
250         //    if(t.canActivate && !t.disabled){
251         //        this.setActiveItem(t, true);
252         //    }
253         //}
254         
255         this.fireEvent("mouseover", this, e, t);
256     },
257     isVisible : function(){
258         return !this.hidden;
259     },
260     onMouseOut : function(e){
261         var t  = this.findTargetItem(e);
262         
263         //if(t ){
264         //    if(t == this.activeItem && t.shouldDeactivate(e)){
265         //        this.activeItem.deactivate();
266         //        delete this.activeItem;
267         //    }
268         //}
269         this.fireEvent("mouseout", this, e, t);
270     },
271     
272     
273     /**
274      * Displays this menu relative to another element
275      * @param {String/HTMLElement/Roo.Element} element The element to align to
276      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
277      * the element (defaults to this.defaultAlign)
278      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
279      */
280     show : function(el, pos, parentMenu)
281     {
282         if (false === this.fireEvent("beforeshow", this)) {
283             Roo.log("show canceled");
284             return;
285         }
286         this.parentMenu = parentMenu;
287         if(!this.el){
288             this.render();
289         }
290         this.el.addClass('show'); // show otherwise we do not know how big we are..
291          
292         var xy = this.el.getAlignToXY(el, pos);
293         
294         // bl-tl << left align  below
295         // tl-bl << left align 
296         
297         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
298             // if it goes to far to the right.. -> align left.
299             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
300         }
301         if(xy[0] < 0){
302             // was left align - go right?
303             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
304         }
305         
306         // goes down the bottom
307         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
308            xy[1]  < 0 ){
309             var a = this.align.replace('?', '').split('-');
310             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
311             
312         }
313         
314         this.showAt(  xy , parentMenu, false);
315     },
316      /**
317      * Displays this menu at a specific xy position
318      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
319      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
320      */
321     showAt : function(xy, parentMenu, /* private: */_e){
322         this.parentMenu = parentMenu;
323         if(!this.el){
324             this.render();
325         }
326         if(_e !== false){
327             this.fireEvent("beforeshow", this);
328             //xy = this.el.adjustForConstraints(xy);
329         }
330         
331         //this.el.show();
332         this.hideMenuItems();
333         this.hidden = false;
334         if (this.triggerEl) {
335             this.triggerEl.addClass('open');
336         }
337         
338         this.el.addClass('show');
339         
340         
341         
342         // reassign x when hitting right
343         
344         // reassign y when hitting bottom
345         
346         // but the list may align on trigger left or trigger top... should it be a properity?
347         
348         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
349             this.el.setXY(xy);
350         }
351         
352         this.focus();
353         this.fireEvent("show", this);
354     },
355     
356     focus : function(){
357         return;
358         if(!this.hidden){
359             this.doFocus.defer(50, this);
360         }
361     },
362
363     doFocus : function(){
364         if(!this.hidden){
365             this.focusEl.focus();
366         }
367     },
368
369     /**
370      * Hides this menu and optionally all parent menus
371      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
372      */
373     hide : function(deep)
374     {
375         if (false === this.fireEvent("beforehide", this)) {
376             Roo.log("hide canceled");
377             return;
378         }
379         this.hideMenuItems();
380         if(this.el && this.isVisible()){
381            
382             if(this.activeItem){
383                 this.activeItem.deactivate();
384                 this.activeItem = null;
385             }
386             if (this.triggerEl) {
387                 this.triggerEl.removeClass('open');
388             }
389             
390             this.el.removeClass('show');
391             this.hidden = true;
392             this.fireEvent("hide", this);
393         }
394         if(deep === true && this.parentMenu){
395             this.parentMenu.hide(true);
396         }
397     },
398     
399     onTriggerClick : function(e)
400     {
401         Roo.log('trigger click');
402         
403         var target = e.getTarget();
404         
405         Roo.log(target.nodeName.toLowerCase());
406         
407         if(target.nodeName.toLowerCase() === 'i'){
408             e.preventDefault();
409         }
410         
411     },
412     
413     onTriggerPress  : function(e)
414     {
415         Roo.log('trigger press');
416         //Roo.log(e.getTarget());
417        // Roo.log(this.triggerEl.dom);
418        
419         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
420         var pel = Roo.get(e.getTarget());
421         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
422             Roo.log('is treeview or dropdown?');
423             return;
424         }
425         
426         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
427             return;
428         }
429         
430         if (this.isVisible()) {
431             Roo.log('hide');
432             this.hide();
433         } else {
434             Roo.log('show');
435             
436             this.show(this.triggerEl, this.align, false);
437         }
438         
439         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
440             e.stopEvent();
441         }
442         
443     },
444        
445     
446     hideMenuItems : function()
447     {
448         Roo.log("hide Menu Items");
449         if (!this.el) { 
450             return;
451         }
452         
453         this.el.select('.open',true).each(function(aa) {
454             
455             aa.removeClass('open');
456          
457         });
458     },
459     addxtypeChild : function (tree, cntr) {
460         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
461           
462         this.menuitems.add(comp);
463         return comp;
464
465     },
466     getEl : function()
467     {
468         Roo.log(this.el);
469         return this.el;
470     },
471     
472     clear : function()
473     {
474         this.getEl().dom.innerHTML = '';
475         this.menuitems.clear();
476     }
477 });
478
479  
480