Menu2.html
20.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
<script type="text/javascript">
function highlight() {
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
}
</script>
</head>
<body onload="prettyPrint(); highlight();">
<pre class="prettyprint lang-js"><span id='Ext-menu-Menu'>/**
</span> * A menu object. This is the container to which you may add {@link Ext.menu.Item menu items}.
*
* Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Components}.
* Menus may also contain {@link Ext.panel.AbstractPanel#dockedItems docked items} because it extends {@link Ext.panel.Panel}.
*
* To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items},
* specify `{@link Ext.menu.Item#plain plain}: true`. This reserves a space for an icon, and indents the Component
* in line with the other menu items.
*
* By default, Menus are absolutely positioned, floating Components. By configuring a Menu with `{@link #floating}: false`,
* a Menu may be used as a child of a {@link Ext.container.Container Container}.
*
* @example
* Ext.create('Ext.menu.Menu', {
* width: 100,
* margin: '0 0 10 0',
* floating: false, // usually you want this set to True (default)
* renderTo: Ext.getBody(), // usually rendered by it's containing component
* items: [{
* text: 'regular item 1'
* },{
* text: 'regular item 2'
* },{
* text: 'regular item 3'
* }]
* });
*
* Ext.create('Ext.menu.Menu', {
* width: 100,
* plain: true,
* floating: false, // usually you want this set to True (default)
* renderTo: Ext.getBody(), // usually rendered by it's containing component
* items: [{
* text: 'plain item 1'
* },{
* text: 'plain item 2'
* },{
* text: 'plain item 3'
* }]
* });
*/
Ext.define('Ext.menu.Menu', {
extend: 'Ext.panel.Panel',
alias: 'widget.menu',
requires: [
'Ext.layout.container.Fit',
'Ext.layout.container.VBox',
'Ext.menu.CheckItem',
'Ext.menu.Item',
'Ext.menu.KeyNav',
'Ext.menu.Manager',
'Ext.menu.Separator'
],
<span id='Ext-menu-Menu-property-parentMenu'> /**
</span> * @property {Ext.menu.Menu} parentMenu
* The parent Menu of this Menu.
*/
<span id='Ext-menu-Menu-cfg-enableKeyNav'> /**
</span> * @cfg {Boolean} [enableKeyNav=true]
* True to enable keyboard navigation for controlling the menu.
* This option should generally be disabled when form fields are
* being used inside the menu.
*/
enableKeyNav: true,
<span id='Ext-menu-Menu-cfg-allowOtherMenus'> /**
</span> * @cfg {Boolean} [allowOtherMenus=false]
* True to allow multiple menus to be displayed at the same time.
*/
allowOtherMenus: false,
<span id='Ext-menu-Menu-cfg-ariaRole'> /**
</span> * @cfg {String} ariaRole
* @private
*/
ariaRole: 'menu',
<span id='Ext-menu-Menu-cfg-autoRender'> /**
</span> * @cfg {Boolean} autoRender
* Floating is true, so autoRender always happens.
* @private
*/
<span id='Ext-menu-Menu-cfg-defaultAlign'> /**
</span> * @cfg {String} [defaultAlign="tl-bl?"]
* The default {@link Ext.Element#getAlignToXY Ext.Element#getAlignToXY} anchor position value for this menu
* relative to its element of origin.
*/
defaultAlign: 'tl-bl?',
<span id='Ext-menu-Menu-cfg-floating'> /**
</span> * @cfg {Boolean} [floating=true]
* A Menu configured as `floating: true` (the default) will be rendered as an absolutely positioned,
* {@link Ext.Component#floating floating} {@link Ext.Component Component}. If configured as `floating: false`, the Menu may be
* used as a child item of another {@link Ext.container.Container Container}.
*/
floating: true,
<span id='Ext-menu-Menu-cfg-constrain'> /**
</span> * @cfg {Boolean} constrain
* Menus are constrained to the document body by default.
* @private
*/
constrain: true,
<span id='Ext-menu-Menu-cfg-hidden'> /**
</span> * @cfg {Boolean} [hidden=undefined]
* True to initially render the Menu as hidden, requiring to be shown manually.
*
* Defaults to `true` when `floating: true`, and defaults to `false` when `floating: false`.
*/
hidden: true,
hideMode: 'visibility',
<span id='Ext-menu-Menu-cfg-ignoreParentClicks'> /**
</span> * @cfg {Boolean} [ignoreParentClicks=false]
* True to ignore clicks on any item in this menu that is a parent item (displays a submenu)
* so that the submenu is not dismissed when clicking the parent item.
*/
ignoreParentClicks: false,
<span id='Ext-menu-Menu-property-isMenu'> /**
</span> * @property {Boolean} isMenu
* `true` in this class to identify an object as an instantiated Menu, or subclass thereof.
*/
isMenu: true,
<span id='Ext-menu-Menu-cfg-layout'> /**
</span> * @cfg {String/Object} layout
* @private
*/
<span id='Ext-menu-Menu-cfg-showSeparator'> /**
</span> * @cfg {Boolean} [showSeparator=true]
* True to show the icon separator.
*/
showSeparator : true,
<span id='Ext-menu-Menu-cfg-minWidth'> /**
</span> * @cfg {Number} [minWidth=120]
* The minimum width of the Menu. The default minWidth only applies when the {@link #floating} config is true.
*/
minWidth: undefined,
defaultMinWidth: 120,
<span id='Ext-menu-Menu-cfg-plain'> /**
</span> * @cfg {Boolean} [plain=false]
* True to remove the incised line down the left side of the menu and to not indent general Component items.
*/
initComponent: function() {
var me = this,
prefix = Ext.baseCSSPrefix,
cls = [prefix + 'menu'],
bodyCls = me.bodyCls ? [me.bodyCls] : [],
isFloating = me.floating !== false;
me.addEvents(
<span id='Ext-menu-Menu-event-click'> /**
</span> * @event click
* Fires when this menu is clicked
* @param {Ext.menu.Menu} menu The menu which has been clicked
* @param {Ext.Component} item The menu item that was clicked. `undefined` if not applicable.
* @param {Ext.EventObject} e The underlying {@link Ext.EventObject}.
*/
'click',
<span id='Ext-menu-Menu-event-mouseenter'> /**
</span> * @event mouseenter
* Fires when the mouse enters this menu
* @param {Ext.menu.Menu} menu The menu
* @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
*/
'mouseenter',
<span id='Ext-menu-Menu-event-mouseleave'> /**
</span> * @event mouseleave
* Fires when the mouse leaves this menu
* @param {Ext.menu.Menu} menu The menu
* @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
*/
'mouseleave',
<span id='Ext-menu-Menu-event-mouseover'> /**
</span> * @event mouseover
* Fires when the mouse is hovering over this menu
* @param {Ext.menu.Menu} menu The menu
* @param {Ext.Component} item The menu item that the mouse is over. `undefined` if not applicable.
* @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
*/
'mouseover'
);
Ext.menu.Manager.register(me);
// Menu classes
if (me.plain) {
cls.push(prefix + 'menu-plain');
}
me.cls = cls.join(' ');
// Menu body classes
bodyCls.unshift(prefix + 'menu-body');
me.bodyCls = bodyCls.join(' ');
// Internal vbox layout, with scrolling overflow
// Placed in initComponent (rather than prototype) in order to support dynamic layout/scroller
// options if we wish to allow for such configurations on the Menu.
// e.g., scrolling speed, vbox align stretch, etc.
if (!me.layout) {
me.layout = {
type: 'vbox',
align: 'stretchmax',
overflowHandler: 'Scroller'
};
}
// only apply the minWidth when we're floating & one hasn't already been set
if (isFloating && me.minWidth === undefined) {
me.minWidth = me.defaultMinWidth;
}
// hidden defaults to false if floating is configured as false
if (!isFloating && me.initialConfig.hidden !== true) {
me.hidden = false;
}
me.callParent(arguments);
me.on('beforeshow', function() {
var hasItems = !!me.items.length;
// FIXME: When a menu has its show cancelled because of no items, it
// gets a visibility: hidden applied to it (instead of the default display: none)
// Not sure why, but we remove this style when we want to show again.
if (hasItems && me.rendered) {
me.el.setStyle('visibility', null);
}
return hasItems;
});
},
beforeRender: function() {
this.callParent(arguments);
// Menus are usually floating: true, which means they shrink wrap their items.
// However, when they are contained, and not auto sized, we must stretch the items.
if (!this.getSizeModel().width.shrinkWrap) {
this.layout.align = 'stretch';
}
},
onBoxReady: function() {
var me = this,
separatorSpec;
me.callParent(arguments);
// TODO: Move this to a subTemplate When we support them in the future
if (me.showSeparator) {
separatorSpec = {
cls: Ext.baseCSSPrefix + 'menu-icon-separator',
html: '&#160;'
};
if ((!Ext.isStrict && Ext.isIE) || Ext.isIE6) {
separatorSpec.style = 'height:' + me.el.getHeight() + 'px';
}
me.iconSepEl = me.layout.getElementTarget().insertFirst(separatorSpec);
}
me.mon(me.el, {
click: me.onClick,
mouseover: me.onMouseOver,
scope: me
});
me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me);
if (me.enableKeyNav) {
me.keyNav = new Ext.menu.KeyNav(me);
}
},
getBubbleTarget: function() {
// If a submenu, this will have a parentMenu property
// If a menu of a Button, it will have an ownerButton property
// Else use the default method.
return this.parentMenu || this.ownerButton || this.callParent(arguments);
},
<span id='Ext-menu-Menu-method-canActivateItem'> /**
</span> * Returns whether a menu item can be activated or not.
* @return {Boolean}
*/
canActivateItem: function(item) {
return item && !item.isDisabled() && item.isVisible() && (item.canActivate || item.getXTypes().indexOf('menuitem') < 0);
},
<span id='Ext-menu-Menu-method-deactivateActiveItem'> /**
</span> * Deactivates the current active item on the menu, if one exists.
*/
deactivateActiveItem: function(andBlurFocusedItem) {
var me = this,
activeItem = me.activeItem,
focusedItem = me.focusedItem;
if (activeItem) {
activeItem.deactivate();
if (!activeItem.activated) {
delete me.activeItem;
}
}
// Blur the focused item if we are being asked to do that too
// Only needed if we are being hidden - mouseout does not blur.
if (focusedItem && andBlurFocusedItem) {
focusedItem.blur();
delete me.focusedItem;
}
},
// inherit docs
getFocusEl: function() {
return this.focusedItem || this.el;
},
// inherit docs
hide: function() {
this.deactivateActiveItem(true);
this.callParent(arguments);
},
// private
getItemFromEvent: function(e) {
return this.getChildByElement(e.getTarget());
},
lookupComponent: function(cmp) {
var me = this;
if (typeof cmp == 'string') {
cmp = me.lookupItemFromString(cmp);
} else if (Ext.isObject(cmp)) {
cmp = me.lookupItemFromObject(cmp);
}
// Apply our minWidth to all of our child components so it's accounted
// for in our VBox layout
cmp.minWidth = cmp.minWidth || me.minWidth;
return cmp;
},
// private
lookupItemFromObject: function(cmp) {
var me = this,
prefix = Ext.baseCSSPrefix,
cls;
if (!cmp.isComponent) {
if (!cmp.xtype) {
cmp = Ext.create('Ext.menu.' + (Ext.isBoolean(cmp.checked) ? 'Check': '') + 'Item', cmp);
} else {
cmp = Ext.ComponentManager.create(cmp, cmp.xtype);
}
}
if (cmp.isMenuItem) {
cmp.parentMenu = me;
}
if (!cmp.isMenuItem && !cmp.dock) {
cls = [prefix + 'menu-item', prefix + 'menu-item-cmp'];
if (!me.plain && (cmp.indent === true || cmp.iconCls === 'no-icon')) {
cls.push(prefix + 'menu-item-indent');
}
if (cmp.rendered) {
cmp.el.addCls(cls);
} else {
cmp.cls = (cmp.cls ? cmp.cls : '') + ' ' + cls.join(' ');
}
}
return cmp;
},
// private
lookupItemFromString: function(cmp) {
return (cmp == 'separator' || cmp == '-') ?
new Ext.menu.Separator()
: new Ext.menu.Item({
canActivate: false,
hideOnClick: false,
plain: true,
text: cmp
});
},
onClick: function(e) {
var me = this,
item;
if (me.disabled) {
e.stopEvent();
return;
}
item = (e.type === 'click') ? me.getItemFromEvent(e) : me.activeItem;
if (item && item.isMenuItem) {
if (!item.menu || !me.ignoreParentClicks) {
item.onClick(e);
} else {
e.stopEvent();
}
}
// Click event may be fired without an item, so we need a second check
if (!item || item.disabled) {
item = undefined;
}
me.fireEvent('click', me, item, e);
},
onDestroy: function() {
var me = this;
Ext.menu.Manager.unregister(me);
delete me.parentMenu;
delete me.ownerButton;
if (me.rendered) {
me.el.un(me.mouseMonitor);
Ext.destroy(me.keyNav);
delete me.keyNav;
}
me.callParent(arguments);
},
onMouseLeave: function(e) {
var me = this;
me.deactivateActiveItem();
if (me.disabled) {
return;
}
me.fireEvent('mouseleave', me, e);
},
onMouseOver: function(e) {
var me = this,
fromEl = e.getRelatedTarget(),
mouseEnter = !me.el.contains(fromEl),
item = me.getItemFromEvent(e),
parentMenu = me.parentMenu,
parentItem = me.parentItem;
if (mouseEnter && parentMenu) {
parentMenu.setActiveItem(parentItem);
parentItem.cancelDeferHide();
parentMenu.mouseMonitor.mouseenter();
}
if (me.disabled) {
return;
}
// Do not activate the item if the mouseover was within the item, and it's already active
if (item && !item.activated) {
me.setActiveItem(item);
if (item.activated && item.expandMenu) {
item.expandMenu();
}
}
if (mouseEnter) {
me.fireEvent('mouseenter', me, e);
}
me.fireEvent('mouseover', me, item, e);
},
setActiveItem: function(item) {
var me = this;
if (item && (item != me.activeItem)) {
me.deactivateActiveItem();
if (me.canActivateItem(item)) {
if (item.activate) {
item.activate();
if (item.activated) {
me.activeItem = item;
me.focusedItem = item;
me.focus();
}
} else {
item.focus();
me.focusedItem = item;
}
}
item.el.scrollIntoView(me.layout.getRenderTarget());
}
},
<span id='Ext-menu-Menu-method-showBy'> /**
</span> * Shows the floating menu by the specified {@link Ext.Component Component} or {@link Ext.Element Element}.
* @param {Ext.Component/Ext.Element} component The {@link Ext.Component} or {@link Ext.Element} to show the menu by.
* @param {String} [position] Alignment position as used by {@link Ext.Element#getAlignToXY}.
* Defaults to `{@link #defaultAlign}`.
* @param {Number[]} [offsets] Alignment offsets as used by {@link Ext.Element#getAlignToXY}.
* @return {Ext.menu.Menu} This Menu.
*/
showBy: function(cmp, pos, off) {
var me = this;
if (me.floating && cmp) {
me.show();
// Align to Component or Element using setPagePosition because normal show
// methods are container-relative, and we must align to the requested element
// or Component:
me.setPagePosition(me.el.getAlignToXY(cmp.el || cmp, pos || me.defaultAlign, off));
me.setVerticalPosition();
}
return me;
},
show: function() {
var me = this,
parentEl, viewHeight, result,
maxWas = me.maxHeight;
// we need to get scope parent for height constraint
if (!me.rendered){
me.doAutoRender();
}
// constrain the height to the curren viewable area
if (me.floating) {
//if our reset css is scoped, there will be a x-reset wrapper on this menu which we need to skip
parentEl = Ext.fly(me.el.getScopeParent());
viewHeight = parentEl.getViewSize().height;
me.maxHeight = Math.min(maxWas || viewHeight, viewHeight);
}
result = me.callParent(arguments);
me.maxHeight = maxWas;
return result;
},
afterComponentLayout: function(width, height, oldWidth, oldHeight){
var me = this;
me.callParent(arguments);
// fixup the separator
if (me.showSeparator){
me.iconSepEl.setHeight(me.componentLayout.lastComponentSize.contentHeight);
}
},
// private
// adjust the vertical position of the menu if the height of the
// menu is equal (or greater than) the viewport size
setVerticalPosition: function(){
var me = this,
max,
y = me.el.getY(),
returnY = y,
height = me.getHeight(),
viewportHeight = Ext.Element.getViewportHeight().height,
parentEl = Ext.fly(me.el.getScopeParent()),
viewHeight = parentEl.getViewSize().height,
normalY = y - parentEl.getScroll().top; // factor in scrollTop of parent
parentEl = null;
if (me.floating) {
max = me.maxHeight ? me.maxHeight : viewHeight - normalY;
if (height > viewHeight) {
returnY = y - normalY;
} else if (max < height) {
returnY = y - (height - max);
} else if((y + height) > viewportHeight){ // keep the document from scrolling
returnY = viewportHeight - height;
}
}
me.el.setY(returnY);
}
});</pre>
</body>
</html>