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 * @class Ext.panel.Panel
17 * @extends Ext.panel.AbstractPanel
18 * <p>Panel is a container that has specific functionality and structural components that make
19 * it the perfect building block for application-oriented user interfaces.</p>
20 * <p>Panels are, by virtue of their inheritance from {@link Ext.container.Container}, capable
21 * of being configured with a {@link Ext.container.Container#layout layout}, and containing child Components.</p>
22 * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.container.Container#add adding} Components
23 * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
24 * those child elements need to be sized using one of Ext's built-in <code><b>{@link Ext.container.Container#layout layout}</b></code> schemes. By
25 * default, Panels use the {@link Ext.layout.container.Auto Auto} scheme. This simply renders
26 * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
28 * {@img Ext.panel.Panel/panel.png Panel components}
29 * <p>A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate
30 * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional
32 * <p>Panel also provides built-in {@link #collapsible collapsible, expandable} and {@link #closable} behavior.
33 * Panels can be easily dropped into any {@link Ext.container.Container Container} or layout, and the
34 * layout and rendering pipeline is {@link Ext.container.Container#add completely managed by the framework}.</p>
35 * <p><b>Note:</b> By default, the <code>{@link #closable close}</code> header tool <i>destroys</i> the Panel resulting in removal of the Panel
36 * and the destruction of any descendant Components. This makes the Panel object, and all its descendants <b>unusable</b>. To enable the close
37 * tool to simply <i>hide</i> a Panel for later re-use, configure the Panel with <b><code>{@link #closeAction closeAction: 'hide'}</code></b>.</p>
38 * <p>Usually, Panels are used as constituents within an application, in which case, they would be used as child items of Containers,
39 * and would themselves use Ext.Components as child {@link #items}. However to illustrate simply rendering a Panel into the document,
40 * here's how to do it:<pre><code>
41 Ext.create('Ext.panel.Panel', {
44 html: '<p>World!</p>',
45 renderTo: document.body
48 * <p>A more realistic scenario is a Panel created to house input fields which will not be rendered, but used as a constituent part of a Container:<pre><code>
49 var filterPanel = Ext.create('Ext.panel.Panel', {
50 bodyPadding: 5, // Don't want content to crunch against the borders
54 fieldLabel: 'Start date'
57 fieldLabel: 'End date'
61 * <p>Note that the Panel above is not configured to render into the document, nor is it configured with a size or position. In a real world scenario,
62 * the Container into which the Panel is added will use a {@link #layout} to render, size and position its child Components.</p>
63 * <p>Panels will often use specific {@link #layout}s to provide an application with shape and structure by containing and arranging child
64 * Components: <pre><code>
65 var resultsPanel = Ext.create('Ext.panel.Panel', {
69 renderTo: document.body,
71 type: 'vbox', // Arrange child items vertically
72 align: 'stretch', // Each takes up full width
75 items: [{ // Results grid specified as a config object with an xtype of 'grid'
77 columns: [{header: 'Column One'}], // One header just for show. There's no data,
78 store: Ext.create('Ext.data.ArrayStore', {}), // A dummy empty data store
79 flex: 1 // Use 1/3 of Container's height (hint to Box layout)
81 xtype: 'splitter' // A splitter between the two child items
82 }, { // Details Panel specified as a config object (no xtype defaults to 'panel').
86 fieldLabel: 'Data item',
88 }], // An array of form fields
89 flex: 2 // Use 2/3 of Container's height (hint to Box layout)
93 * The example illustrates one possible method of displaying search results. The Panel contains a grid with the resulting data arranged
94 * in rows. Each selected row may be displayed in detail in the Panel below. The {@link Ext.layout.container.VBox vbox} layout is used
95 * to arrange the two vertically. It is configured to stretch child items horizontally to full width. Child items may either be configured
96 * with a numeric height, or with a <code>flex</code> value to distribute available space proportionately.</p>
97 * <p>This Panel itself may be a child item of, for exaple, a {@link Ext.tab.Panel} which will size its child items to fit within its
99 * <p>Using these techniques, as long as the <b>layout</b> is chosen and configured correctly, an application may have any level of
100 * nested containment, all dynamically sized according to configuration, the user's preference and available browser size.</p>
102 Ext.define('Ext.panel.Panel', {
103 extend: 'Ext.panel.AbstractPanel',
110 'Ext.layout.component.Dock',
113 alias: 'widget.panel',
114 alternateClassName: 'Ext.Panel',
117 * @cfg {String} collapsedCls
118 * A CSS class to add to the panel's element after it has been collapsed (defaults to
119 * <code>'collapsed'</code>).
121 collapsedCls: 'collapsed',
124 * @cfg {Boolean} animCollapse
125 * <code>true</code> to animate the transition when the panel is collapsed, <code>false</code> to skip the
126 * animation (defaults to <code>true</code> if the {@link Ext.fx.Anim} class is available, otherwise <code>false</code>).
127 * May also be specified as the animation duration in milliseconds.
129 animCollapse: Ext.enableFx,
132 * @cfg {Number} minButtonWidth
133 * Minimum width of all footer toolbar buttons in pixels (defaults to <tt>75</tt>). If set, this will
134 * be used as the default value for the <tt>{@link Ext.button.Button#minWidth}</tt> config of
135 * each Button added to the <b>footer toolbar</b> via the {@link #fbar} or {@link #buttons} configurations.
136 * It will be ignored for buttons that have a minWidth configured some other way, e.g. in their own config
137 * object or via the {@link Ext.container.Container#config-defaults defaults} of their parent container.
142 * @cfg {Boolean} collapsed
143 * <code>true</code> to render the panel collapsed, <code>false</code> to render it expanded (defaults to
144 * <code>false</code>).
149 * @cfg {Boolean} collapseFirst
150 * <code>true</code> to make sure the collapse/expand toggle button always renders first (to the left of)
151 * any other tools in the panel's title bar, <code>false</code> to render it last (defaults to <code>true</code>).
156 * @cfg {Boolean} hideCollapseTool
157 * <code>true</code> to hide the expand/collapse toggle button when <code>{@link #collapsible} == true</code>,
158 * <code>false</code> to display it (defaults to <code>false</code>).
160 hideCollapseTool: false,
163 * @cfg {Boolean} titleCollapse
164 * <code>true</code> to allow expanding and collapsing the panel (when <code>{@link #collapsible} = true</code>)
165 * by clicking anywhere in the header bar, <code>false</code>) to allow it only by clicking to tool button
166 * (defaults to <code>false</code>)).
168 titleCollapse: false,
171 * @cfg {String} collapseMode
172 * <p><b>Important: this config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.</b></p>
173 * <p>When <i>not</i> a direct child item of a {@link Ext.layout.container.Border border layout}, then the Panel's header remains visible, and the body is collapsed to zero dimensions.
174 * If the Panel has no header, then a new header (orientated correctly depending on the {@link #collapseDirection}) will be inserted to show a the title and a re-expand tool.</p>
175 * <p>When a child item of a {@link Ext.layout.container.Border border layout}, this config has two options:
176 * <div class="mdetail-params"><ul>
177 * <li><b><code>undefined/omitted</code></b><div class="sub-desc">When collapsed, a placeholder {@link Ext.panel.Header Header} is injected into the layout to represent the Panel
178 * and to provide a UI with a Tool to allow the user to re-expand the Panel.</div></li>
179 * <li><b><code>header</code></b> : <div class="sub-desc">The Panel collapses to leave its header visible as when not inside a {@link Ext.layout.container.Border border layout}.</div></li>
184 * @cfg {Mixed} placeholder
185 * <p><b>Important: This config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}
186 * when not using the <code>'header'</code> {@link #collapseMode}.</b></p>
187 * <p><b>Optional.</b> A Component (or config object for a Component) to show in place of this Panel when this Panel is collapsed by a
188 * {@link Ext.layout.container.Border border layout}. Defaults to a generated {@link Ext.panel.Header Header}
189 * containing a {@link Ext.panel.Tool Tool} to re-expand the Panel.</p>
193 * @cfg {Boolean} floatable
194 * <p><b>Important: This config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.</b></p>
195 * <tt>true</tt> to allow clicking a collapsed Panel's {@link #placeholder} to display the Panel floated
196 * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
197 * clicking the expand button to see it again (defaults to <tt>true</tt>).
202 * @cfg {Mixed} overlapHeader
203 * True to overlap the header in a panel over the framing of the panel itself. This is needed when frame:true (and is done automatically for you). Otherwise it is undefined.
204 * If you manually add rounded corners to a panel header which does not have frame:true, this will need to be set to true.
208 * @cfg {Boolean} collapsible
209 * <p>True to make the panel collapsible and have an expand/collapse toggle Tool added into
210 * the header tool button area. False to keep the panel sized either statically, or by an owning layout manager, with no toggle Tool (defaults to false).</p>
211 * See {@link #collapseMode} and {@link #collapseDirection}
216 * @cfg {Boolean} collapseDirection
217 * <p>The direction to collapse the Panel when the toggle button is clicked.</p>
218 * <p>Defaults to the {@link #headerPosition}</p>
219 * <p><b>Important: This config is <u>ignored</u> for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.</b></p>
220 * <p>Specify as <code>'top'</code>, <code>'bottom'</code>, <code>'left'</code> or <code>'right'</code>.</p>
224 * @cfg {Boolean} closable
225 * <p>True to display the 'close' tool button and allow the user to close the window, false to
226 * hide the button and disallow closing the window (defaults to <code>false</code>).</p>
227 * <p>By default, when close is requested by clicking the close button in the header, the {@link #close}
228 * method will be called. This will <i>{@link Ext.Component#destroy destroy}</i> the Panel and its content
229 * meaning that it may not be reused.</p>
230 * <p>To make closing a Panel <i>hide</i> the Panel so that it may be reused, set
231 * {@link #closeAction} to 'hide'.</p>
236 * @cfg {String} closeAction
237 * <p>The action to take when the close header tool is clicked:
238 * <div class="mdetail-params"><ul>
239 * <li><b><code>'{@link #destroy}'</code></b> : <b>Default</b><div class="sub-desc">
240 * {@link #destroy remove} the window from the DOM and {@link Ext.Component#destroy destroy}
241 * it and all descendant Components. The window will <b>not</b> be available to be
242 * redisplayed via the {@link #show} method.
244 * <li><b><code>'{@link #hide}'</code></b> : <div class="sub-desc">
245 * {@link #hide} the window by setting visibility to hidden and applying negative offsets.
246 * The window will be available to be redisplayed via the {@link #show} method.
249 * <p><b>Note:</b> This behavior has changed! setting *does* affect the {@link #close} method
250 * which will invoke the approriate closeAction.
252 closeAction: 'destroy',
255 * @cfg {Object/Array} dockedItems
256 * A component or series of components to be added as docked items to this panel.
257 * The docked items can be docked to either the top, right, left or bottom of a panel.
258 * This is typically used for things like toolbars or tab bars:
260 var panel = new Ext.panel.Panel({
265 text: 'Docked to the top'
272 * @cfg {Boolean} preventHeader Prevent a Header from being created and shown. Defaults to false.
274 preventHeader: false,
277 * @cfg {String} headerPosition Specify as <code>'top'</code>, <code>'bottom'</code>, <code>'left'</code> or <code>'right'</code>. Defaults to <code>'top'</code>.
279 headerPosition: 'top',
282 * @cfg {Boolean} frame
283 * True to apply a frame to the panel.
288 * @cfg {Boolean} frameHeader
289 * True to apply a frame to the panel panels header (if 'frame' is true).
295 * An array of {@link Ext.panel.Tool} configs/instances to be added to the header tool area. The tools are stored as child
296 * components of the header container. They can be accessed using {@link #down} and {#query}, as well as the other
297 * component methods. The toggle tool is automatically created if {@link #collapsible} is set to true.
298 * <p>Note that, apart from the toggle tool which is provided when a panel is collapsible, these
299 * tools only provide the visual button. Any required functionality must be provided by adding
300 * handlers that implement the necessary behavior.</p>
301 * <p>Example usage:</p>
305 qtip: 'Refresh form Data',
307 handler: function(event, toolEl, panel){
314 handler: function(event, toolEl, panel){
322 * @cfg {String} title
323 * The title text to be used to display in the {@link Ext.panel.Header panel header} (defaults to '').
324 * When a `title` is specified the {@link Ext.panel.Header} will automatically be created and displayed unless
325 * {@link #preventHeader} is set to `true`.
328 initComponent: function() {
334 * @event beforeexpand
335 * Fires before this panel is expanded. Return false to prevent the expand.
336 * @param {Ext.panel.Panel} p The Panel being expanded.
337 * @param {Boolean} animate True if the expand is animated, else false.
342 * @event beforecollapse
343 * Fires before this panel is collapsed. Return false to prevent the collapse.
344 * @param {Ext.panel.Panel} p The Panel being collapsed.
345 * @param {String} direction. The direction of the collapse. One of<ul>
346 * <li>Ext.Component.DIRECTION_TOP</li>
347 * <li>Ext.Component.DIRECTION_RIGHT</li>
348 * <li>Ext.Component.DIRECTION_BOTTOM</li>
349 * <li>Ext.Component.DIRECTION_LEFT</li></ul>
350 * @param {Boolean} animate True if the collapse is animated, else false.
356 * Fires after this Panel has expanded.
357 * @param {Ext.panel.Panel} p The Panel that has been expanded.
363 * Fires after this Panel hass collapsed.
364 * @param {Ext.panel.Panel} p The Panel that has been collapsed.
370 * Fires after the Panel title has been set or changed.
371 * @param {Ext.panel.Panel} p the Panel which has been resized.
372 * @param {String} newTitle The new title.
373 * @param {String} oldTitle The previous panel title.
379 * Fires after the Panel iconCls has been set or changed.
380 * @param {Ext.panel.Panel} p the Panel which has been resized.
381 * @param {String} newIconCls The new iconCls.
382 * @param {String} oldIconCls The previous panel iconCls.
387 // Save state on these two events.
388 this.addStateEvents('expand', 'collapse');
395 me.setUI(me.ui + '-framed');
400 me.collapseDirection = me.collapseDirection || me.headerPosition || Ext.Component.DIRECTION_TOP;
402 // Backwards compatibility
406 setBorder: function(border) {
408 // method = (border === false || border === 0) ? 'addClsWithUI' : 'removeClsWithUI';
410 // me.callParent(arguments);
412 // if (me.collapsed) {
413 // me[method](me.collapsedCls + '-noborder');
417 // me.header.setBorder(border);
418 // if (me.collapsed) {
419 // me.header[method](me.collapsedCls + '-noborder');
423 this.callParent(arguments);
426 beforeDestroy: function() {
434 initAria: function() {
436 this.initHeaderAria();
439 initHeaderAria: function() {
444 el.dom.setAttribute('aria-labelledby', header.titleCmp.id);
448 getHeader: function() {
453 * Set a title for the panel's header. See {@link Ext.panel.Header#title}.
454 * @param {String} newTitle
456 setTitle: function(newTitle) {
458 oldTitle = this.title;
462 me.header.setTitle(newTitle);
468 me.reExpander.setTitle(newTitle);
470 me.fireEvent('titlechange', me, newTitle, oldTitle);
474 * Set the iconCls for the panel's header. See {@link Ext.panel.Header#iconCls}.
475 * @param {String} newIconCls
477 setIconCls: function(newIconCls) {
479 oldIconCls = me.iconCls;
481 me.iconCls = newIconCls;
482 var header = me.header;
484 header.setIconCls(newIconCls);
486 me.fireEvent('iconchange', me, newIconCls, oldIconCls);
489 bridgeToolbars: function() {
493 minButtonWidth = me.minButtonWidth;
495 function initToolbar (toolbar, pos, useButtonAlign) {
496 if (Ext.isArray(toolbar)) {
502 else if (!toolbar.xtype) {
503 toolbar.xtype = 'toolbar';
506 if (pos == 'left' || pos == 'right') {
507 toolbar.vertical = true;
510 // Legacy support for buttonAlign (only used by buttons/fbar)
511 if (useButtonAlign) {
512 toolbar.layout = Ext.applyIf(toolbar.layout || {}, {
513 // default to 'end' (right-aligned) if me.buttonAlign is undefined or invalid
514 pack: { left:'start', center:'center' }[me.buttonAlign] || 'end'
520 // Short-hand toolbars (tbar, bbar and fbar plus new lbar and rbar):
523 * @cfg {String} buttonAlign
524 * <p>The alignment of any buttons added to this panel. Valid values are 'right',
525 * 'left' and 'center' (defaults to 'right' for buttons/fbar, 'left' for other toolbar types).</p>
526 * <p><b>NOTE:</b> The newer way to specify toolbars is to use the dockedItems config, and
527 * instead of buttonAlign you would add the layout: { pack: 'start' | 'center' | 'end' }
528 * option to the dockedItem config.</p>
532 * @cfg {Object/Array} tbar
534 Convenience method. Short for 'Top Bar'.
537 { xtype: 'button', text: 'Button 1' }
546 { xtype: 'button', text: 'Button 1' }
553 me.addDocked(initToolbar(me.tbar, 'top'));
558 * @cfg {Object/Array} bbar
560 Convenience method. Short for 'Bottom Bar'.
563 { xtype: 'button', text: 'Button 1' }
572 { xtype: 'button', text: 'Button 1' }
579 me.addDocked(initToolbar(me.bbar, 'bottom'));
584 * @cfg {Object/Array} buttons
586 Convenience method used for adding buttons docked to the bottom of the panel. This is a
587 synonym for the {@link #fbar} config.
599 defaults: {minWidth: {@link #minButtonWidth}},
601 { xtype: 'component', flex: 1 },
602 { xtype: 'button', text: 'Button 1' }
606 The {@link #minButtonWidth} is used as the default {@link Ext.button.Button#minWidth minWidth} for
607 each of the buttons in the buttons toolbar.
612 me.fbar = me.buttons;
617 * @cfg {Object/Array} fbar
619 Convenience method used for adding items to the bottom of the panel. Short for Footer Bar.
622 { type: 'button', text: 'Button 1' }
631 defaults: {minWidth: {@link #minButtonWidth}},
633 { xtype: 'component', flex: 1 },
634 { xtype: 'button', text: 'Button 1' }
638 The {@link #minButtonWidth} is used as the default {@link Ext.button.Button#minWidth minWidth} for
639 each of the buttons in the fbar.
644 fbar = initToolbar(me.fbar, 'bottom', true); // only we useButtonAlign
647 // Apply the minButtonWidth config to buttons in the toolbar
648 if (minButtonWidth) {
649 fbarDefaults = fbar.defaults;
650 fbar.defaults = function(config) {
651 var defaults = fbarDefaults || {};
652 if ((!config.xtype || config.xtype === 'button' || (config.isComponent && config.isXType('button'))) &&
653 !('minWidth' in defaults)) {
654 defaults = Ext.apply({minWidth: minButtonWidth}, defaults);
665 * @cfg {Object/Array} lbar
667 * Convenience method. Short for 'Left Bar' (left-docked, vertical toolbar).
670 * { xtype: 'button', text: 'Button 1' }
679 * { xtype: 'button', text: 'Button 1' }
686 me.addDocked(initToolbar(me.lbar, 'left'));
691 * @cfg {Object/Array} rbar
693 * Convenience method. Short for 'Right Bar' (right-docked, vertical toolbar).
696 * { xtype: 'button', text: 'Button 1' }
705 * { xtype: 'button', text: 'Button 1' }
712 me.addDocked(initToolbar(me.rbar, 'right'));
719 * Tools are a Panel-specific capabilty.
720 * Panel uses initTools. Subclasses may contribute tools by implementing addTools.
722 initTools: function() {
725 me.tools = me.tools || [];
727 // Add a collapse tool unless configured to not show a collapse tool
728 // or to not even show a header.
729 if (me.collapsible && !(me.hideCollapseTool || me.header === false)) {
730 me.collapseDirection = me.collapseDirection || me.headerPosition || 'top';
731 me.collapseTool = me.expandTool = me.createComponent({
733 type: 'collapse-' + me.collapseDirection,
734 expandType: me.getOppositeDirection(me.collapseDirection),
735 handler: me.toggleCollapse,
739 // Prepend collapse tool is configured to do so.
740 if (me.collapseFirst) {
741 me.tools.unshift(me.collapseTool);
745 // Add subclass-specific tools.
748 // Make Panel closable.
750 me.addClsWithUI('closable');
753 handler: Ext.Function.bind(me.close, this, [])
757 // Append collapse tool if needed.
758 if (me.collapseTool && !me.collapseFirst) {
759 me.tools.push(me.collapseTool);
765 * Template method to be implemented in subclasses to add their tools after the collapsible tool.
767 addTools: Ext.emptyFn,
770 * <p>Closes the Panel. By default, this method, removes it from the DOM, {@link Ext.Component#destroy destroy}s
771 * the Panel object and all its descendant Components. The {@link #beforeclose beforeclose}
772 * event is fired before the close happens and will cancel the close action if it returns false.<p>
773 * <p><b>Note:</b> This method is not affected by the {@link #closeAction} setting which
774 * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}.
775 * To hide the Panel without destroying it, call {@link #hide}.</p>
778 if (this.fireEvent('beforeclose', this) !== false) {
784 doClose: function() {
785 this.fireEvent('close', this);
786 this[this.closeAction]();
789 onRender: function(ct, position) {
793 // Add class-specific header tools.
794 // Panel adds collapsible and closable.
797 // Dock the header/title
800 // Call to super after adding the header, to prevent an unnecessary re-layout
801 me.callParent(arguments);
804 afterRender: function() {
806 me.callParent(arguments);
808 me.collapsed = false;
809 me.collapse(null, false, true);
814 * Create, hide, or show the header component as appropriate based on the current config.
816 * @param {Boolean} force True to force the header to be created
818 updateHeader: function(force) {
824 if (!me.preventHeader && (force || title || (tools && tools.length))) {
826 header = me.header = Ext.create('Ext.panel.Header', {
828 orientation : (me.headerPosition == 'left' || me.headerPosition == 'right') ? 'vertical' : 'horizontal',
829 dock : me.headerPosition || 'top',
830 textCls : me.headerTextCls,
831 iconCls : me.iconCls,
832 baseCls : me.baseCls + '-header',
835 indicateDrag: me.draggable,
837 frame : me.frame && me.frameHeader,
838 ignoreParentFrame : me.frame || me.overlapHeader,
839 ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement,
840 listeners : me.collapsible && me.titleCollapse ? {
841 click: me.toggleCollapse,
845 me.addDocked(header, 0);
847 // Reference the Header's tool array.
848 // Header injects named references.
849 me.tools = header.tools;
859 setUI: function(ui) {
862 me.callParent(arguments);
870 getContentTarget: function() {
874 getTargetEl: function() {
875 return this.body || this.frameBody || this.el;
878 addTool: function(tool) {
879 this.tools.push(tool);
880 var header = this.header;
882 header.addTool(tool);
887 getOppositeDirection: function(d) {
888 var c = Ext.Component;
890 case c.DIRECTION_TOP:
891 return c.DIRECTION_BOTTOM;
892 case c.DIRECTION_RIGHT:
893 return c.DIRECTION_LEFT;
894 case c.DIRECTION_BOTTOM:
895 return c.DIRECTION_TOP;
896 case c.DIRECTION_LEFT:
897 return c.DIRECTION_RIGHT;
902 * Collapses the panel body so that the body becomes hidden. Docked Components parallel to the
903 * border towards which the collapse takes place will remain visible. Fires the {@link #beforecollapse} event which will
904 * cancel the collapse action if it returns false.
905 * @param {String} direction. The direction to collapse towards. Must be one of<ul>
906 * <li>Ext.Component.DIRECTION_TOP</li>
907 * <li>Ext.Component.DIRECTION_RIGHT</li>
908 * <li>Ext.Component.DIRECTION_BOTTOM</li>
909 * <li>Ext.Component.DIRECTION_LEFT</li></ul>
910 * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
911 * {@link #animCollapse} panel config)
912 * @return {Ext.panel.Panel} this
914 collapse: function(direction, animate, /* private - passed if called at render time */ internal) {
917 height = me.getHeight(),
918 width = me.getWidth(),
921 dockedItems = me.dockedItems.items,
922 dockedItemCount = dockedItems.length,
936 afteranimate: me.afterCollapse,
939 duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration)
942 reExpanderOrientation,
949 direction = me.collapseDirection;
952 // If internal (Called because of initial collapsed state), then no animation, and no events.
955 } else if (me.collapsed || me.fireEvent('beforecollapse', me, direction, animate) === false) {
959 reExpanderDock = direction;
960 me.expandDirection = me.getOppositeDirection(direction);
962 // Track docked items which we hide during collapsed state
963 me.hiddenDocked = [];
966 case c.DIRECTION_TOP:
967 case c.DIRECTION_BOTTOM:
968 me.expandedSize = me.getHeight();
969 reExpanderOrientation = 'horizontal';
970 collapseDimension = 'height';
971 getDimension = 'getHeight';
972 setDimension = 'setHeight';
974 // Collect the height of the visible header.
975 // Hide all docked items except the header.
976 // Hide *ALL* docked items if we're going to end up hiding the whole Panel anyway
977 for (; i < dockedItemCount; i++) {
978 comp = dockedItems[i];
979 if (comp.isVisible()) {
980 if (comp.isHeader && (!comp.dock || comp.dock == 'top' || comp.dock == 'bottom')) {
983 me.hiddenDocked.push(comp);
988 if (direction == Ext.Component.DIRECTION_BOTTOM) {
989 pos = me.getPosition()[1] - Ext.fly(me.el.dom.offsetParent).getRegion().top;
994 case c.DIRECTION_LEFT:
995 case c.DIRECTION_RIGHT:
996 me.expandedSize = me.getWidth();
997 reExpanderOrientation = 'vertical';
998 collapseDimension = 'width';
999 getDimension = 'getWidth';
1000 setDimension = 'setWidth';
1002 // Collect the height of the visible header.
1003 // Hide all docked items except the header.
1004 // Hide *ALL* docked items if we're going to end up hiding the whole Panel anyway
1005 for (; i < dockedItemCount; i++) {
1006 comp = dockedItems[i];
1007 if (comp.isVisible()) {
1008 if (comp.isHeader && (comp.dock == 'left' || comp.dock == 'right')) {
1011 me.hiddenDocked.push(comp);
1016 if (direction == Ext.Component.DIRECTION_RIGHT) {
1017 pos = me.getPosition()[0] - Ext.fly(me.el.dom.offsetParent).getRegion().left;
1018 anim.from.left = pos;
1023 throw('Panel collapse must be passed a valid Component collapse direction');
1026 // No scrollbars when we shrink this Panel
1027 // And no laying out of any children... we're effectively *hiding* the body
1028 me.setAutoScroll(false);
1029 me.suspendLayout = true;
1030 me.body.setVisibilityMode(Ext.core.Element.DISPLAY);
1032 // Disable toggle tool during animated collapse
1033 if (animate && me.collapseTool) {
1034 me.collapseTool.disable();
1037 // Add the collapsed class now, so that collapsed CSS rules are applied before measurements are taken.
1038 me.addClsWithUI(me.collapsedCls);
1039 // if (me.border === false) {
1040 // me.addClsWithUI(me.collapsedCls + '-noborder');
1043 // We found a header: Measure it to find the collapse-to size.
1045 //we must add the collapsed cls to the header and then remove to get the proper height
1046 reExpander.addClsWithUI(me.collapsedCls);
1047 reExpander.addClsWithUI(me.collapsedCls + '-' + reExpander.dock);
1048 if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1049 reExpander.addClsWithUI(me.collapsedCls + '-border-' + reExpander.dock);
1052 frameInfo = reExpander.getFrameInfo();
1055 newSize = reExpander[getDimension]() + (frameInfo ? frameInfo[direction] : 0);
1058 reExpander.removeClsWithUI(me.collapsedCls);
1059 reExpander.removeClsWithUI(me.collapsedCls + '-' + reExpander.dock);
1060 if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1061 reExpander.removeClsWithUI(me.collapsedCls + '-border-' + reExpander.dock);
1064 // No header: Render and insert a temporary one, and then measure it.
1067 hideMode: 'offsets',
1070 orientation: reExpanderOrientation,
1071 dock: reExpanderDock,
1072 textCls: me.headerTextCls,
1073 iconCls: me.iconCls,
1074 baseCls: me.baseCls + '-header',
1076 frame: me.frame && me.frameHeader,
1077 ignoreParentFrame: me.frame || me.overlapHeader,
1078 indicateDrag: me.draggable,
1079 cls: me.baseCls + '-collapsed-placeholder ' + ' ' + Ext.baseCSSPrefix + 'docked ' + me.baseCls + '-' + me.ui + '-collapsed',
1082 if (!me.hideCollapseTool) {
1083 reExpander[(reExpander.orientation == 'horizontal') ? 'tools' : 'items'] = [{
1085 type: 'expand-' + me.expandDirection,
1086 handler: me.toggleCollapse,
1091 // Capture the size of the re-expander.
1092 // For vertical headers in IE6 and IE7, this will be sized by a CSS rule in _panel.scss
1093 reExpander = me.reExpander = Ext.create('Ext.panel.Header', reExpander);
1094 newSize = reExpander[getDimension]() + ((reExpander.frame) ? reExpander.frameSize[direction] : 0);
1097 // Insert the new docked item
1098 me.insertDocked(0, reExpander);
1101 me.reExpander = reExpander;
1102 me.reExpander.addClsWithUI(me.collapsedCls);
1103 me.reExpander.addClsWithUI(me.collapsedCls + '-' + reExpander.dock);
1104 if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1105 me.reExpander.addClsWithUI(me.collapsedCls + '-border-' + me.reExpander.dock);
1108 // If collapsing right or down, we'll be also animating the left or top.
1109 if (direction == Ext.Component.DIRECTION_RIGHT) {
1110 anim.to.left = pos + (width - newSize);
1111 } else if (direction == Ext.Component.DIRECTION_BOTTOM) {
1112 anim.to.top = pos + (height - newSize);
1115 // Animate to the new size
1116 anim.to[collapseDimension] = newSize;
1118 // When we collapse a panel, the panel is in control of one dimension (depending on
1119 // collapse direction) and sets that on the component. We must restore the user's
1120 // original value (including non-existance) when we expand. Using this technique, we
1121 // mimic setCalculatedSize for the dimension we do not control and setSize for the
1122 // one we do (only while collapsed).
1123 if (!me.collapseMemento) {
1124 me.collapseMemento = new Ext.util.Memento(me);
1126 me.collapseMemento.capture(['width', 'height', 'minWidth', 'minHeight']);
1128 // Remove any flex config before we attempt to collapse.
1129 me.savedFlex = me.flex;
1137 me.setSize(anim.to.width, anim.to.height);
1138 if (Ext.isDefined(anim.to.left) || Ext.isDefined(anim.to.top)) {
1139 me.setPosition(anim.to.left, anim.to.top);
1141 me.afterCollapse(false, internal);
1146 afterCollapse: function(animated, internal) {
1149 l = me.hiddenDocked.length;
1151 me.collapseMemento.restore(['minWidth', 'minHeight']);
1154 for (; i < l; i++) {
1155 me.hiddenDocked[i].hide();
1157 if (me.reExpander) {
1158 me.reExpander.updateFrame();
1159 me.reExpander.show();
1161 me.collapsed = true;
1164 me.doComponentLayout();
1168 me.resizer.disable();
1171 // Now we can restore the dimension we don't control to its original state
1172 if (Ext.Component.VERTICAL_DIRECTION.test(me.expandDirection)) {
1173 me.collapseMemento.restore('width');
1175 me.collapseMemento.restore('height');
1178 // If me Panel was configured with a collapse tool in its header, flip it's type
1179 if (me.collapseTool) {
1180 me.collapseTool.setType('expand-' + me.expandDirection);
1183 me.fireEvent('collapse', me);
1186 // Re-enable the toggle tool after an animated collapse
1187 if (animated && me.collapseTool) {
1188 me.collapseTool.enable();
1193 * Expands the panel body so that it becomes visible. Fires the {@link #beforeexpand} event which will
1194 * cancel the expand action if it returns false.
1195 * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
1196 * {@link #animCollapse} panel config)
1197 * @return {Ext.panel.Panel} this
1199 expand: function(animate) {
1201 if (!me.collapsed || me.fireEvent('beforeexpand', me, animate) === false) {
1206 l = me.hiddenDocked.length,
1207 direction = me.expandDirection,
1208 height = me.getHeight(),
1209 width = me.getWidth(),
1212 // Disable toggle tool during animated expand
1213 if (animate && me.collapseTool) {
1214 me.collapseTool.disable();
1217 // Show any docked items that we hid on collapse
1218 // And hide the injected reExpander Header
1219 for (; i < l; i++) {
1220 me.hiddenDocked[i].hidden = false;
1221 me.hiddenDocked[i].el.show();
1223 if (me.reExpander) {
1224 if (me.reExpander.temporary) {
1225 me.reExpander.hide();
1227 me.reExpander.removeClsWithUI(me.collapsedCls);
1228 me.reExpander.removeClsWithUI(me.collapsedCls + '-' + me.reExpander.dock);
1229 if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1230 me.reExpander.removeClsWithUI(me.collapsedCls + '-border-' + me.reExpander.dock);
1232 me.reExpander.updateFrame();
1236 // If me Panel was configured with a collapse tool in its header, flip it's type
1237 if (me.collapseTool) {
1238 me.collapseTool.setType('collapse-' + me.collapseDirection);
1241 // Unset the flag before the potential call to calculateChildBox to calculate our newly flexed size
1242 me.collapsed = false;
1244 // Collapsed means body element was hidden
1247 // Remove any collapsed styling before any animation begins
1248 me.removeClsWithUI(me.collapsedCls);
1249 // if (me.border === false) {
1250 // me.removeClsWithUI(me.collapsedCls + '-noborder');
1261 afteranimate: me.afterExpand,
1266 if ((direction == Ext.Component.DIRECTION_TOP) || (direction == Ext.Component.DIRECTION_BOTTOM)) {
1268 // If autoHeight, measure the height now we have shown the body element.
1269 if (me.autoHeight) {
1270 me.setCalculatedSize(me.width, null);
1271 anim.to.height = me.getHeight();
1273 // Must size back down to collapsed for the animation.
1274 me.setCalculatedSize(me.width, anim.from.height);
1276 // If we were flexed, then we can't just restore to the saved size.
1277 // We must restore to the currently correct, flexed size, so we much ask the Box layout what that is.
1278 else if (me.savedFlex) {
1279 me.flex = me.savedFlex;
1280 anim.to.height = me.ownerCt.layout.calculateChildBox(me).height;
1283 // Else, restore to saved height
1285 anim.to.height = me.expandedSize;
1288 // top needs animating upwards
1289 if (direction == Ext.Component.DIRECTION_TOP) {
1290 pos = me.getPosition()[1] - Ext.fly(me.el.dom.offsetParent).getRegion().top;
1291 anim.from.top = pos;
1292 anim.to.top = pos - (anim.to.height - height);
1294 } else if ((direction == Ext.Component.DIRECTION_LEFT) || (direction == Ext.Component.DIRECTION_RIGHT)) {
1296 // If autoWidth, measure the width now we have shown the body element.
1298 me.setCalculatedSize(null, me.height);
1299 anim.to.width = me.getWidth();
1301 // Must size back down to collapsed for the animation.
1302 me.setCalculatedSize(anim.from.width, me.height);
1304 // If we were flexed, then we can't just restore to the saved size.
1305 // We must restore to the currently correct, flexed size, so we much ask the Box layout what that is.
1306 else if (me.savedFlex) {
1307 me.flex = me.savedFlex;
1308 anim.to.width = me.ownerCt.layout.calculateChildBox(me).width;
1311 // Else, restore to saved width
1313 anim.to.width = me.expandedSize;
1316 // left needs animating leftwards
1317 if (direction == Ext.Component.DIRECTION_LEFT) {
1318 pos = me.getPosition()[0] - Ext.fly(me.el.dom.offsetParent).getRegion().left;
1319 anim.from.left = pos;
1320 anim.to.left = pos - (anim.to.width - width);
1327 me.setCalculatedSize(anim.to.width, anim.to.height);
1329 me.setLeft(anim.to.x);
1332 me.setTop(anim.to.y);
1334 me.afterExpand(false);
1340 afterExpand: function(animated) {
1343 if (me.collapseMemento) {
1344 // collapse has to use setSize (since it takes control of the component's size in
1345 // collapsed mode) and so we restore the original size now that the component has
1347 me.collapseMemento.restoreAll();
1350 me.setAutoScroll(me.initialConfig.autoScroll);
1352 // Restored to a calculated flex. Delete the set width and height properties so that flex works from now on.
1354 me.flex = me.savedFlex;
1355 delete me.savedFlex;
1360 // Reinstate layout out after Panel has re-expanded
1361 delete me.suspendLayout;
1362 if (animated && me.ownerCt) {
1363 // IE 6 has an intermittent repaint issue in this case so give
1364 // it a little extra time to catch up before laying out.
1365 Ext.defer(me.ownerCt.doLayout, Ext.isIE6 ? 1 : 0, me);
1369 me.resizer.enable();
1372 me.fireEvent('expand', me);
1374 // Re-enable the toggle tool after an animated expand
1375 if (animated && me.collapseTool) {
1376 me.collapseTool.enable();
1381 * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
1382 * @return {Ext.panel.Panel} this
1384 toggleCollapse: function() {
1385 if (this.collapsed) {
1386 this.expand(this.animCollapse);
1388 this.collapse(this.collapseDirection, this.animCollapse);
1394 getKeyMap : function(){
1396 this.keyMap = Ext.create('Ext.util.KeyMap', this.el, this.keys);
1402 initDraggable : function(){
1404 * <p>If this Panel is configured {@link #draggable}, this property will contain
1405 * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.</p>
1406 * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
1407 * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
1408 * @type Ext.dd.DragSource.
1411 this.dd = Ext.create('Ext.panel.DD', this, Ext.isBoolean(this.draggable) ? null : this.draggable);
1414 // private - helper function for ghost
1415 ghostTools : function() {
1417 origTools = this.initialConfig.tools;
1420 Ext.each(origTools, function(tool) {
1421 // Some tools can be full components, and copying them into the ghost
1422 // actually removes them from the owning panel. You could also potentially
1423 // end up with duplicate DOM ids as well. To avoid any issues we just make
1424 // a simple bare-minimum clone of each tool for ghosting purposes.
1438 // private - used for dragging
1439 ghost: function(cls) {
1441 ghostPanel = me.ghostPanel,
1445 ghostPanel = Ext.create('Ext.panel.Panel', {
1446 renderTo: document.body,
1450 frame: Ext.supports.CSS3BorderRadius ? me.frame : false,
1452 overlapHeader: me.overlapHeader,
1453 headerPosition: me.headerPosition,
1454 width: me.getWidth(),
1455 height: me.getHeight(),
1456 iconCls: me.iconCls,
1457 baseCls: me.baseCls,
1458 tools: me.ghostTools(),
1459 cls: me.baseCls + '-ghost ' + (cls ||'')
1461 me.ghostPanel = ghostPanel;
1463 ghostPanel.floatParent = me.floatParent;
1465 ghostPanel.setZIndex(Ext.Number.from(me.el.getStyle('zIndex'), 0));
1467 ghostPanel.toFront();
1469 ghostPanel.el.show();
1470 ghostPanel.setPosition(box.x, box.y);
1471 ghostPanel.setSize(box.width, box.height);
1473 if (me.floatingItems) {
1474 me.floatingItems.hide();
1480 unghost: function(show, matchPosition) {
1482 if (!me.ghostPanel) {
1485 if (show !== false) {
1487 if (matchPosition !== false) {
1488 me.setPosition(me.ghostPanel.getPosition());
1490 if (me.floatingItems) {
1491 me.floatingItems.show();
1493 Ext.defer(me.focus, 10, me);
1495 me.ghostPanel.el.hide();
1498 initResizable: function(resizable) {
1499 if (this.collapsed) {
1500 resizable.disabled = true;
1502 this.callParent([resizable]);