2 * @class Ext.layout.container.Border
3 * @extends Ext.layout.container.Container
4 * <p>This is a multi-pane, application-oriented UI layout style that supports multiple
5 * nested panels, automatic bars between regions and built-in
6 * {@link Ext.panel.Panel#collapsible expanding and collapsing} of regions.</p>
7 * <p>This class is intended to be extended or created via the <code>layout:'border'</code>
8 * {@link Ext.container.Container#layout} config, and should generally not need to be created directly
9 * via the new keyword.</p>
10 * {@img Ext.layout.container.Border/Ext.layout.container.Border.png Ext.layout.container.Border container layout}
11 * <p>Example usage:</p>
13 Ext.create('Ext.panel.Panel', {
16 title: 'Border Layout',
19 title: 'South Region is resizable',
20 region: 'south', // position for region
23 split: true, // enable resizing
26 // xtype: 'panel' implied by default
27 title: 'West Region is collapsible',
32 collapsible: true, // make collapsible
33 id: 'west-region-container',
36 title: 'Center Region',
37 region: 'center', // center region is required, no width/height specified
42 renderTo: Ext.getBody()
45 * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
46 * <li>Any Container using the Border layout <b>must</b> have a child item with <code>region:'center'</code>.
47 * The child item in the center region will always be resized to fill the remaining space not used by
48 * the other regions in the layout.</li>
49 * <li>Any child items with a region of <code>west</code> or <code>east</code> may be configured with either
50 * an initial <code>width</code>, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width <b>string</b> (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of <code>1</code>.</li>
51 * <li>Any child items with a region of <code>north</code> or <code>south</code> may be configured with either
52 * an initial <code>height</code>, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height <b>string</b> (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of <code>1</code>.</li>
53 * <li>The regions of a BorderLayout are <b>fixed at render time</b> and thereafter, its child Components may not be removed or added</b>.To add/remove
54 * Components within a BorderLayout, have them wrapped by an additional Container which is directly
55 * managed by the BorderLayout. If the region is to be collapsible, the Container used directly
56 * by the BorderLayout manager should be a Panel. In the following example a Container (an Ext.panel.Panel)
57 * is added to the west region:<pre><code>
58 wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
59 wrc.{@link Ext.container.Container#removeAll removeAll}();
60 wrc.{@link Ext.container.Container#add add}({
66 * <li><b>There is no BorderLayout.Region class in ExtJS 4.0+</b></li>
69 Ext.define('Ext.layout.container.Border', {
71 alias: ['layout.border'],
72 extend: 'Ext.layout.container.Container',
73 requires: ['Ext.resizer.Splitter', 'Ext.container.Container', 'Ext.fx.Anim'],
74 alternateClassName: 'Ext.layout.BorderLayout',
76 targetCls: Ext.baseCSSPrefix + 'border-layout-ct',
78 itemCls: Ext.baseCSSPrefix + 'border-item',
80 bindToOwnerCtContainer: true,
84 percentageRe: /(\d+)%/,
93 constructor: function(config) {
94 this.initialConfig = config;
95 this.callParent(arguments);
98 onLayout: function() {
100 if (!me.borderLayoutInitialized) {
101 me.initializeBorderLayout();
104 // Delegate this operation to the shadow "V" or "H" box layout, and then down to any embedded layout.
105 me.fixHeightConstraints();
106 me.shadowLayout.onLayout();
107 if (me.embeddedContainer) {
108 me.embeddedContainer.layout.onLayout();
111 // If the panel was originally configured with collapsed: true, it will have
112 // been initialized with a "borderCollapse" flag: Collapse it now before the first layout.
113 if (!me.initialCollapsedComplete) {
114 Ext.iterate(me.regions, function(name, region){
115 if (region.borderCollapse) {
116 me.onBeforeRegionCollapse(region, region.collapseDirection, false, 0);
119 me.initialCollapsedComplete = true;
123 isValidParent : function(item, target, position) {
124 if (!this.borderLayoutInitialized) {
125 this.initializeBorderLayout();
128 // Delegate this operation to the shadow "V" or "H" box layout.
129 return this.shadowLayout.isValidParent(item, target, position);
132 beforeLayout: function() {
133 if (!this.borderLayoutInitialized) {
134 this.initializeBorderLayout();
137 // Delegate this operation to the shadow "V" or "H" box layout.
138 this.shadowLayout.beforeLayout();
141 renderItems: function(items, target) {
143 Ext.Error.raise('This should not be called');
147 renderItem: function(item) {
149 Ext.Error.raise('This should not be called');
153 initializeBorderLayout: function() {
156 items = me.getLayoutItems(),
158 regions = (me.regions = {}),
165 // Map of Splitters for each region
169 for (; i < ln; i++) {
171 regions[comp.region] = comp;
173 // Intercept collapsing to implement showing an alternate Component as a collapsed placeholder
174 if (comp.region != 'center' && comp.collapsible && comp.collapseMode != 'header') {
176 // This layout intercepts any initial collapsed state. Panel must not do this itself.
177 comp.borderCollapse = comp.collapsed;
178 delete comp.collapsed;
181 beforecollapse: me.onBeforeRegionCollapse,
182 beforeexpand: me.onBeforeRegionExpand,
183 destroy: me.onRegionDestroy,
190 if (!regions.center) {
191 Ext.Error.raise("You must specify a center region when defining a BorderLayout.");
194 comp = regions.center;
199 comp.maintainFlex = true;
201 // Begin the VBox and HBox item list.
204 comp.collapseDirection = Ext.Component.DIRECTION_LEFT;
205 hBoxItems.push(comp);
207 hBoxItems.push(me.splitters.west = me.createSplitter(comp));
209 percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
211 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
215 comp = regions.north;
217 comp.collapseDirection = Ext.Component.DIRECTION_TOP;
218 vBoxItems.push(comp);
220 vBoxItems.push(me.splitters.north = me.createSplitter(comp));
222 percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
224 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
229 // Decide into which Collection the center region goes.
230 if (regions.north || regions.south) {
231 if (regions.east || regions.west) {
233 // Create the embedded center. Mark it with the region: 'center' property so that it can be identified as the center.
234 vBoxItems.push(me.embeddedContainer = Ext.create('Ext.container.Container', {
237 id: me.owner.id + '-embedded-center',
238 cls: Ext.baseCSSPrefix + 'border-item',
239 flex: regions.center.flex,
246 hBoxItems.push(regions.center);
248 // No east or west: the original center goes straight into the vbox
250 vBoxItems.push(regions.center);
253 // If we have no north or south, then the center is part of the HBox items
255 hBoxItems.push(regions.center);
258 // Finish off the VBox and HBox item list.
259 comp = regions.south;
261 comp.collapseDirection = Ext.Component.DIRECTION_BOTTOM;
263 vBoxItems.push(me.splitters.south = me.createSplitter(comp));
265 percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
267 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
270 vBoxItems.push(comp);
274 comp.collapseDirection = Ext.Component.DIRECTION_RIGHT;
276 hBoxItems.push(me.splitters.east = me.createSplitter(comp));
278 percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
280 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
283 hBoxItems.push(comp);
286 // Create the injected "items" collections for the Containers.
287 // If we have north or south, then the shadow Container will be a VBox.
288 // If there are also east or west regions, its center will be a shadow HBox.
289 // If there are *only* east or west regions, then the shadow layout will be an HBox (or Fit).
290 if (regions.north || regions.south) {
292 me.shadowContainer = Ext.create('Ext.container.Container', {
295 layout: Ext.applyIf({
300 me.createItems(me.shadowContainer, vBoxItems);
302 // Allow the Splitters to orientate themselves
303 if (me.splitters.north) {
304 me.splitters.north.ownerCt = me.shadowContainer;
306 if (me.splitters.south) {
307 me.splitters.south.ownerCt = me.shadowContainer;
310 // Inject items into the HBox Container if there is one - if there was an east or west.
311 if (me.embeddedContainer) {
312 me.embeddedContainer.ownerCt = me.shadowContainer;
313 me.createItems(me.embeddedContainer, hBoxItems);
315 // Allow the Splitters to orientate themselves
316 if (me.splitters.east) {
317 me.splitters.east.ownerCt = me.embeddedContainer;
319 if (me.splitters.west) {
320 me.splitters.west.ownerCt = me.embeddedContainer;
323 // These spliiters need to be constrained by components one-level below
324 // the component in their vobx. We update the min/maxHeight on the helper
325 // (embeddedContainer) prior to starting the split/drag. This has to be
326 // done on-the-fly to allow min/maxHeight of the E/C/W regions to be set
328 Ext.each([me.splitters.north, me.splitters.south], function (splitter) {
330 splitter.on('beforedragstart', me.fixHeightConstraints, me);
334 // The east or west region wanted a percentage
335 if (horizontalFlex) {
336 regions.center.flex -= horizontalFlex;
338 // The north or south region wanted a percentage
340 me.embeddedContainer.flex -= verticalFlex;
343 // The north or south region wanted a percentage
345 regions.center.flex -= verticalFlex;
349 // If we have no north or south, then there's only one Container, and it's
350 // an HBox, or, if only a center region was specified, a Fit.
352 me.shadowContainer = Ext.create('Ext.container.Container', {
355 layout: Ext.applyIf({
356 type: (hBoxItems.length == 1) ? 'fit' : 'hbox',
360 me.createItems(me.shadowContainer, hBoxItems);
362 // Allow the Splitters to orientate themselves
363 if (me.splitters.east) {
364 me.splitters.east.ownerCt = me.shadowContainer;
366 if (me.splitters.west) {
367 me.splitters.west.ownerCt = me.shadowContainer;
370 // The east or west region wanted a percentage
371 if (horizontalFlex) {
372 regions.center.flex -= verticalFlex;
376 // Create upward links from the region Components to their shadow ownerCts
377 for (i = 0, items = me.shadowContainer.items.items, ln = items.length; i < ln; i++) {
378 items[i].shadowOwnerCt = me.shadowContainer;
380 if (me.embeddedContainer) {
381 for (i = 0, items = me.embeddedContainer.items.items, ln = items.length; i < ln; i++) {
382 items[i].shadowOwnerCt = me.embeddedContainer;
386 // This is the layout that we delegate all operations to
387 me.shadowLayout = me.shadowContainer.getLayout();
389 me.borderLayoutInitialized = true;
392 setupState: function(comp){
393 var getState = comp.getState;
394 comp.getState = function(){
395 // call the original getState
396 var state = getState.call(comp) || {},
397 region = comp.region;
399 state.collapsed = !!comp.collapsed;
400 if (region == 'west' || region == 'east') {
401 state.width = comp.getWidth();
403 state.height = comp.getHeight();
407 comp.addStateEvents(['collapse', 'expand', 'resize']);
411 * Create the items collection for our shadow/embedded containers
414 createItems: function(container, items){
415 // Have to inject an items Collection *after* construction.
416 // The child items of the shadow layout must retain their original, user-defined ownerCt
417 delete container.items;
418 container.initItems();
419 container.items.addAll(items);
423 // Create a splitter for a child of the layout.
424 createSplitter: function(comp) {
426 interceptCollapse = (comp.collapseMode != 'header'),
429 resizer = Ext.create('Ext.resizer.Splitter', {
430 hidden: !!comp.hidden,
431 collapseTarget: comp,
432 performCollapse: !interceptCollapse,
433 listeners: interceptCollapse ? {
435 fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
436 element: 'collapseEl'
441 // Mini collapse means that the splitter is the placeholder Component
442 if (comp.collapseMode == 'mini') {
443 comp.placeholder = resizer;
446 // Arrange to hide/show a region's associated splitter when the region is hidden/shown
448 hide: me.onRegionVisibilityChange,
449 show: me.onRegionVisibilityChange,
456 // Propogates the min/maxHeight values from the inner hbox items to its container.
457 fixHeightConstraints: function () {
459 ct = me.embeddedContainer,
460 maxHeight = 1e99, minHeight = -1;
466 ct.items.each(function (item) {
467 if (Ext.isNumber(item.maxHeight)) {
468 maxHeight = Math.max(maxHeight, item.maxHeight);
470 if (Ext.isNumber(item.minHeight)) {
471 minHeight = Math.max(minHeight, item.minHeight);
475 ct.maxHeight = maxHeight;
476 ct.minHeight = minHeight;
479 // Hide/show a region's associated splitter when the region is hidden/shown
480 onRegionVisibilityChange: function(comp){
481 this.splitters[comp.region][comp.hidden ? 'hide' : 'show']();
485 // Called when a splitter mini-collapse tool is clicked on.
486 // The listener is only added if this layout is controlling collapsing,
487 // not if the component's collapseMode is 'mini' or 'header'.
488 onSplitterCollapseClick: function(comp) {
489 if (comp.collapsed) {
490 this.onPlaceHolderToolClick(null, null, null, {client: comp});
497 * <p>Return the {@link Ext.panel.Panel#placeholder placeholder} Component to which the passed child Panel of the layout will collapse.
498 * By default, this will be a {@link Ext.panel.Header Header} component (Docked to the appropriate border). See {@link Ext.panel.Panel#placeholder placeholder}.
499 * config to customize this.</p>
500 * <p><b>Note that this will be a fully instantiated Component, but will only be <i>rendered</i> when the Panel is first collapsed.</b></p>
501 * @param {Panel} panel The child Panel of the layout for which to return the {@link Ext.panel.Panel#placeholder placeholder}.
502 * @returns {Component} The Panel's {@link Ext.panel.Panel#placeholder placeholder} unless the {@link Ext.panel.Panel#collapseMode collapseMode} is
503 * <code>'header'</code>, in which case <i>undefined</i> is returned.
505 getPlaceholder: function(comp) {
507 placeholder = comp.placeholder,
508 shadowContainer = comp.shadowOwnerCt,
509 shadowLayout = shadowContainer.layout,
510 oppositeDirection = Ext.panel.Panel.prototype.getOppositeDirection(comp.collapseDirection),
511 horiz = (comp.region == 'north' || comp.region == 'south');
513 // No placeholder if the collapse mode is not the Border layout default
514 if (comp.collapseMode == 'header') {
518 // Provide a replacement Container with an expand tool
520 if (comp.collapseMode == 'mini') {
521 placeholder = Ext.create('Ext.resizer.Splitter', {
522 id: 'collapse-placeholder-' + comp.id,
523 collapseTarget: comp,
524 performCollapse: false,
527 fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
528 element: 'collapseEl'
532 placeholder.addCls(placeholder.collapsedCls);
535 id: 'collapse-placeholder-' + comp.id,
536 margins: comp.initialConfig.margins || Ext.getClass(comp).prototype.margins,
538 orientation: horiz ? 'horizontal' : 'vertical',
540 textCls: comp.headerTextCls,
541 iconCls: comp.iconCls,
542 baseCls: comp.baseCls + '-header',
544 indicateDrag: comp.draggable,
545 cls: Ext.baseCSSPrefix + 'region-collapsed-placeholder ' + Ext.baseCSSPrefix + 'region-collapsed-' + comp.collapseDirection + '-placeholder',
546 listeners: comp.floatable ? {
549 me.floatCollapsedPanel(e, comp);
555 // Hack for IE6/7/IEQuirks's inability to display an inline-block
556 if ((Ext.isIE6 || Ext.isIE7 || (Ext.isIEQuirks)) && !horiz) {
557 placeholder.width = 25;
559 if (!comp.hideCollapseTool) {
560 placeholder[horiz ? 'tools' : 'items'] = [{
563 type: 'expand-' + oppositeDirection,
564 handler: me.onPlaceHolderToolClick,
569 placeholder = me.owner.createComponent(placeholder);
570 if (comp.isXType('panel')) {
572 titlechange: me.onRegionTitleChange,
573 iconchange: me.onRegionIconChange,
579 // The collapsed Component holds a reference to its placeholder and vice versa
580 comp.placeholder = placeholder;
581 placeholder.comp = comp;
588 * Update the placeholder title when panel title has been set or changed.
590 onRegionTitleChange: function(comp, newTitle) {
591 comp.placeholder.setTitle(newTitle);
596 * Update the placeholder iconCls when panel iconCls has been set or changed.
598 onRegionIconChange: function(comp, newIconCls) {
599 comp.placeholder.setIconCls(newIconCls);
604 * Calculates the size and positioning of the passed child item. Must be present because Panel's expand,
605 * when configured with a flex, calls this method on its ownerCt's layout.
606 * @param {Component} child The child Component to calculate the box for
607 * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height.
609 calculateChildBox: function(comp) {
611 if (me.shadowContainer.items.contains(comp)) {
612 return me.shadowContainer.layout.calculateChildBox(comp);
614 else if (me.embeddedContainer && me.embeddedContainer.items.contains(comp)) {
615 return me.embeddedContainer.layout.calculateChildBox(comp);
621 * Intercepts the Panel's own collapse event and perform's substitution of the Panel
622 * with a placeholder Header orientated in the appropriate dimension.
623 * @param comp The Panel being collapsed.
626 * @returns {Boolean} false to inhibit the Panel from performing its own collapse.
628 onBeforeRegionCollapse: function(comp, direction, animate) {
632 miniCollapse = comp.collapseMode == 'mini',
633 shadowContainer = comp.shadowOwnerCt,
634 shadowLayout = shadowContainer.layout,
635 placeholder = comp.placeholder,
636 sl = me.owner.suspendLayout,
637 scsl = shadowContainer.suspendLayout,
638 isNorthOrWest = (comp.region == 'north' || comp.region == 'west'); // Flag to keep the placeholder non-adjacent to any Splitter
640 // Do not trigger a layout during transition to collapsed Component
641 me.owner.suspendLayout = true;
642 shadowContainer.suspendLayout = true;
644 // Prevent upward notifications from downstream layouts
645 shadowLayout.layoutBusy = true;
646 if (shadowContainer.componentLayout) {
647 shadowContainer.componentLayout.layoutBusy = true;
649 me.shadowContainer.layout.layoutBusy = true;
650 me.layoutBusy = true;
651 me.owner.componentLayout.layoutBusy = true;
653 // Provide a replacement Container with an expand tool
655 placeholder = me.getPlaceholder(comp);
658 // placeholder already in place; show it.
659 if (placeholder.shadowOwnerCt === shadowContainer) {
662 // Insert the collapsed placeholder Component into the appropriate Box layout shadow Container
663 // It must go next to its client Component, but non-adjacent to the splitter so splitter can find its collapse client.
664 // Inject an ownerCt value pointing to the owner, border layout Container as the user will expect.
666 shadowContainer.insert(shadowContainer.items.indexOf(comp) + (isNorthOrWest ? 0 : 1), placeholder);
667 placeholder.shadowOwnerCt = shadowContainer;
668 placeholder.ownerCt = me.owner;
671 // Flag the collapsing Component as hidden and show the placeholder.
672 // This causes the shadow Box layout's calculateChildBoxes to calculate the correct new arrangement.
673 // We hide or slideOut the Component's element
676 if (!placeholder.rendered) {
677 shadowLayout.renderItem(placeholder, shadowLayout.innerCt);
679 // The inserted placeholder does not have the proper size, so copy the width
680 // for N/S or the height for E/W from the component. This fixes EXTJSIV-1562
681 // without recursive layouts. This is only an issue initially. After this time,
682 // placeholder will have the correct width/height set by the layout (which has
683 // already happened when we get here initially).
684 if (comp.region == 'north' || comp.region == 'south') {
685 placeholder.setCalculatedSize(comp.getWidth());
687 placeholder.setCalculatedSize(undefined, comp.getHeight());
691 // Jobs to be done after the collapse has been done
692 function afterCollapse() {
693 // Reinstate automatic laying out.
694 me.owner.suspendLayout = sl;
695 shadowContainer.suspendLayout = scsl;
696 delete shadowLayout.layoutBusy;
697 if (shadowContainer.componentLayout) {
698 delete shadowContainer.componentLayout.layoutBusy;
700 delete me.shadowContainer.layout.layoutBusy;
701 delete me.layoutBusy;
702 delete me.owner.componentLayout.layoutBusy;
704 // Fire the collapse event: The Panel has in fact been collapsed, but by substitution of an alternative Component
705 comp.collapsed = true;
706 comp.fireEvent('collapse', comp);
710 * Set everything to the new positions. Note that we
711 * only want to animate the collapse if it wasn't configured
712 * initially with collapsed: true
714 if (comp.animCollapse && me.initialCollapsedComplete) {
715 shadowLayout.layout();
716 compEl.dom.style.zIndex = 100;
718 // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
720 placeholder.el.hide();
722 compEl.slideOut(me.slideDirection[comp.region], {
723 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
725 afteranimate: function() {
726 compEl.show().setLeftTop(-10000, -10000);
727 compEl.dom.style.zIndex = '';
729 // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
731 placeholder.el.slideIn(me.slideDirection[comp.region], {
741 compEl.setLeftTop(-10000, -10000);
742 shadowLayout.layout();
749 // Hijack the expand operation to remove the placeholder and slide the region back in.
750 onBeforeRegionExpand: function(comp, animate) {
751 this.onPlaceHolderToolClick(null, null, null, {client: comp});
755 // Called when the collapsed placeholder is clicked to reinstate a "collapsed" (in reality hidden) Panel.
756 onPlaceHolderToolClick: function(e, target, owner, tool) {
760 // Hide the placeholder unless it was the Component's preexisting splitter
761 hidePlaceholder = (comp.collapseMode != 'mini') || !comp.split,
764 placeholder = comp.placeholder,
765 placeholderEl = placeholder.el,
766 shadowContainer = comp.shadowOwnerCt,
767 shadowLayout = shadowContainer.layout,
769 sl = me.owner.suspendLayout,
770 scsl = shadowContainer.suspendLayout,
773 // If the slide in is still going, stop it.
774 // This will either leave the Component in its fully floated state (which is processed below)
775 // or in its collapsed state. Either way, we expand it..
776 if (comp.getActiveAnimation()) {
777 comp.stopAnimation();
780 // If the Component is fully floated when they click the placeholder Tool,
781 // it will be primed with a slide out animation object... so delete that
782 // and remove the mouseout listeners
783 if (comp.slideOutAnim) {
784 // Remove mouse leave monitors
785 compEl.un(comp.panelMouseMon);
786 placeholderEl.un(comp.placeholderMouseMon);
788 delete comp.slideOutAnim;
789 delete comp.panelMouseMon;
790 delete comp.placeholderMouseMon;
792 // If the Panel was floated and primed with a slideOut animation, we don't want to animate its layout operation.
796 // Do not trigger a layout during transition to expanded Component
797 me.owner.suspendLayout = true;
798 shadowContainer.suspendLayout = true;
800 // Prevent upward notifications from downstream layouts
801 shadowLayout.layoutBusy = true;
802 if (shadowContainer.componentLayout) {
803 shadowContainer.componentLayout.layoutBusy = true;
805 me.shadowContainer.layout.layoutBusy = true;
806 me.layoutBusy = true;
807 me.owner.componentLayout.layoutBusy = true;
809 // Unset the hidden and collapsed flags set in onBeforeRegionCollapse. The shadowLayout will now take it into account
810 // Find where the shadow Box layout plans to put the expanding Component.
812 comp.collapsed = false;
813 if (hidePlaceholder) {
814 placeholder.hidden = true;
816 toCompBox = shadowLayout.calculateChildBox(comp);
818 // Show the collapse tool in case it was hidden by the slide-in
819 if (comp.collapseTool) {
820 comp.collapseTool.show();
823 // If we're going to animate, we need to hide the component before moving it back into position
824 if (comp.animCollapse && !isFloating) {
825 compEl.setStyle('visibility', 'hidden');
827 compEl.setLeftTop(toCompBox.left, toCompBox.top);
829 // Equalize the size of the expanding Component prior to animation
830 // in case the layout area has changed size during the time it was collapsed.
831 curSize = comp.getSize();
832 if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
833 me.setItemSize(comp, toCompBox.width, toCompBox.height);
836 // Jobs to be done after the expand has been done
837 function afterExpand() {
838 // Reinstate automatic laying out.
839 me.owner.suspendLayout = sl;
840 shadowContainer.suspendLayout = scsl;
841 delete shadowLayout.layoutBusy;
842 if (shadowContainer.componentLayout) {
843 delete shadowContainer.componentLayout.layoutBusy;
845 delete me.shadowContainer.layout.layoutBusy;
846 delete me.layoutBusy;
847 delete me.owner.componentLayout.layoutBusy;
849 // In case it was floated out and they clicked the re-expand tool
850 comp.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');
852 // Fire the expand event: The Panel has in fact been expanded, but by removal of an alternative Component
853 comp.fireEvent('expand', comp);
856 // Hide the placeholder
857 if (hidePlaceholder) {
858 placeholder.el.hide();
861 // Slide the expanding Component to its new position.
862 // When that is done, layout the layout.
863 if (comp.animCollapse && !isFloating) {
864 compEl.dom.style.zIndex = 100;
865 compEl.slideIn(me.slideDirection[comp.region], {
866 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
868 afteranimate: function() {
869 compEl.dom.style.zIndex = '';
871 shadowLayout.onLayout();
877 shadowLayout.onLayout();
882 floatCollapsedPanel: function(e, comp) {
884 if (comp.floatable === false) {
890 placeholder = comp.placeholder,
891 placeholderEl = placeholder.el,
892 shadowContainer = comp.shadowOwnerCt,
893 shadowLayout = shadowContainer.layout,
894 placeholderBox = shadowLayout.getChildBox(placeholder),
895 scsl = shadowContainer.suspendLayout,
896 curSize, toCompBox, compAnim;
898 // Ignore clicks on tools.
899 if (e.getTarget('.' + Ext.baseCSSPrefix + 'tool')) {
903 // It's *being* animated, ignore the click.
904 // Possible future enhancement: Stop and *reverse* the current active Fx.
905 if (compEl.getActiveAnimation()) {
909 // If the Component is already fully floated when they click the placeholder,
910 // it will be primed with a slide out animation object... so slide it out.
911 if (comp.slideOutAnim) {
912 me.slideOutFloatedComponent(comp);
916 // Function to be called when the mouse leaves the floated Panel
917 // Slide out when the mouse leaves the region bounded by the slid Component and its placeholder.
918 function onMouseLeaveFloated(e) {
919 var slideRegion = compEl.getRegion().union(placeholderEl.getRegion()).adjust(1, -1, -1, 1);
921 // If mouse is not within slide Region, slide it out
922 if (!slideRegion.contains(e.getPoint())) {
923 me.slideOutFloatedComponent(comp);
927 // Monitor for mouseouting of the placeholder. Hide it if they exit for half a second or more
928 comp.placeholderMouseMon = placeholderEl.monitorMouseLeave(500, onMouseLeaveFloated);
930 // Do not trigger a layout during slide out of the Component
931 shadowContainer.suspendLayout = true;
933 // Prevent upward notifications from downstream layouts
934 me.layoutBusy = true;
935 me.owner.componentLayout.layoutBusy = true;
937 // The collapse tool is hidden while slid.
938 // It is re-shown on expand.
939 if (comp.collapseTool) {
940 comp.collapseTool.hide();
943 // Set flags so that the layout will calculate the boxes for what we want
945 comp.collapsed = false;
946 placeholder.hidden = true;
948 // Recalculate new arrangement of the Component being floated.
949 toCompBox = shadowLayout.calculateChildBox(comp);
950 placeholder.hidden = false;
952 // Component to appear just after the placeholder, whatever "after" means in the context of the shadow Box layout.
953 if (comp.region == 'north' || comp.region == 'west') {
954 toCompBox[shadowLayout.parallelBefore] += placeholderBox[shadowLayout.parallelPrefix] - 1;
956 toCompBox[shadowLayout.parallelBefore] -= (placeholderBox[shadowLayout.parallelPrefix] - 1);
958 compEl.setStyle('visibility', 'hidden');
959 compEl.setLeftTop(toCompBox.left, toCompBox.top);
961 // Equalize the size of the expanding Component prior to animation
962 // in case the layout area has changed size during the time it was collapsed.
963 curSize = comp.getSize();
964 if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
965 me.setItemSize(comp, toCompBox.width, toCompBox.height);
968 // This animation slides the collapsed Component's el out to just beyond its placeholder
971 afteranimate: function() {
972 shadowContainer.suspendLayout = scsl;
973 delete me.layoutBusy;
974 delete me.owner.componentLayout.layoutBusy;
976 // Prime the Component with an Anim config object to slide it back out
977 compAnim.listeners = {
978 afterAnimate: function() {
979 compEl.show().removeCls(Ext.baseCSSPrefix + 'border-region-slide-in').setLeftTop(-10000, -10000);
981 // Reinstate the correct, current state after slide out animation finishes
983 comp.collapsed = true;
984 delete comp.slideOutAnim;
985 delete comp.panelMouseMon;
986 delete comp.placeholderMouseMon;
989 comp.slideOutAnim = compAnim;
995 // Give the element the correct class which places it at a high z-index
996 compEl.addCls(Ext.baseCSSPrefix + 'border-region-slide-in');
998 // Begin the slide in
999 compEl.slideIn(me.slideDirection[comp.region], compAnim);
1001 // Monitor for mouseouting of the slid area. Hide it if they exit for half a second or more
1002 comp.panelMouseMon = compEl.monitorMouseLeave(500, onMouseLeaveFloated);
1006 slideOutFloatedComponent: function(comp) {
1007 var compEl = comp.el,
1010 // Remove mouse leave monitors
1011 compEl.un(comp.panelMouseMon);
1012 comp.placeholder.el.un(comp.placeholderMouseMon);
1014 // Slide the Component out
1015 compEl.slideOut(this.slideDirection[comp.region], comp.slideOutAnim);
1017 delete comp.slideOutAnim;
1018 delete comp.panelMouseMon;
1019 delete comp.placeholderMouseMon;
1024 * Ensure any collapsed placeholder Component is destroyed along with its region.
1025 * Can't do this in onDestroy because they may remove a Component and use it elsewhere.
1027 onRegionDestroy: function(comp) {
1028 var placeholder = comp.placeholder;
1030 delete placeholder.ownerCt;
1031 placeholder.destroy();
1037 * Ensure any shadow Containers are destroyed.
1038 * Ensure we don't keep references to Components.
1040 onDestroy: function() {
1042 shadowContainer = me.shadowContainer,
1043 embeddedContainer = me.embeddedContainer;
1045 if (shadowContainer) {
1046 delete shadowContainer.ownerCt;
1047 Ext.destroy(shadowContainer);
1050 if (embeddedContainer) {
1051 delete embeddedContainer.ownerCt;
1052 Ext.destroy(embeddedContainer);
1055 delete me.splitters;
1056 delete me.shadowContainer;
1057 delete me.embeddedContainer;
1058 me.callParent(arguments);