/** * @class Ext.panel.Panel * @extends Ext.AbstractPanel *Panel is a container that has specific functionality and structural components that make * it the perfect building block for application-oriented user interfaces.
*Panels are, by virtue of their inheritance from {@link Ext.Container}, capable * of being configured with a {@link Ext.Container#layout layout}, and containing child Components.
*When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether * those child elements need to be sized using one of Ext's built-in
*{@link Ext.Container#layout layout}
schemes. By * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders * child components, appending them one after the other inside the Container, and does not apply any sizing * at all.A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional * information).
*Panel also provides built-in {@link #collapsible expandable and collapsible behavior}, along with * a variety of {@link #tools prebuilt tool buttons} that can be wired up to provide other customized * behavior. Panels can be easily dropped into any {@link Ext.Container Container} or layout, and the * layout and rendering pipeline is {@link Ext.Container#add completely managed by the framework}.
*Note: By default, the
* @constructor * @param {Object} config The config object * @xtype panel */ Ext.define('Ext.panel.Panel', { extend: 'Ext.AbstractPanel', requires: [ 'Ext.panel.Header', 'Ext.fx.Anim', 'Ext.util.KeyMap', 'Ext.panel.DD', 'Ext.XTemplate', 'Ext.layout.component.Dock' ], alias: 'widget.panel', alternateClassName: 'Ext.Panel', /** * @cfg {String} collapsedCls * A CSS class to add to the panel's element after it has been collapsed (defaults to *{@link #closable close}
header tool destroys the Window resulting in * destruction of any child Components. This makes the Window object, and all its descendants unusable. To enable * re-use of a Window, use{@link #closeAction closeAction: 'hide'}
.'x-panel-collapsed'
). */ /** * @cfg {Boolean} animCollapse *true
to animate the transition when the panel is collapsed,false
to skip the * animation (defaults totrue
if the {@link Ext.Fx} class is available, otherwisefalse
). * May also be specified as the animation duration in milliseconds. */ animCollapse: Ext.enableFx, /** * @cfg {Number} minButtonWidth * Minimum width of all form buttons in pixels (defaults to 75). If set, this will * be used as the default value for the {@link Ext.form.Button#minWidth} config of * each Button added to the footer toolbar. Will be ignored for buttons that have this value configured some * other way, e.g. in their own config object or via the {@link Ext.lib.Container#defaults defaults config} of * their parent container. */ minButtonWidth: 75, /** * @cfg {Boolean} collapsed *true
to render the panel collapsed,false
to render it expanded (defaults to *false
). */ collapsed: false, /** * @cfg {Boolean} collapseFirst *true
to make sure the collapse/expand toggle button always renders first (to the left of) * any other tools in the panel's title bar,false
to render it last (defaults totrue
). */ collapseFirst: true, /** * @cfg {Boolean} hideCollapseTool *true
to hide the expand/collapse toggle button when{@link #collapsible} == true
, *false
to display it (defaults tofalse
). */ hideCollapseTool: false, /** * @cfg {Boolean} titleCollapse *true
to allow expanding and collapsing the panel (when{@link #collapsible} = true
) * by clicking anywhere in the header bar,false
) to allow it only by clicking to tool button * (defaults tofalse
)). */ titleCollapse: true, /** * @cfg {String} collapseMode *Important: this config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.
*When not 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. * 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.
*When a child item of a {@link Ext.layout.container.Border border layout}, this config has two options: *
*/ /** * @cfg {Mixed} placeHolder **
- *
alt
: Default.When collapsed, a placeholder Container is injected into the layout to represent the Panel * and to provide a UI with a Tool to allow the user to re-expand the Panel.- *
header
:The Panel collapses to leave a header visible as when not inside a {@link Ext.layout.container.Border border layout}.Important: This config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout} * when using the default
*'alt'
{@link #collapseMode}.b>Optional. A Component (or config object for a Component) to show in place of this Panel when this Panel is collapsed by a * {@link Ext.layout.container.Border border layout}. Defaults to a generated {@link Ext.panel.Header Header} * containing a {@link Ext.panel.Panel.Tool Tool} to re-expand the Panel.
*/ /** * @cfg {Boolean} floatable *Important: This config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.
* true to allow clicking a collapsed Panel's {@link #placeHolder} to display the Panel floated * above the layout, false to force the user to fully expand a collapsed region by * clicking the expand button to see it again (defaults to true). */ floatable: true, /** * @cfg {Boolean} collapsible *True to make the panel collapsible and have an expand/collapse toggle Tool added into * 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).
* See {@link #collapseMode} and {@link #collapseDirection} */ collapsible: false, /** * @cfg {Boolean} collapseDirection *The direction to collapse the Panel when the toggle button is clicked.
*Defaults to the {@link #headerPosition}
*Important: This config is ignored for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.
*Specify as
*/ /** * @cfg {Boolean} closable *'top'
,'bottom'
,'left'
or'right'
.True to display the 'close' tool button and allow the user to close the window, false to * hide the button and disallow closing the window (defaults to
*false
).By default, when close is requested by clicking the close button in the header, the {@link #close} * method will be called. This will {@link Ext.Component#destroy destroy} the Panel and its content * meaning that it may not be reused.
*To make closing a Panel hide the Panel so that it may be reused, set * {@link #closeAction} to 'hide'.
*/ closable: false, /** * @cfg {String} closeAction *The action to take when the close header tool is clicked: *
**
- *
'{@link #close}'
: Default* {@link #close remove} the window from the DOM and {@link Ext.Component#destroy destroy} * it and all descendant Components. The window will not be available to be * redisplayed via the {@link #show} method. *- *
'{@link #hide}'
:* {@link #hide} the window by setting visibility to hidden and applying negative offsets. * The window will be available to be redisplayed via the {@link #show} method. *Note: This behavior has changed! setting *does* affect the {@link #close} method * which will invoke the approriate closeAction. */ closeAction: 'destroy',
/** * @cfg {Boolean} preventHeader Prevent a Header from being created and shown. Defaults to false. */ preventHeader: false, /** * @cfg {String} headerPosition Specify as'top'
,'bottom'
,'left'
or'right'
. Defaults to'top'
. */ headerPosition: 'top', /** * @cfg {Boolean} frame * True to apply a frame to the panel. */ frame: false, renderTpl: [ '{bodyCls}' ], initComponent: function() { var me = this, cls; me.callParent(); if (me.unstyled) { me.baseCls = me.baseCSSPrefix + 'plain'; } if (me.frame) { me.ui = 'framed'; } me.collapsedCls = me.collapsedCls || me.baseCls + '-collapsed'; me.collapseDirection = me.collapseDirection || me.headerPosition || Ext.Component.DIRECTION_TOP; // Backwards compatibility me.bridgeToolbars(); if (me.border === false) { me.hideBorders(); } }, hideBorders : function() { var me = this, cls = me.baseCls + '-noborder'; me.addCls(cls); if (me.rendered) { me.body.addCls(cls + '-body'); } else { me.renderData.bodyCls = me.renderData.bodyCls || '' + (' ' + cls + '-body'); } }, showBorders : function() { var me = this, cls = me.baseCls + '-noborder'; me.removeCls(cls); if (me.rendered) { me.body.removeCls(cls + '-body'); } else { me.renderData.bodyCls = me.renderData.bodyCls.replace(cls + '-body', ''); } }, beforeDestroy: function() { Ext.destroy( this.ghostPanel ); this.callParent(); }, initAria: function() { Ext.panel.Panel.superclass.initAria.call(this); this.initHeaderAria(); }, initHeaderAria: function() { var me = this, el = me.el, header = me.header; if (el && header) { el.dom.setAttribute('aria-labelledby', header.titleCmp.id); } }, /** * Set a title for the panel's header. See {@link Ext.panel.Header#title}. * @param {String} title */ setTitle: function(title) { var me = this; me.title = title; if (me.header) { me.header.setTitle(title); } else { me.updateHeader(); } }, /** * Set the iconCls for the panel's header. See {@link Ext.panel.Header#iconCls}. * @param cls */ setIconClass: function(cls) { this.iconCls = cls; var header = this.header; if (header) { header.setIconClass(cls); } }, initItems: function() { // Catch addition of descendant fields this.on('add', this.onSubCmpAdded, this); this.callParent(arguments); }, onSubCmpAdded: function(parent, cmp) { var minButtonWidth = this.minButtonWidth; if (minButtonWidth && cmp.isButton/* && parent && parent.ui == 'footer'*/) { if (!cmp.hasOwnProperty('minWidth')) { cmp.minWidth = minButtonWidth; } } }, bridgeToolbars: function() { var me = this, fbar, buttons = me.buttons, minButtonWidth = me.minButtonWidth, initToolbar = function(toolbar, pos) { if (Ext.isArray(toolbar)) { toolbar = { xtype: 'toolbar', items: toolbar }; } else if (!toolbar.xtype) { toolbar.xtype = 'toolbar'; } toolbar.dock = pos; return toolbar; }; // Apply the minButtonWidth config to the items in the 'buttons' toolbar config if (buttons && minButtonWidth) { Ext.each(buttons, function(button) { if (Ext.isObject(button) && !('minWidth' in button)) { button.minWidth = minButtonWidth; } }); } // Backwards compatibility if (me.tbar) { me.addDocked(initToolbar(me.tbar, 'top')); me.tbar = null; } if (me.bbar) { me.addDocked(initToolbar(me.bbar, 'bottom')); me.bbar = null; } if (me.buttons) { me.fbar = me.buttons; me.buttons = null; } if (me.fbar) { fbar = initToolbar(me.fbar, 'bottom'); fbar.ui = 'footer'; fbar = me.addDocked(fbar)[0]; fbar.insert(0, { flex: 1, xtype: 'component' }); me.fbar = null; } }, /** * @private. * Tools are a Panel-specific capabilty. * Panel uses initTools. Subclasses may contribute tools by implementing addTools. */ initTools: function() { var me = this; me.tools = me.tools || []; // Add a collapse tool unless configured to not show a collapse tool // or to not even show a header. if (me.collapsible && !(me.hideCollapseTool || me.header === false)) { me.collapseTool = me.expandTool = me.createComponent({ xtype: 'tool', type: 'collapse-' + me.collapseDirection, expandType: me.getOppositeDirection(me.collapseDirection), handler: me.toggleCollapse, scope: me }); // Prepend collapse tool is configured to do so. if (me.collapseFirst) { me.tools.unshift(me.collapseTool); } } // Add subclass-specific tools. this.addTools(); // Make Panel closable. if (this.closable) { this.addCls(this.baseCls + '-closable'); this.addTool({ type: 'close', handler: Ext.Function.bind(this.close, this, []) }); } // Append collapse tool if needed. if (me.collapseTool && !me.collapseFirst) { me.tools.push(me.collapseTool); } }, /** * @private * Template method to be implemented in subclasses to add their tools after the collapsible tool. */ addTools: Ext.emptyFn, /** *{baseCls}-body-framed {baseCls}-body-{ui} "style="{bodyStyle}" >Closes the Panel. By default, this method, removes it from the DOM, {@link Ext.Component#destroy destroy}s * the Panel object and all its descendant Components. The {@link #beforeclose beforeclose} * event is fired before the close happens and will cancel the close action if it returns false.
*
Note: This method is not affected by the {@link #closeAction} setting which * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}. * To hide the Panel without destroying it, call {@link #hide}.
*/ close: function() { if (this.fireEvent('beforeclose', this) !== false) { this.doClose(); } }, // private doClose: function() { this.fireEvent('close', this); this[this.closeAction](); }, onRender: function(ct, position) { var me = this; // Add class-specific header tools. // Panel adds collapsible and closable. me.initTools(); // Dock the header/title me.updateHeader(); // Call to super after adding the header, to prevent an unnecessary re-layout me.callParent(arguments); }, /** * Create, hide, or show the header component as appropriate based on the current config. * @private */ updateHeader: function() { var me = this, header = me.header, title = me.title, tools = me.tools; if (!me.preventHeader && (title || (tools && tools.length))) { if (!header) { header = me.header = new Ext.panel.Header({ title : title, orientation : (me.headerPosition == 'left' || me.headerPosition == 'right') ? 'vertical' : 'horizontal', dock : me.headerPosition || 'top', textCls : me.headerTextCls, iconCls : me.iconCls, baseCls : me.baseCls + '-header', tools : tools, ui : me.ui, indicateDrag: me.draggable, frame : me.frame, ignoreParentFrame : me.frame || me.overlapHeader }); me.addDocked(header, 0); // Reference the Header's tool array. // Header injects named references. me.tools = header.tools; } header.show(); this.initHeaderAria(); } else if (header) { header.hide(); } }, // private getContentTarget: function() { return this.body; }, getTargetEl: function() { return this.body || this.frameBody || this.el; }, addTool: function(tool) { this.tools.push(tool); var header = this.header; if (header) { header.addTool(tool); } this.updateHeader(); }, getOppositeDirection: function(d) { var c = Ext.Component; switch (d) { case c.DIRECTION_TOP: return c.DIRECTION_BOTTOM; case c.DIRECTION_RIGHT: return c.DIRECTION_LEFT; case c.DIRECTION_BOTTOM: return c.DIRECTION_TOP; case c.DIRECTION_LEFT: return c.DIRECTION_RIGHT; } }, /** * Collapses the panel body so that the body becomes hidden. Docked Components parallel to the * border towards which the collapse takes place will remain visible. Fires the {@link #beforecollapse} event which will * cancel the collapse action if it returns false. * @param {Number} direction. The direction to collapse towards. Must be one of
If this Panel is configured {@link #draggable}, this property will contain * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.
* The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource} * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}. * @type Ext.dd.DragSource. * @property dd */ this.dd = new Ext.panel.DD(this, Ext.isBoolean(this.draggable) ? null : this.draggable); }, // private - used for dragging ghost: function(cls) { var me = this, box = me.getBox(); if (!me.ghostPanel) { me.ghostPanel = new Ext.panel.Panel({ renderTo: document.body, floating: true, frame: me.frame, title: me.title, overlapHeader: me.overlapHeader, headerPosition: me.headerPosition, width: me.getWidth(), height: me.getHeight(), baseCls: me.baseCls, tools: [{ type: 'placeholder' }], cls: me.baseCls + '-ghost ' + (cls ||'') }); } me.ghostPanel.floatParent = me.floatParent; me.ghostPanel.setZIndex(Ext.num(me.el.getStyle('zIndex'), 0)); me.ghostPanel.el.show(); me.ghostPanel.setPosition(box.x, box.y); me.ghostPanel.setSize(box.width, box.height); me.el.hide(); if (me.floatingItems) { me.floatingItems.hide(); } return me.ghostPanel; }, // private unghost: function(show, matchPosition) { var me = this; if (!me.ghostPanel) { return; } if (show !== false) { me.el.show(); if (matchPosition !== false) { me.setPosition(me.ghostPanel.getPosition()); } if (me.floatingItems) { me.floatingItems.show(); } Ext.defer(me.focus, 10, me); } me.ghostPanel.el.hide(); }, // should update body's el. doAutoLoad: function() { } });