Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / docs / source / Menu2.html
diff --git a/docs/source/Menu2.html b/docs/source/Menu2.html
new file mode 100644 (file)
index 0000000..cd93875
--- /dev/null
@@ -0,0 +1,591 @@
+<!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-menu.Menu-method-constructor'><span id='Ext-menu.Menu'>/**
+</span></span> * @class Ext.menu.Menu
+ * @extends Ext.panel.Panel
+ *
+ * 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#iconCls iconCls}: 'no-icon'` _or_ `{@link Ext.menu.Item#indent indent}: true`.
+ * This reserves a space for an icon, and indents the Component in line with the other menu items.
+ * See {@link Ext.form.field.ComboBox}.{@link Ext.form.field.ComboBox#getListParent getListParent} for an example.
+
+ * 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}.
+ * {@img Ext.menu.Item/Ext.menu.Item.png Ext.menu.Item component}
+__Example Usage__
+        Ext.create('Ext.menu.Menu', {
+                width: 100,
+                height: 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,
+                height: 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'
+                }]
+        }); 
+ * @xtype menu
+ * @markdown
+ * @constructor
+ * @param {Object} config The config object
+ */
+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-cfg-allowOtherMenus'>    /**
+</span>     * @cfg {Boolean} allowOtherMenus
+     * True to allow multiple menus to be displayed at the same time. Defaults to `false`.
+     * @markdown
+     */
+    allowOtherMenus: false,
+
+<span id='Ext-menu.Menu-cfg-ariaRole'>    /**
+</span>     * @cfg {String} ariaRole @hide
+     */
+    ariaRole: 'menu',
+
+<span id='Ext-menu.Menu-cfg-autoRender'>    /**
+</span>     * @cfg {Boolean} autoRender @hide
+     * floating is true, so autoRender always happens
+     */
+
+<span id='Ext-menu.Menu-cfg-defaultAlign'>    /**
+</span>     * @cfg {String} defaultAlign
+     * The default {@link Ext.core.Element#getAlignToXY Ext.core.Element#getAlignToXY} anchor position value for this menu
+     * relative to its element of origin. Defaults to `'tl-bl?'`.
+     * @markdown
+     */
+    defaultAlign: 'tl-bl?',
+
+<span id='Ext-menu.Menu-cfg-floating'>    /**
+</span>     * @cfg {Boolean} floating
+     * 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}.
+     * @markdown
+     */
+    floating: true,
+
+<span id='Ext-menu.Menu-cfg-constrain'>    /**
+</span>     * @cfg {Boolean} @hide
+     * Menu performs its own size changing constraining, so ensure Component's constraining is not applied
+     */
+    constrain: false,
+
+<span id='Ext-menu.Menu-cfg-hidden'>    /**
+</span>     * @cfg {Boolean} hidden
+     * 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`.
+     * @markdown
+     */
+    hidden: true,
+
+<span id='Ext-menu.Menu-cfg-ignoreParentClicks'>    /**
+</span>     * @cfg {Boolean} ignoreParentClicks
+     * 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. Defaults to `false`.
+     * @markdown
+     */
+    ignoreParentClicks: false,
+
+    isMenu: true,
+
+<span id='Ext-menu.Menu-cfg-layout'>    /**
+</span>     * @cfg {String/Object} layout @hide
+     */
+
+<span id='Ext-menu.Menu-cfg-showSeparator'>    /**
+</span>     * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).
+     */
+    showSeparator : true,
+
+<span id='Ext-menu.Menu-cfg-minWidth'>    /**
+</span>     * @cfg {Number} minWidth
+     * The minimum width of the Menu. Defaults to `120`.
+     * @markdown
+     */
+    minWidth: 120,
+
+<span id='Ext-menu.Menu-cfg-plain'>    /**
+</span>     * @cfg {Boolean} plain
+     * True to remove the incised line down the left side of the menu and to not
+     * indent general Component items. Defaults to `false`.
+     * @markdown
+     */
+
+    initComponent: function() {
+        var me = this,
+            prefix = Ext.baseCSSPrefix;
+
+        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}.
+             * @markdown
+             */
+            '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}
+             * @markdown
+             */
+            '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}
+             * @markdown
+             */
+            '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
+        var cls = [prefix + 'menu'];
+        if (me.plain) {
+            cls.push(prefix + 'menu-plain');
+        }
+        me.cls = cls.join(' ');
+
+        // Menu body classes
+        var bodyCls = me.bodyCls ? [me.bodyCls] : [];
+        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.
+        me.layout = {
+            type: 'vbox',
+            align: 'stretchmax',
+            autoSize: true,
+            clearInnerCtOnLayout: true,
+            overflowHandler: 'Scroller'
+        };
+
+        // hidden defaults to false if floating is configured as false
+        if (me.floating === false &amp;&amp; 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 &amp;&amp; me.rendered) {
+                me.el.setStyle('visibility', null);
+            }
+            return hasItems;
+        });
+    },
+
+    afterRender: function(ct) {
+        var me = this,
+            prefix = Ext.baseCSSPrefix,
+            space = '&amp;#160;';
+
+        me.callParent(arguments);
+
+        // TODO: Move this to a subTemplate When we support them in the future
+        if (me.showSeparator) {
+            me.iconSepEl = me.layout.getRenderTarget().insertFirst({
+                cls: prefix + 'menu-icon-separator',
+                html: space
+            });
+        }
+
+        me.focusEl = me.el.createChild({
+            cls: prefix + 'menu-focus',
+            tabIndex: '-1',
+            html: space
+        });
+
+        me.mon(me.el, {
+            click: me.onClick,
+            mouseover: me.onMouseOver,
+            scope: me
+        });
+        me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me);
+
+        if (me.showSeparator &amp;&amp; ((!Ext.isStrict &amp;&amp; Ext.isIE) || Ext.isIE6)) {
+            me.iconSepEl.setHeight(me.el.getHeight());
+        }
+
+        me.keyNav = Ext.create('Ext.menu.KeyNav', me);
+    },
+
+    afterLayout: function() {
+        var me = this;
+        me.callParent(arguments);
+
+        // For IE6 &amp; IE quirks, we have to resize the el and body since position: absolute
+        // floating elements inherit their parent's width, making them the width of
+        // document.body instead of the width of their contents.
+        // This includes left/right dock items.
+        if ((!Ext.iStrict &amp;&amp; Ext.isIE) || Ext.isIE6) {
+            var innerCt = me.layout.getRenderTarget(),
+                innerCtWidth = 0,
+                dis = me.dockedItems,
+                l = dis.length,
+                i = 0,
+                di, clone, newWidth;
+
+            innerCtWidth = innerCt.getWidth();
+
+            newWidth = innerCtWidth + me.body.getBorderWidth('lr') + me.body.getPadding('lr');
+
+            // First set the body to the new width
+            me.body.setWidth(newWidth);
+
+            // Now we calculate additional width (docked items) and set the el's width
+            for (; i &lt; l, di = dis.getAt(i); i++) {
+                if (di.dock == 'left' || di.dock == 'right') {
+                    newWidth += di.getWidth();
+                }
+            }
+            me.el.setWidth(newWidth);
+        }
+    },
+
+<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 &amp;&amp; !item.isDisabled() &amp;&amp; item.isVisible() &amp;&amp; (item.canActivate || item.getXTypes().indexOf('menuitem') &lt; 0);
+    },
+
+<span id='Ext-menu.Menu-method-deactivateActiveItem'>    /**
+</span>     * Deactivates the current active item on the menu, if one exists.
+     */
+    deactivateActiveItem: function() {
+        var me = this;
+
+        if (me.activeItem) {
+            me.activeItem.deactivate();
+            if (!me.activeItem.activated) {
+                delete me.activeItem;
+            }
+        }
+        if (me.focusedItem) {
+            me.focusedItem.blur();
+            if (!me.focusedItem.$focused) {
+                delete me.focusedItem;
+            }
+        }
+    },
+
+    // inherit docs
+    getFocusEl: function() {
+        return this.focusEl;
+    },
+
+    // inherit docs
+    hide: function() {
+        this.deactivateActiveItem();
+        this.callParent(arguments);
+    },
+
+    // private
+    getItemFromEvent: function(e) {
+        return this.getChildByElement(e.getTarget());
+    },
+
+    lookupComponent: function(cmp) {
+        var me = this;
+
+        if (Ext.isString(cmp)) {
+            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;
+
+        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 &amp;&amp; !cmp.dock) {
+            var cls = [
+                    prefix + 'menu-item',
+                    prefix + 'menu-item-cmp'
+                ],
+                intercept = Ext.Function.createInterceptor;
+
+            // Wrap focus/blur to control component focus
+            cmp.focus = intercept(cmp.focus, function() {
+                this.$focused = true;
+            }, cmp);
+            cmp.blur = intercept(cmp.blur, function() {
+                this.$focused = false;
+            }, cmp);
+
+            if (!me.plain &amp;&amp; (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(' ');
+            }
+            cmp.isMenuItem = true;
+        }
+        return cmp;
+    },
+
+    // private
+    lookupItemFromString: function(cmp) {
+        return (cmp == 'separator' || cmp == '-') ?
+            Ext.createWidget('menuseparator')
+            : Ext.createWidget('menuitem', {
+                canActivate: false,
+                hideOnClick: false,
+                plain: true,
+                text: cmp
+            });
+    },
+
+    onClick: function(e) {
+        var me = this,
+            item;
+
+        if (me.disabled) {
+            e.stopEvent();
+            return;
+        }
+
+        if ((e.getTarget() == me.focusEl.dom) || e.within(me.layout.getRenderTarget())) {
+            item = me.getItemFromEvent(e) || me.activeItem;
+
+            if (item) {
+                if (item.getXTypes().indexOf('menuitem') &gt;= 0) {
+                    if (!item.menu || !me.ignoreParentClicks) {
+                        item.onClick(e);
+                    } else {
+                        e.stopEvent();
+                    }
+                }
+            }
+            me.fireEvent('click', me, item, e);
+        }
+    },
+
+    onDestroy: function() {
+        var me = this;
+
+        Ext.menu.Manager.unregister(me);
+        if (me.rendered) {
+            me.el.un(me.mouseMonitor);
+            me.keyNav.destroy();
+            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);
+
+        if (mouseEnter &amp;&amp; me.parentMenu) {
+            me.parentMenu.setActiveItem(me.parentItem);
+            me.parentMenu.mouseMonitor.mouseenter();
+        }
+
+        if (me.disabled) {
+            return;
+        }
+
+        if (item) {
+            me.setActiveItem(item);
+            if (item.activated &amp;&amp; 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 &amp;&amp; (item != me.activeItem &amp;&amp; item != me.focusedItem)) {
+            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.core.Element Element}.
+     * @param {Mixed component} The {@link Ext.Component} or {@link Ext.core.Element} to show the menu by.
+     * @param {String} position (optional) Alignment position as used by {@link Ext.core.Element#getAlignToXY Ext.core.Element.getAlignToXY}. Defaults to `{@link #defaultAlign}`.
+     * @param {Array} offsets (optional) Alignment offsets as used by {@link Ext.core.Element#getAlignToXY Ext.core.Element.getAlignToXY}. Defaults to `undefined`.
+     * @return {Menu} This Menu.
+     * @markdown
+     */
+    showBy: function(cmp, pos, off) {
+        var me = this;
+
+        if (me.floating &amp;&amp; cmp) {
+            me.layout.autoSize = true;
+            me.show();
+
+            // Component or Element
+            cmp = cmp.el || cmp;
+
+            // Convert absolute to floatParent-relative coordinates if necessary.
+            var xy = me.el.getAlignToXY(cmp, pos || me.defaultAlign, off);
+            if (me.floatParent) {
+                var r = me.floatParent.getTargetEl().getViewRegion();
+                xy[0] -= r.x;
+                xy[1] -= r.y;
+            }
+            me.showAt(xy);
+            me.doConstrain();
+        }
+        return me;
+    },
+
+    doConstrain : function() {
+        var me = this,
+            y = this.el.getY(),
+            max, full,
+            returnY = y, normalY, parentEl, scrollTop, viewHeight;
+
+        delete me.height;
+        me.setSize();
+        full = me.getHeight();
+        if (me.floating) {
+            parentEl = Ext.fly(me.el.dom.parentNode);
+            scrollTop = parentEl.getScroll().top;
+            viewHeight = parentEl.getViewSize().height;
+            //Normalize y by the scroll position for the parent element.  Need to move it into the coordinate space
+            //of the view.
+            normalY = y - scrollTop;
+            max = me.maxHeight ? me.maxHeight : viewHeight - normalY;
+            if (full &gt; viewHeight) {
+                max = viewHeight;
+                //Set returnY equal to (0,0) in view space by reducing y by the value of normalY
+                returnY = y - normalY;
+            } else if (max &lt; full) {
+                returnY = y - (full - max);
+                max = full;
+            }
+        }else{
+            max = me.getHeight();
+        }
+        // Always respect maxHeight
+        if (me.maxHeight){
+            max = Math.min(me.maxHeight, max);
+        }
+        if (full &gt; max &amp;&amp; max &gt; 0){
+            me.layout.autoSize = false;
+            me.setHeight(max);
+            if (me.showSeparator){
+                me.iconSepEl.setHeight(me.layout.getRenderTarget().dom.scrollHeight);
+            }
+        }
+        me.el.setY(returnY);
+    }
+});</pre></pre></body></html>
\ No newline at end of file