3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * Panel is a container that has specific functionality and structural components that make it the perfect building
17 * block for application-oriented user interfaces.
19 * Panels are, by virtue of their inheritance from {@link Ext.container.Container}, capable of being configured with a
20 * {@link Ext.container.Container#layout layout}, and containing child Components.
22 * When either specifying child {@link #items} of a Panel, or dynamically {@link Ext.container.Container#add adding}
23 * Components to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether those
24 * child elements need to be sized using one of Ext's built-in `{@link Ext.container.Container#layout layout}`
25 * schemes. By default, Panels use the {@link Ext.layout.container.Auto Auto} scheme. This simply renders child
26 * components, appending them one after the other inside the Container, and **does not apply any sizing** at all.
28 * {@img Ext.panel.Panel/panel.png Panel components}
30 * A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate {@link
31 * Ext.panel.Header header}, {@link #fbar footer} and body sections.
33 * Panel also provides built-in {@link #collapsible collapsible, expandable} and {@link #closable} behavior. Panels can
34 * be easily dropped into any {@link Ext.container.Container Container} or layout, and the layout and rendering pipeline
35 * is {@link Ext.container.Container#add completely managed by the framework}.
37 * **Note:** By default, the `{@link #closable close}` header tool _destroys_ the Panel resulting in removal of the
38 * Panel and the destruction of any descendant Components. This makes the Panel object, and all its descendants
39 * **unusable**. To enable the close tool to simply _hide_ a Panel for later re-use, configure the Panel with
40 * `{@link #closeAction closeAction}: 'hide'`.
42 * Usually, Panels are used as constituents within an application, in which case, they would be used as child items of
43 * Containers, and would themselves use Ext.Components as child {@link #items}. However to illustrate simply rendering a
44 * Panel into the document, here's how to do it:
47 * Ext.create('Ext.panel.Panel', {
50 * html: '<p>World!</p>',
51 * renderTo: Ext.getBody()
54 * A more realistic scenario is a Panel created to house input fields which will not be rendered, but used as a
55 * constituent part of a Container:
58 * var filterPanel = Ext.create('Ext.panel.Panel', {
59 * bodyPadding: 5, // Don't want content to crunch against the borders
64 * fieldLabel: 'Start date'
67 * fieldLabel: 'End date'
69 * renderTo: Ext.getBody()
72 * Note that the Panel above is not configured to render into the document, nor is it configured with a size or
73 * position. In a real world scenario, the Container into which the Panel is added will use a {@link #layout} to render,
74 * size and position its child Components.
76 * Panels will often use specific {@link #layout}s to provide an application with shape and structure by containing and
77 * arranging child Components:
80 * var resultsPanel = Ext.create('Ext.panel.Panel', {
84 * renderTo: Ext.getBody(),
86 * type: 'vbox', // Arrange child items vertically
87 * align: 'stretch', // Each takes up full width
90 * items: [{ // Results grid specified as a config object with an xtype of 'grid'
92 * columns: [{header: 'Column One'}], // One header just for show. There's no data,
93 * store: Ext.create('Ext.data.ArrayStore', {}), // A dummy empty data store
94 * flex: 1 // Use 1/3 of Container's height (hint to Box layout)
96 * xtype: 'splitter' // A splitter between the two child items
97 * }, { // Details Panel specified as a config object (no xtype defaults to 'panel').
101 * fieldLabel: 'Data item',
103 * }], // An array of form fields
104 * flex: 2 // Use 2/3 of Container's height (hint to Box layout)
108 * The example illustrates one possible method of displaying search results. The Panel contains a grid with the
109 * resulting data arranged in rows. Each selected row may be displayed in detail in the Panel below. The {@link
110 * Ext.layout.container.VBox vbox} layout is used to arrange the two vertically. It is configured to stretch child items
111 * horizontally to full width. Child items may either be configured with a numeric height, or with a `flex` value to
112 * distribute available space proportionately.
114 * This Panel itself may be a child item of, for exaple, a {@link Ext.tab.Panel} which will size its child items to fit
115 * within its content area.
117 * Using these techniques, as long as the **layout** is chosen and configured correctly, an application may have any
118 * level of nested containment, all dynamically sized according to configuration, the user's preference and available
121 Ext.define('Ext.panel.Panel', {
122 extend: 'Ext.panel.AbstractPanel',
129 'Ext.layout.component.Dock',
132 alias: 'widget.panel',
133 alternateClassName: 'Ext.Panel',
136 * @cfg {String} collapsedCls
137 * A CSS class to add to the panel's element after it has been collapsed.
139 collapsedCls: 'collapsed',
142 * @cfg {Boolean} animCollapse
143 * `true` to animate the transition when the panel is collapsed, `false` to skip the animation (defaults to `true`
144 * if the {@link Ext.fx.Anim} class is available, otherwise `false`). May also be specified as the animation
145 * duration in milliseconds.
147 animCollapse: Ext.enableFx,
150 * @cfg {Number} minButtonWidth
151 * Minimum width of all footer toolbar buttons in pixels. If set, this will be used as the default
152 * value for the {@link Ext.button.Button#minWidth} config of each Button added to the **footer toolbar** via the
153 * {@link #fbar} or {@link #buttons} configurations. It will be ignored for buttons that have a minWidth configured
154 * some other way, e.g. in their own config object or via the {@link Ext.container.Container#defaults defaults} of
155 * their parent container.
160 * @cfg {Boolean} collapsed
161 * `true` to render the panel collapsed, `false` to render it expanded.
166 * @cfg {Boolean} collapseFirst
167 * `true` to make sure the collapse/expand toggle button always renders first (to the left of) any other tools in
168 * the panel's title bar, `false` to render it last.
173 * @cfg {Boolean} hideCollapseTool
174 * `true` to hide the expand/collapse toggle button when `{@link #collapsible} == true`, `false` to display it.
176 hideCollapseTool: false,
179 * @cfg {Boolean} titleCollapse
180 * `true` to allow expanding and collapsing the panel (when `{@link #collapsible} = true`) by clicking anywhere in
181 * the header bar, `false`) to allow it only by clicking to tool butto).
183 titleCollapse: false,
186 * @cfg {String} collapseMode
187 * **Important: this config is only effective for {@link #collapsible} Panels which are direct child items of a
188 * {@link Ext.layout.container.Border border layout}.**
190 * When _not_ a direct child item of a {@link Ext.layout.container.Border border layout}, then the Panel's header
191 * remains visible, and the body is collapsed to zero dimensions. If the Panel has no header, then a new header
192 * (orientated correctly depending on the {@link #collapseDirection}) will be inserted to show a the title and a re-
195 * When a child item of a {@link Ext.layout.container.Border border layout}, this config has two options:
197 * - **`undefined/omitted`**
199 * When collapsed, a placeholder {@link Ext.panel.Header Header} is injected into the layout to represent the Panel
200 * and to provide a UI with a Tool to allow the user to re-expand the Panel.
204 * The Panel collapses to leave its header visible as when not inside a {@link Ext.layout.container.Border border
209 * @cfg {Ext.Component/Object} placeholder
210 * **Important: This config is only effective for {@link #collapsible} Panels which are direct child items of a
211 * {@link Ext.layout.container.Border border layout} when not using the `'header'` {@link #collapseMode}.**
213 * **Optional.** A Component (or config object for a Component) to show in place of this Panel when this Panel is
214 * collapsed by a {@link Ext.layout.container.Border border layout}. Defaults to a generated {@link Ext.panel.Header
215 * Header} containing a {@link Ext.panel.Tool Tool} to re-expand the Panel.
219 * @cfg {Boolean} floatable
220 * **Important: This config is only effective for {@link #collapsible} Panels which are direct child items of a
221 * {@link Ext.layout.container.Border border layout}.**
223 * true to allow clicking a collapsed Panel's {@link #placeholder} to display the Panel floated above the layout,
224 * false to force the user to fully expand a collapsed region by clicking the expand button to see it again.
229 * @cfg {Boolean} overlapHeader
230 * True to overlap the header in a panel over the framing of the panel itself. This is needed when frame:true (and
231 * is done automatically for you). Otherwise it is undefined. If you manually add rounded corners to a panel header
232 * which does not have frame:true, this will need to be set to true.
236 * @cfg {Boolean} collapsible
237 * True to make the panel collapsible and have an expand/collapse toggle Tool added into the header tool button
238 * area. False to keep the panel sized either statically, or by an owning layout manager, with no toggle Tool.
240 * See {@link #collapseMode} and {@link #collapseDirection}
245 * @cfg {Boolean} collapseDirection
246 * The direction to collapse the Panel when the toggle button is clicked.
248 * Defaults to the {@link #headerPosition}
250 * **Important: This config is _ignored_ for {@link #collapsible} Panels which are direct child items of a {@link
251 * Ext.layout.container.Border border layout}.**
253 * Specify as `'top'`, `'bottom'`, `'left'` or `'right'`.
257 * @cfg {Boolean} closable
258 * True to display the 'close' tool button and allow the user to close the window, false to hide the button and
259 * disallow closing the window.
261 * By default, when close is requested by clicking the close button in the header, the {@link #close} method will be
262 * called. This will _{@link Ext.Component#destroy destroy}_ the Panel and its content meaning that it may not be
265 * To make closing a Panel _hide_ the Panel so that it may be reused, set {@link #closeAction} to 'hide'.
270 * @cfg {String} closeAction
271 * The action to take when the close header tool is clicked:
273 * - **`'{@link #destroy}'`** :
275 * {@link #destroy remove} the window from the DOM and {@link Ext.Component#destroy destroy} it and all descendant
276 * Components. The window will **not** be available to be redisplayed via the {@link #show} method.
278 * - **`'{@link #hide}'`** :
280 * {@link #hide} the window by setting visibility to hidden and applying negative offsets. The window will be
281 * available to be redisplayed via the {@link #show} method.
283 * **Note:** This behavior has changed! setting *does* affect the {@link #close} method which will invoke the
284 * approriate closeAction.
286 closeAction: 'destroy',
289 * @cfg {Object/Object[]} dockedItems
290 * A component or series of components to be added as docked items to this panel. The docked items can be docked to
291 * either the top, right, left or bottom of a panel. This is typically used for things like toolbars or tab bars:
293 * var panel = new Ext.panel.Panel({
298 * text: 'Docked to the top'
305 * @cfg {Boolean} preventHeader
306 * Prevent a Header from being created and shown.
308 preventHeader: false,
311 * @cfg {String} headerPosition
312 * Specify as `'top'`, `'bottom'`, `'left'` or `'right'`.
314 headerPosition: 'top',
317 * @cfg {Boolean} frame
318 * True to apply a frame to the panel.
323 * @cfg {Boolean} frameHeader
324 * True to apply a frame to the panel panels header (if 'frame' is true).
329 * @cfg {Object[]/Ext.panel.Tool[]} tools
330 * An array of {@link Ext.panel.Tool} configs/instances to be added to the header tool area. The tools are stored as
331 * child components of the header container. They can be accessed using {@link #down} and {#query}, as well as the
332 * other component methods. The toggle tool is automatically created if {@link #collapsible} is set to true.
334 * Note that, apart from the toggle tool which is provided when a panel is collapsible, these tools only provide the
335 * visual button. Any required functionality must be provided by adding handlers that implement the necessary
342 * tooltip: 'Refresh form Data',
344 * handler: function(event, toolEl, panel){
350 * tooltip: 'Get Help',
351 * handler: function(event, toolEl, panel){
358 * @cfg {String} [title='']
359 * The title text to be used to display in the {@link Ext.panel.Header panel header}. When a
360 * `title` is specified the {@link Ext.panel.Header} will automatically be created and displayed unless
361 * {@link #preventHeader} is set to `true`.
365 * @cfg {String} iconCls
366 * CSS class for icon in header. Used for displaying an icon to the left of a title.
369 initComponent: function() {
377 * Fires before the user closes the panel. Return false from any listener to stop the close event being
379 * @param {Ext.panel.Panel} panel The Panel object
384 * @event beforeexpand
385 * Fires before this panel is expanded. Return false to prevent the expand.
386 * @param {Ext.panel.Panel} p The Panel being expanded.
387 * @param {Boolean} animate True if the expand is animated, else false.
392 * @event beforecollapse
393 * Fires before this panel is collapsed. Return false to prevent the collapse.
394 * @param {Ext.panel.Panel} p The Panel being collapsed.
395 * @param {String} direction . The direction of the collapse. One of
397 * - Ext.Component.DIRECTION_TOP
398 * - Ext.Component.DIRECTION_RIGHT
399 * - Ext.Component.DIRECTION_BOTTOM
400 * - Ext.Component.DIRECTION_LEFT
402 * @param {Boolean} animate True if the collapse is animated, else false.
408 * Fires after this Panel has expanded.
409 * @param {Ext.panel.Panel} p The Panel that has been expanded.
415 * Fires after this Panel hass collapsed.
416 * @param {Ext.panel.Panel} p The Panel that has been collapsed.
422 * Fires after the Panel title has been set or changed.
423 * @param {Ext.panel.Panel} p the Panel which has been resized.
424 * @param {String} newTitle The new title.
425 * @param {String} oldTitle The previous panel title.
431 * Fires after the Panel iconCls has been set or changed.
432 * @param {Ext.panel.Panel} p the Panel which has been resized.
433 * @param {String} newIconCls The new iconCls.
434 * @param {String} oldIconCls The previous panel iconCls.
439 // Save state on these two events.
440 this.addStateEvents('expand', 'collapse');
447 me.setUI(me.ui + '-framed');
450 // Backwards compatibility
454 me.collapseDirection = me.collapseDirection || me.headerPosition || Ext.Component.DIRECTION_TOP;
457 setBorder: function(border) {
459 // method = (border === false || border === 0) ? 'addClsWithUI' : 'removeClsWithUI';
461 // me.callParent(arguments);
463 // if (me.collapsed) {
464 // me[method](me.collapsedCls + '-noborder');
468 // me.header.setBorder(border);
469 // if (me.collapsed) {
470 // me.header[method](me.collapsedCls + '-noborder');
474 this.callParent(arguments);
477 beforeDestroy: function() {
485 initAria: function() {
487 this.initHeaderAria();
490 initHeaderAria: function() {
495 el.dom.setAttribute('aria-labelledby', header.titleCmp.id);
499 getHeader: function() {
504 * Set a title for the panel's header. See {@link Ext.panel.Header#title}.
505 * @param {String} newTitle
507 setTitle: function(newTitle) {
509 oldTitle = this.title;
513 me.header.setTitle(newTitle);
519 me.reExpander.setTitle(newTitle);
521 me.fireEvent('titlechange', me, newTitle, oldTitle);
525 * Set the iconCls for the panel's header. See {@link Ext.panel.Header#iconCls}. It will fire the
526 * {@link #iconchange} event after completion.
527 * @param {String} newIconCls The new CSS class name
529 setIconCls: function(newIconCls) {
531 oldIconCls = me.iconCls;
533 me.iconCls = newIconCls;
534 var header = me.header;
536 header.setIconCls(newIconCls);
538 me.fireEvent('iconchange', me, newIconCls, oldIconCls);
541 bridgeToolbars: function() {
546 minButtonWidth = me.minButtonWidth;
548 function initToolbar (toolbar, pos, useButtonAlign) {
549 if (Ext.isArray(toolbar)) {
555 else if (!toolbar.xtype) {
556 toolbar.xtype = 'toolbar';
559 if (pos == 'left' || pos == 'right') {
560 toolbar.vertical = true;
563 // Legacy support for buttonAlign (only used by buttons/fbar)
564 if (useButtonAlign) {
565 toolbar.layout = Ext.applyIf(toolbar.layout || {}, {
566 // default to 'end' (right-aligned) if me.buttonAlign is undefined or invalid
567 pack: { left:'start', center:'center' }[me.buttonAlign] || 'end'
573 // Short-hand toolbars (tbar, bbar and fbar plus new lbar and rbar):
576 * @cfg {String} buttonAlign
577 * The alignment of any buttons added to this panel. Valid values are 'right', 'left' and 'center' (defaults to
578 * 'right' for buttons/fbar, 'left' for other toolbar types).
580 * **NOTE:** The prefered way to specify toolbars is to use the dockedItems config. Instead of buttonAlign you
581 * would add the layout: { pack: 'start' | 'center' | 'end' } option to the dockedItem config.
585 * @cfg {Object/Object[]} tbar
586 * Convenience config. Short for 'Top Bar'.
589 * { xtype: 'button', text: 'Button 1' }
598 * { xtype: 'button', text: 'Button 1' }
603 docked.push(initToolbar(me.tbar, 'top'));
608 * @cfg {Object/Object[]} bbar
609 * Convenience config. Short for 'Bottom Bar'.
612 * { xtype: 'button', text: 'Button 1' }
621 * { xtype: 'button', text: 'Button 1' }
626 docked.push(initToolbar(me.bbar, 'bottom'));
631 * @cfg {Object/Object[]} buttons
632 * Convenience config used for adding buttons docked to the bottom of the panel. This is a
633 * synonym for the {@link #fbar} config.
636 * { text: 'Button 1' }
645 * defaults: {minWidth: {@link #minButtonWidth}},
647 * { xtype: 'component', flex: 1 },
648 * { xtype: 'button', text: 'Button 1' }
652 * The {@link #minButtonWidth} is used as the default {@link Ext.button.Button#minWidth minWidth} for
653 * each of the buttons in the buttons toolbar.
656 me.fbar = me.buttons;
661 * @cfg {Object/Object[]} fbar
662 * Convenience config used for adding items to the bottom of the panel. Short for Footer Bar.
665 * { type: 'button', text: 'Button 1' }
674 * defaults: {minWidth: {@link #minButtonWidth}},
676 * { xtype: 'component', flex: 1 },
677 * { xtype: 'button', text: 'Button 1' }
681 * The {@link #minButtonWidth} is used as the default {@link Ext.button.Button#minWidth minWidth} for
682 * each of the buttons in the fbar.
685 fbar = initToolbar(me.fbar, 'bottom', true); // only we useButtonAlign
688 // Apply the minButtonWidth config to buttons in the toolbar
689 if (minButtonWidth) {
690 fbarDefaults = fbar.defaults;
691 fbar.defaults = function(config) {
692 var defaults = fbarDefaults || {};
693 if ((!config.xtype || config.xtype === 'button' || (config.isComponent && config.isXType('button'))) &&
694 !('minWidth' in defaults)) {
695 defaults = Ext.apply({minWidth: minButtonWidth}, defaults);
706 * @cfg {Object/Object[]} lbar
707 * Convenience config. Short for 'Left Bar' (left-docked, vertical toolbar).
710 * { xtype: 'button', text: 'Button 1' }
719 * { xtype: 'button', text: 'Button 1' }
724 docked.push(initToolbar(me.lbar, 'left'));
729 * @cfg {Object/Object[]} rbar
730 * Convenience config. Short for 'Right Bar' (right-docked, vertical toolbar).
733 * { xtype: 'button', text: 'Button 1' }
742 * { xtype: 'button', text: 'Button 1' }
747 docked.push(initToolbar(me.rbar, 'right'));
751 if (me.dockedItems) {
752 if (!Ext.isArray(me.dockedItems)) {
753 me.dockedItems = [me.dockedItems];
755 me.dockedItems = me.dockedItems.concat(docked);
757 me.dockedItems = docked;
763 * Tools are a Panel-specific capabilty.
764 * Panel uses initTools. Subclasses may contribute tools by implementing addTools.
766 initTools: function() {
769 me.tools = me.tools ? Ext.Array.clone(me.tools) : [];
771 // Add a collapse tool unless configured to not show a collapse tool
772 // or to not even show a header.
773 if (me.collapsible && !(me.hideCollapseTool || me.header === false)) {
774 me.collapseDirection = me.collapseDirection || me.headerPosition || 'top';
775 me.collapseTool = me.expandTool = me.createComponent({
777 type: 'collapse-' + me.collapseDirection,
778 expandType: me.getOppositeDirection(me.collapseDirection),
779 handler: me.toggleCollapse,
783 // Prepend collapse tool is configured to do so.
784 if (me.collapseFirst) {
785 me.tools.unshift(me.collapseTool);
789 // Add subclass-specific tools.
792 // Make Panel closable.
794 me.addClsWithUI('closable');
797 handler: Ext.Function.bind(me.close, this, [])
801 // Append collapse tool if needed.
802 if (me.collapseTool && !me.collapseFirst) {
803 me.tools.push(me.collapseTool);
810 * Template method to be implemented in subclasses to add their tools after the collapsible tool.
812 addTools: Ext.emptyFn,
815 * Closes the Panel. By default, this method, removes it from the DOM, {@link Ext.Component#destroy destroy}s the
816 * Panel object and all its descendant Components. The {@link #beforeclose beforeclose} event is fired before the
817 * close happens and will cancel the close action if it returns false.
819 * **Note:** This method is also affected by the {@link #closeAction} setting. For more explicit control use
820 * {@link #destroy} and {@link #hide} methods.
823 if (this.fireEvent('beforeclose', this) !== false) {
829 doClose: function() {
830 this.fireEvent('close', this);
831 this[this.closeAction]();
834 onRender: function(ct, position) {
838 // Add class-specific header tools.
839 // Panel adds collapsible and closable.
842 // Dock the header/title
845 // Call to super after adding the header, to prevent an unnecessary re-layout
846 me.callParent(arguments);
849 afterRender: function() {
852 me.callParent(arguments);
854 // Instate the collapsed state after render. We need to wait for
855 // this moment so that we have established at least some of our size (from our
856 // configured dimensions or from content via the component layout)
858 me.collapsed = false;
859 me.collapse(null, false, true);
864 * Create, hide, or show the header component as appropriate based on the current config.
866 * @param {Boolean} force True to force the header to be created
868 updateHeader: function(force) {
874 if (!me.preventHeader && (force || title || (tools && tools.length))) {
876 header = me.header = Ext.create('Ext.panel.Header', {
878 orientation : (me.headerPosition == 'left' || me.headerPosition == 'right') ? 'vertical' : 'horizontal',
879 dock : me.headerPosition || 'top',
880 textCls : me.headerTextCls,
881 iconCls : me.iconCls,
882 baseCls : me.baseCls + '-header',
885 indicateDrag: me.draggable,
887 frame : me.frame && me.frameHeader,
888 ignoreParentFrame : me.frame || me.overlapHeader,
889 ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement,
890 listeners : me.collapsible && me.titleCollapse ? {
891 click: me.toggleCollapse,
895 me.addDocked(header, 0);
897 // Reference the Header's tool array.
898 // Header injects named references.
899 me.tools = header.tools;
909 setUI: function(ui) {
912 me.callParent(arguments);
920 getContentTarget: function() {
924 getTargetEl: function() {
925 return this.body || this.frameBody || this.el;
928 // the overrides below allow for collapsed regions inside the border layout to be hidden
931 isVisible: function(deep){
933 if (me.collapsed && me.placeholder) {
934 return me.placeholder.isVisible(deep);
936 return me.callParent(arguments);
942 if (me.collapsed && me.placeholder) {
943 me.placeholder.hide();
945 me.callParent(arguments);
952 if (me.collapsed && me.placeholder) {
953 // force hidden back to true, since this gets set by the layout
955 me.placeholder.show();
957 me.callParent(arguments);
961 addTool: function(tool) {
965 if (Ext.isArray(tool)) {
966 Ext.each(tool, me.addTool, me);
971 header.addTool(tool);
976 getOppositeDirection: function(d) {
977 var c = Ext.Component;
979 case c.DIRECTION_TOP:
980 return c.DIRECTION_BOTTOM;
981 case c.DIRECTION_RIGHT:
982 return c.DIRECTION_LEFT;
983 case c.DIRECTION_BOTTOM:
984 return c.DIRECTION_TOP;
985 case c.DIRECTION_LEFT:
986 return c.DIRECTION_RIGHT;
991 * Collapses the panel body so that the body becomes hidden. Docked Components parallel to the border towards which
992 * the collapse takes place will remain visible. Fires the {@link #beforecollapse} event which will cancel the
993 * collapse action if it returns false.
995 * @param {String} direction . The direction to collapse towards. Must be one of
997 * - Ext.Component.DIRECTION_TOP
998 * - Ext.Component.DIRECTION_RIGHT
999 * - Ext.Component.DIRECTION_BOTTOM
1000 * - Ext.Component.DIRECTION_LEFT
1002 * @param {Boolean} [animate] True to animate the transition, else false (defaults to the value of the
1003 * {@link #animCollapse} panel config)
1004 * @return {Ext.panel.Panel} this
1006 collapse: function(direction, animate, /* private - passed if called at render time */ internal) {
1009 height = me.getHeight(),
1010 width = me.getWidth(),
1013 dockedItems = me.dockedItems.items,
1014 dockedItemCount = dockedItems.length,
1028 afteranimate: me.afterCollapse,
1031 duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration)
1034 reExpanderOrientation,
1040 direction = me.collapseDirection;
1043 // If internal (Called because of initial collapsed state), then no animation, and no events.
1046 } else if (me.collapsed || me.fireEvent('beforecollapse', me, direction, animate) === false) {
1050 reExpanderDock = direction;
1051 me.expandDirection = me.getOppositeDirection(direction);
1053 // Track docked items which we hide during collapsed state
1054 me.hiddenDocked = [];
1056 switch (direction) {
1057 case c.DIRECTION_TOP:
1058 case c.DIRECTION_BOTTOM:
1059 reExpanderOrientation = 'horizontal';
1060 collapseDimension = 'height';
1061 getDimension = 'getHeight';
1063 // Attempt to find a reExpander Component (docked in a horizontal orientation)
1064 // Also, collect all other docked items which we must hide after collapse.
1065 for (; i < dockedItemCount; i++) {
1066 comp = dockedItems[i];
1067 if (comp.isVisible()) {
1068 if (comp.isXType('header', true) && (!comp.dock || comp.dock == 'top' || comp.dock == 'bottom')) {
1071 me.hiddenDocked.push(comp);
1073 } else if (comp === me.reExpander) {
1078 if (direction == Ext.Component.DIRECTION_BOTTOM) {
1079 pos = me.getPosition()[1] - Ext.fly(me.el.dom.offsetParent).getRegion().top;
1080 anim.from.top = pos;
1084 case c.DIRECTION_LEFT:
1085 case c.DIRECTION_RIGHT:
1086 reExpanderOrientation = 'vertical';
1087 collapseDimension = 'width';
1088 getDimension = 'getWidth';
1090 // Attempt to find a reExpander Component (docked in a vecrtical orientation)
1091 // Also, collect all other docked items which we must hide after collapse.
1092 for (; i < dockedItemCount; i++) {
1093 comp = dockedItems[i];
1094 if (comp.isVisible()) {
1095 if (comp.isHeader && (comp.dock == 'left' || comp.dock == 'right')) {
1098 me.hiddenDocked.push(comp);
1100 } else if (comp === me.reExpander) {
1105 if (direction == Ext.Component.DIRECTION_RIGHT) {
1106 pos = me.getPosition()[0] - Ext.fly(me.el.dom.offsetParent).getRegion().left;
1107 anim.from.left = pos;
1112 throw('Panel collapse must be passed a valid Component collapse direction');
1115 // Disable toggle tool during animated collapse
1116 if (animate && me.collapseTool) {
1117 me.collapseTool.disable();
1120 // Add the collapsed class now, so that collapsed CSS rules are applied before measurements are taken.
1121 me.addClsWithUI(me.collapsedCls);
1122 // if (me.border === false) {
1123 // me.addClsWithUI(me.collapsedCls + '-noborder');
1126 // We found a header: Measure it to find the collapse-to size.
1127 if (reExpander && reExpander.rendered) {
1129 //we must add the collapsed cls to the header and then remove to get the proper height
1130 reExpander.addClsWithUI(me.collapsedCls);
1131 reExpander.addClsWithUI(me.collapsedCls + '-' + reExpander.dock);
1132 if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1133 reExpander.addClsWithUI(me.collapsedCls + '-border-' + reExpander.dock);
1136 frameInfo = reExpander.getFrameInfo();
1139 newSize = reExpander[getDimension]() + (frameInfo ? frameInfo[direction] : 0);
1142 reExpander.removeClsWithUI(me.collapsedCls);
1143 reExpander.removeClsWithUI(me.collapsedCls + '-' + reExpander.dock);
1144 if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1145 reExpander.removeClsWithUI(me.collapsedCls + '-border-' + reExpander.dock);
1148 // No header: Render and insert a temporary one, and then measure it.
1151 hideMode: 'offsets',
1154 orientation: reExpanderOrientation,
1155 dock: reExpanderDock,
1156 textCls: me.headerTextCls,
1157 iconCls: me.iconCls,
1158 baseCls: me.baseCls + '-header',
1160 frame: me.frame && me.frameHeader,
1161 ignoreParentFrame: me.frame || me.overlapHeader,
1162 indicateDrag: me.draggable,
1163 cls: me.baseCls + '-collapsed-placeholder ' + ' ' + Ext.baseCSSPrefix + 'docked ' + me.baseCls + '-' + me.ui + '-collapsed',
1166 if (!me.hideCollapseTool) {
1167 reExpander[(reExpander.orientation == 'horizontal') ? 'tools' : 'items'] = [{
1169 type: 'expand-' + me.expandDirection,
1170 handler: me.toggleCollapse,
1175 // Capture the size of the re-expander.
1176 // For vertical headers in IE6 and IE7, this will be sized by a CSS rule in _panel.scss
1177 reExpander = me.reExpander = Ext.create('Ext.panel.Header', reExpander);
1178 newSize = reExpander[getDimension]() + ((reExpander.frame) ? reExpander.frameSize[direction] : 0);
1181 // Insert the new docked item
1182 me.insertDocked(0, reExpander);
1185 me.reExpander = reExpander;
1186 me.reExpander.addClsWithUI(me.collapsedCls);
1187 me.reExpander.addClsWithUI(me.collapsedCls + '-' + reExpander.dock);
1188 if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1189 me.reExpander.addClsWithUI(me.collapsedCls + '-border-' + me.reExpander.dock);
1192 // If collapsing right or down, we'll be also animating the left or top.
1193 if (direction == Ext.Component.DIRECTION_RIGHT) {
1194 anim.to.left = pos + (width - newSize);
1195 } else if (direction == Ext.Component.DIRECTION_BOTTOM) {
1196 anim.to.top = pos + (height - newSize);
1199 // Animate to the new size
1200 anim.to[collapseDimension] = newSize;
1202 // When we collapse a panel, the panel is in control of one dimension (depending on
1203 // collapse direction) and sets that on the component. We must restore the user's
1204 // original value (including non-existance) when we expand. Using this technique, we
1205 // mimic setCalculatedSize for the dimension we do not control and setSize for the
1206 // one we do (only while collapsed).
1207 if (!me.collapseMemento) {
1208 me.collapseMemento = new Ext.util.Memento(me);
1210 me.collapseMemento.capture(['width', 'height', 'minWidth', 'minHeight', 'layoutManagedHeight', 'layoutManagedWidth']);
1212 // Remove any flex config before we attempt to collapse.
1213 me.savedFlex = me.flex;
1217 me.suspendLayout = true;
1222 me.setSize(anim.to.width, anim.to.height);
1223 if (Ext.isDefined(anim.to.left) || Ext.isDefined(anim.to.top)) {
1224 me.setPosition(anim.to.left, anim.to.top);
1226 me.afterCollapse(false, internal);
1231 afterCollapse: function(animated, internal) {
1234 l = me.hiddenDocked.length;
1236 me.collapseMemento.restore(['minWidth', 'minHeight']);
1238 // Now we can restore the dimension we don't control to its original state
1239 // Leave the value in the memento so that it can be correctly restored
1240 // if it is set by animation.
1241 if (Ext.Component.VERTICAL_DIRECTION_Re.test(me.expandDirection)) {
1242 me.layoutManagedHeight = 2;
1243 me.collapseMemento.restore('width', false);
1245 me.layoutManagedWidth = 2;
1246 me.collapseMemento.restore('height', false);
1249 // We must hide the body, otherwise it overlays docked items which come before
1250 // it in the DOM order. Collapsing its dimension won't work - padding and borders keep a size.
1251 me.saveScrollTop = me.body.dom.scrollTop;
1252 me.body.setStyle('display', 'none');
1254 for (; i < l; i++) {
1255 me.hiddenDocked[i].hide();
1257 if (me.reExpander) {
1258 me.reExpander.updateFrame();
1259 me.reExpander.show();
1261 me.collapsed = true;
1262 me.suspendLayout = false;
1266 // Because Component layouts only inform upstream containers if they have changed size,
1267 // explicitly lay out the container now, because the lastComponentsize will have been set by the non-animated setCalculatedSize.
1269 me.ownerCt.layout.layout();
1271 } else if (me.reExpander.temporary) {
1272 me.doComponentLayout();
1277 me.resizer.disable();
1280 // If me Panel was configured with a collapse tool in its header, flip it's type
1281 if (me.collapseTool) {
1282 me.collapseTool.setType('expand-' + me.expandDirection);
1285 me.fireEvent('collapse', me);
1288 // Re-enable the toggle tool after an animated collapse
1289 if (animated && me.collapseTool) {
1290 me.collapseTool.enable();
1295 * Expands the panel body so that it becomes visible. Fires the {@link #beforeexpand} event which will cancel the
1296 * expand action if it returns false.
1297 * @param {Boolean} [animate] True to animate the transition, else false (defaults to the value of the
1298 * {@link #animCollapse} panel config)
1299 * @return {Ext.panel.Panel} this
1301 expand: function(animate) {
1303 if (!me.collapsed || me.fireEvent('beforeexpand', me, animate) === false) {
1308 l = me.hiddenDocked.length,
1309 direction = me.expandDirection,
1310 height = me.getHeight(),
1311 width = me.getWidth(),
1314 // Disable toggle tool during animated expand
1315 if (animate && me.collapseTool) {
1316 me.collapseTool.disable();
1319 // Show any docked items that we hid on collapse
1320 // And hide the injected reExpander Header
1321 for (; i < l; i++) {
1322 me.hiddenDocked[i].hidden = false;
1323 me.hiddenDocked[i].el.show();
1325 if (me.reExpander) {
1326 if (me.reExpander.temporary) {
1327 me.reExpander.hide();
1329 me.reExpander.removeClsWithUI(me.collapsedCls);
1330 me.reExpander.removeClsWithUI(me.collapsedCls + '-' + me.reExpander.dock);
1331 if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1332 me.reExpander.removeClsWithUI(me.collapsedCls + '-border-' + me.reExpander.dock);
1334 me.reExpander.updateFrame();
1338 // If me Panel was configured with a collapse tool in its header, flip it's type
1339 if (me.collapseTool) {
1340 me.collapseTool.setType('collapse-' + me.collapseDirection);
1343 // Restore body display and scroll position
1344 me.body.setStyle('display', '');
1345 me.body.dom.scrollTop = me.saveScrollTop;
1347 // Unset the flag before the potential call to calculateChildBox to calculate our newly flexed size
1348 me.collapsed = false;
1350 // Remove any collapsed styling before any animation begins
1351 me.removeClsWithUI(me.collapsedCls);
1352 // if (me.border === false) {
1353 // me.removeClsWithUI(me.collapsedCls + '-noborder');
1364 afteranimate: me.afterExpand,
1369 if ((direction == Ext.Component.DIRECTION_TOP) || (direction == Ext.Component.DIRECTION_BOTTOM)) {
1371 // Restore the collapsed dimension.
1372 // Leave it in the memento, so that the final restoreAll can overwrite anything that animation does.
1373 me.collapseMemento.restore('height', false);
1375 // If autoHeight, measure the height now we have shown the body element.
1376 if (me.height === undefined) {
1377 me.setCalculatedSize(me.width, null);
1378 anim.to.height = me.getHeight();
1380 // Must size back down to collapsed for the animation.
1381 me.setCalculatedSize(me.width, anim.from.height);
1383 // If we were flexed, then we can't just restore to the saved size.
1384 // We must restore to the currently correct, flexed size, so we much ask the Box layout what that is.
1385 else if (me.savedFlex) {
1386 me.flex = me.savedFlex;
1387 anim.to.height = me.ownerCt.layout.calculateChildBox(me).height;
1390 // Else, restore to saved height
1392 anim.to.height = me.height;
1395 // top needs animating upwards
1396 if (direction == Ext.Component.DIRECTION_TOP) {
1397 pos = me.getPosition()[1] - Ext.fly(me.el.dom.offsetParent).getRegion().top;
1398 anim.from.top = pos;
1399 anim.to.top = pos - (anim.to.height - height);
1401 } else if ((direction == Ext.Component.DIRECTION_LEFT) || (direction == Ext.Component.DIRECTION_RIGHT)) {
1403 // Restore the collapsed dimension.
1404 // Leave it in the memento, so that the final restoreAll can overwrite anything that animation does.
1405 me.collapseMemento.restore('width', false);
1407 // If autoWidth, measure the width now we have shown the body element.
1408 if (me.width === undefined) {
1409 me.setCalculatedSize(null, me.height);
1410 anim.to.width = me.getWidth();
1412 // Must size back down to collapsed for the animation.
1413 me.setCalculatedSize(anim.from.width, me.height);
1415 // If we were flexed, then we can't just restore to the saved size.
1416 // We must restore to the currently correct, flexed size, so we much ask the Box layout what that is.
1417 else if (me.savedFlex) {
1418 me.flex = me.savedFlex;
1419 anim.to.width = me.ownerCt.layout.calculateChildBox(me).width;
1422 // Else, restore to saved width
1424 anim.to.width = me.width;
1427 // left needs animating leftwards
1428 if (direction == Ext.Component.DIRECTION_LEFT) {
1429 pos = me.getPosition()[0] - Ext.fly(me.el.dom.offsetParent).getRegion().left;
1430 anim.from.left = pos;
1431 anim.to.left = pos - (anim.to.width - width);
1438 me.setCalculatedSize(anim.to.width, anim.to.height);
1440 me.setLeft(anim.to.x);
1443 me.setTop(anim.to.y);
1445 me.afterExpand(false);
1451 afterExpand: function(animated) {
1454 // Restored to a calculated flex. Delete the set width and height properties so that flex works from now on.
1456 me.flex = me.savedFlex;
1457 delete me.savedFlex;
1462 // Restore width/height and dimension management flags to original values
1463 if (me.collapseMemento) {
1464 me.collapseMemento.restoreAll();
1467 if (animated && me.ownerCt) {
1468 // IE 6 has an intermittent repaint issue in this case so give
1469 // it a little extra time to catch up before laying out.
1470 Ext.defer(me.ownerCt.doLayout, Ext.isIE6 ? 1 : 0, me);
1474 me.resizer.enable();
1477 me.fireEvent('expand', me);
1479 // Re-enable the toggle tool after an animated expand
1480 if (animated && me.collapseTool) {
1481 me.collapseTool.enable();
1486 * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
1487 * @return {Ext.panel.Panel} this
1489 toggleCollapse: function() {
1490 if (this.collapsed) {
1491 this.expand(this.animCollapse);
1493 this.collapse(this.collapseDirection, this.animCollapse);
1499 getKeyMap : function(){
1501 this.keyMap = Ext.create('Ext.util.KeyMap', this.el, this.keys);
1507 initDraggable : function(){
1509 * @property {Ext.dd.DragSource} dd
1510 * If this Panel is configured {@link #draggable}, this property will contain an instance of {@link
1511 * Ext.dd.DragSource} which handles dragging the Panel.
1513 * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource} in order to
1514 * supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
1516 this.dd = Ext.create('Ext.panel.DD', this, Ext.isBoolean(this.draggable) ? null : this.draggable);
1519 // private - helper function for ghost
1520 ghostTools : function() {
1522 headerTools = this.header.query('tool[hidden=false]');
1524 if (headerTools.length) {
1525 Ext.each(headerTools, function(tool) {
1526 // Some tools can be full components, and copying them into the ghost
1527 // actually removes them from the owning panel. You could also potentially
1528 // end up with duplicate DOM ids as well. To avoid any issues we just make
1529 // a simple bare-minimum clone of each tool for ghosting purposes.
1542 // private - used for dragging
1543 ghost: function(cls) {
1545 ghostPanel = me.ghostPanel,
1550 ghostPanel = Ext.create('Ext.panel.Panel', {
1551 renderTo: me.floating ? me.el.dom.parentNode : document.body,
1555 frame: Ext.supports.CSS3BorderRadius ? me.frame : false,
1556 overlapHeader: me.overlapHeader,
1557 headerPosition: me.headerPosition,
1558 baseCls: me.baseCls,
1559 cls: me.baseCls + '-ghost ' + (cls ||'')
1561 me.ghostPanel = ghostPanel;
1563 ghostPanel.floatParent = me.floatParent;
1565 ghostPanel.setZIndex(Ext.Number.from(me.el.getStyle('zIndex'), 0));
1567 ghostPanel.toFront();
1569 header = ghostPanel.header;
1572 header.suspendLayout = true;
1573 Ext.Array.forEach(header.query('tool'), function(tool){
1574 header.remove(tool);
1576 header.suspendLayout = false;
1578 ghostPanel.addTool(me.ghostTools());
1579 ghostPanel.setTitle(me.title);
1580 ghostPanel.setIconCls(me.iconCls);
1582 ghostPanel.el.show();
1583 ghostPanel.setPosition(box.x, box.y);
1584 ghostPanel.setSize(box.width, box.height);
1586 if (me.floatingItems) {
1587 me.floatingItems.hide();
1593 unghost: function(show, matchPosition) {
1595 if (!me.ghostPanel) {
1598 if (show !== false) {
1600 if (matchPosition !== false) {
1601 me.setPosition(me.ghostPanel.getPosition());
1603 if (me.floatingItems) {
1604 me.floatingItems.show();
1606 Ext.defer(me.focus, 10, me);
1608 me.ghostPanel.el.hide();
1611 initResizable: function(resizable) {
1612 if (this.collapsed) {
1613 resizable.disabled = true;
1615 this.callParent([resizable]);
1618 this.prototype.animCollapse = Ext.enableFx;