Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / resizer / Splitter.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
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.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.resizer.Splitter
17  * @extends Ext.Component
18  * <p>This class functions <b>between siblings of a {@link Ext.layout.container.VBox VBox} or {@link Ext.layout.container.HBox HBox}
19  * layout</b> to resize both immediate siblings.</p>
20  * <p>By default it will set the size of both siblings. <b>One</b> of the siblings may be configured with
21  * <code>{@link Ext.Component#maintainFlex maintainFlex}: true</code> which will cause it not to receive a new size explicitly, but to be resized
22  * by the layout.</p>
23  * <p>A Splitter may be configured to show a centered mini-collapse tool orientated to collapse the {@link #collapseTarget}.
24  * The Splitter will then call that sibling Panel's {@link Ext.panel.Panel#collapse collapse} or {@link Ext.panel.Panel#expand expand} method
25  * to perform the appropriate operation (depending on the sibling collapse state). To create the mini-collapse tool but take care
26  * of collapsing yourself, configure the splitter with <code>{@link #performCollapse} false</code>.</p>
27  */
28 Ext.define('Ext.resizer.Splitter', {
29     extend: 'Ext.Component',
30     requires: ['Ext.XTemplate'],
31     uses: ['Ext.resizer.SplitterTracker'],
32     alias: 'widget.splitter',
33
34     renderTpl: [
35         '<tpl if="collapsible===true"><div class="' + Ext.baseCSSPrefix + 'collapse-el ' + Ext.baseCSSPrefix + 'layout-split-{collapseDir}">&nbsp;</div></tpl>'
36     ],
37
38     baseCls: Ext.baseCSSPrefix + 'splitter',
39     collapsedClsInternal: Ext.baseCSSPrefix + 'splitter-collapsed',
40
41     /**
42      * @cfg {Boolean} collapsible
43      * <code>true</code> to show a mini-collapse tool in the Splitter to toggle expand and collapse on the {@link #collapseTarget} Panel.
44      * Defaults to the {@link Ext.panel.Panel#collapsible collapsible} setting of the Panel.
45      */
46     collapsible: false,
47
48     /**
49      * @cfg {Boolean} performCollapse
50      * <p>Set to <code>false</code> to prevent this Splitter's mini-collapse tool from managing the collapse
51      * state of the {@link #collapseTarget}.</p>
52      */
53
54     /**
55      * @cfg {Boolean} collapseOnDblClick
56      * <code>true</code> to enable dblclick to toggle expand and collapse on the {@link #collapseTarget} Panel.
57      */
58     collapseOnDblClick: true,
59
60     /**
61      * @cfg {Number} defaultSplitMin
62      * Provides a default minimum width or height for the two components
63      * that the splitter is between.
64      */
65     defaultSplitMin: 40,
66
67     /**
68      * @cfg {Number} defaultSplitMax
69      * Provides a default maximum width or height for the two components
70      * that the splitter is between.
71      */
72     defaultSplitMax: 1000,
73     
74     /**
75      * @cfg {String} collapsedCls
76      * A class to add to the splitter when it is collapsed. See {@link #collapsible}.
77      */
78
79     width: 5,
80     height: 5,
81
82     /**
83      * @cfg {Mixed} collapseTarget
84      * <p>A string describing the relative position of the immediate sibling Panel to collapse. May be 'prev' or 'next' (Defaults to 'next')</p>
85      * <p>Or the immediate sibling Panel to collapse.</p>
86      * <p>The orientation of the mini-collapse tool will be inferred from this setting.</p>
87      * <p><b>Note that only Panels may be collapsed.</b></p>
88      */
89     collapseTarget: 'next',
90
91     /**
92      * @property orientation
93      * @type String
94      * Orientation of this Splitter. <code>'vertical'</code> when used in an hbox layout, <code>'horizontal'</code>
95      * when used in a vbox layout.
96      */
97
98     onRender: function() {
99         var me = this,
100             target = me.getCollapseTarget(),
101             collapseDir = me.getCollapseDirection();
102
103         Ext.applyIf(me.renderData, {
104             collapseDir: collapseDir,
105             collapsible: me.collapsible || target.collapsible
106         });
107         Ext.applyIf(me.renderSelectors, {
108             collapseEl: '.' + Ext.baseCSSPrefix + 'collapse-el'
109         });
110
111         this.callParent(arguments);
112
113         // Add listeners on the mini-collapse tool unless performCollapse is set to false
114         if (me.performCollapse !== false) {
115             if (me.renderData.collapsible) {
116                 me.mon(me.collapseEl, 'click', me.toggleTargetCmp, me);
117             }
118             if (me.collapseOnDblClick) {
119                 me.mon(me.el, 'dblclick', me.toggleTargetCmp, me);
120             }
121         }
122
123         // Ensure the mini collapse icon is set to the correct direction when the target is collapsed/expanded by any means
124         me.mon(target, 'collapse', me.onTargetCollapse, me);
125         me.mon(target, 'expand', me.onTargetExpand, me);
126
127         me.el.addCls(me.baseCls + '-' + me.orientation);
128         me.el.unselectable();
129
130         me.tracker = Ext.create('Ext.resizer.SplitterTracker', {
131             el: me.el
132         });
133
134         // Relay the most important events to our owner (could open wider later):
135         me.relayEvents(me.tracker, [ 'beforedragstart', 'dragstart', 'dragend' ]);
136     },
137
138     getCollapseDirection: function() {
139         var me = this,
140             idx,
141             type = me.ownerCt.layout.type;
142
143         // Avoid duplication of string tests.
144         // Create a two bit truth table of the configuration of the Splitter:
145         // Collapse Target | orientation
146         //        0              0             = next, horizontal
147         //        0              1             = next, vertical
148         //        1              0             = prev, horizontal
149         //        1              1             = prev, vertical
150         if (me.collapseTarget.isComponent) {
151             idx = Number(me.ownerCt.items.indexOf(me.collapseTarget) == me.ownerCt.items.indexOf(me) - 1) << 1 | Number(type == 'hbox');
152         } else {
153             idx = Number(me.collapseTarget == 'prev') << 1 | Number(type == 'hbox');
154         }
155
156         // Read the data out the truth table
157         me.orientation = ['horizontal', 'vertical'][idx & 1];
158         return ['bottom', 'right', 'top', 'left'][idx];
159     },
160
161     getCollapseTarget: function() {
162         var me = this;
163         
164         return me.collapseTarget.isComponent ? me.collapseTarget : me.collapseTarget == 'prev' ? me.previousSibling() : me.nextSibling();
165     },
166
167     onTargetCollapse: function(target) {
168         this.el.addCls([this.collapsedClsInternal, this.collapsedCls]);
169     },
170
171     onTargetExpand: function(target) {
172         this.el.removeCls([this.collapsedClsInternal, this.collapsedCls]);
173     },
174
175     toggleTargetCmp: function(e, t) {
176         var cmp = this.getCollapseTarget();
177
178         if (cmp.isVisible()) {
179             // restore
180             if (cmp.collapsed) {
181                 cmp.expand(cmp.animCollapse);
182             // collapse
183             } else {
184                 cmp.collapse(this.renderData.collapseDir, cmp.animCollapse);
185             }
186         }
187     },
188
189     /*
190      * Work around IE bug. %age margins do not get recalculated on element resize unless repaint called.
191      */
192     setSize: function() {
193         var me = this;
194         me.callParent(arguments);
195         if (Ext.isIE) {
196             me.el.repaint();
197         }
198     }
199 });
200