]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/resizer/Splitter.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / resizer / Splitter.js
CommitLineData
6527f429
DM
1/**\r
2 * This class functions **between siblings of a {@link Ext.layout.container.VBox VBox} or {@link Ext.layout.container.HBox HBox}\r
3 * layout** to resize both immediate siblings.\r
4 *\r
5 * A Splitter will preserve the flex ratio of any flexed siblings it is required to resize. It does this by setting the `flex` property of *all* flexed siblings\r
6 * to equal their pixel size. The actual numerical `flex` property in the Components will change, but the **ratio** to the total flex value will be preserved.\r
7 *\r
8 * A Splitter may be configured to show a centered mini-collapse tool orientated to collapse the {@link #collapseTarget}.\r
9 * The Splitter will then call that sibling Panel's {@link Ext.panel.Panel#method-collapse collapse} or {@link Ext.panel.Panel#method-expand expand} method\r
10 * to perform the appropriate operation (depending on the sibling collapse state). To create the mini-collapse tool but take care\r
11 * of collapsing yourself, configure the splitter with `{@link #performCollapse}: false`.\r
12 */\r
13Ext.define('Ext.resizer.Splitter', {\r
14 extend: 'Ext.Component',\r
15 requires: ['Ext.XTemplate'],\r
16 uses: ['Ext.resizer.SplitterTracker'],\r
17 xtype: 'splitter',\r
18\r
19 childEls: [\r
20 'collapseEl'\r
21 ],\r
22\r
23 renderTpl: [\r
24 '<tpl if="collapsible===true">',\r
25 '<div id="{id}-collapseEl" data-ref="collapseEl" role="presentation" class="', Ext.baseCSSPrefix, 'collapse-el ',\r
26 Ext.baseCSSPrefix, 'layout-split-{collapseDir}{childElCls}">',\r
27 '</div>',\r
28 '</tpl>'\r
29 ],\r
30\r
31 isSplitter: true,\r
32\r
33 baseCls: Ext.baseCSSPrefix + 'splitter',\r
34 collapsedClsInternal: Ext.baseCSSPrefix + 'splitter-collapsed',\r
35 \r
36 // Default to tree, allow internal classes to disable resizing\r
37 canResize: true,\r
38\r
39 /**\r
40 * @cfg {Boolean} [collapsible]\r
41 * True to show a mini-collapse tool in the Splitter to toggle expand and collapse on the {@link #collapseTarget} Panel.\r
42 * Defaults to the {@link Ext.panel.Panel#collapsible collapsible} setting of the Panel.\r
43 */\r
44 collapsible: null,\r
45\r
46 /**\r
47 * @cfg {Boolean} performCollapse\r
48 * Set to false to prevent this Splitter's mini-collapse tool from managing the collapse\r
49 * state of the {@link #collapseTarget}.\r
50 */\r
51\r
52 /**\r
53 * @cfg {Boolean} collapseOnDblClick\r
54 * True to enable dblclick to toggle expand and collapse on the {@link #collapseTarget} Panel.\r
55 */\r
56 collapseOnDblClick: true,\r
57\r
58 /**\r
59 * @cfg {Number} defaultSplitMin\r
60 * Provides a default minimum width or height for the two components\r
61 * that the splitter is between.\r
62 */\r
63 defaultSplitMin: 40,\r
64\r
65 /**\r
66 * @cfg {Number} defaultSplitMax\r
67 * Provides a default maximum width or height for the two components\r
68 * that the splitter is between.\r
69 */\r
70 defaultSplitMax: 1000,\r
71\r
72 /**\r
73 * @cfg {String} collapsedCls\r
74 * A class to add to the splitter when it is collapsed. See {@link #collapsible}.\r
75 */\r
76\r
77 /**\r
78 * @cfg {String/Ext.panel.Panel} collapseTarget\r
79 * A string describing the relative position of the immediate sibling Panel to collapse. May be 'prev' or 'next'.\r
80 *\r
81 * Or the immediate sibling Panel to collapse.\r
82 *\r
83 * The orientation of the mini-collapse tool will be inferred from this setting.\r
84 *\r
85 * **Note that only Panels may be collapsed.**\r
86 */\r
87 collapseTarget: 'next',\r
88\r
89 /**\r
90 * @property {String} orientation\r
91 * Orientation of this Splitter. `'vertical'` when used in an hbox layout, `'horizontal'`\r
92 * when used in a vbox layout.\r
93 */\r
94\r
95 horizontal: false,\r
96 vertical: false,\r
97\r
98 /**\r
99 * @cfg {Number} size\r
100 * The size of the splitter. This becomes the height for vertical splitters and \r
101 * width for horizontal splitters.\r
102 */\r
103 size: 5,\r
104 \r
105 /**\r
106 * @cfg {Object} [tracker]\r
107 * Any configuration options to be passed to the underlying {@link Ext.resizer.SplitterTracker}.\r
108 */\r
109 tracker: null,\r
110 \r
111 ariaRole: 'separator',\r
112 \r
113 focusable: true,\r
114 \r
115 tabIndex: 0,\r
116\r
117 /**\r
118 * Returns the config object (with an `xclass` property) for the splitter tracker. This\r
119 * is overridden by {@link Ext.resizer.BorderSplitter BorderSplitter} to create a\r
120 * {@link Ext.resizer.BorderSplitterTracker BorderSplitterTracker}.\r
121 * @protected\r
122 */\r
123 getTrackerConfig: function () {\r
124 return Ext.apply({\r
125 xclass: 'Ext.resizer.SplitterTracker',\r
126 el: this.el,\r
127 splitter: this\r
128 }, this.tracker);\r
129 },\r
130\r
131 beforeRender: function() {\r
132 var me = this,\r
133 target = me.getCollapseTarget(),\r
134 collapsible = me.collapsible,\r
135 ariaAttr;\r
136\r
137 me.callParent();\r
138\r
139 if (target.collapsed) {\r
140 me.addCls(me.collapsedClsInternal);\r
141 }\r
142 if (!me.canResize) {\r
143 me.addCls(me.baseCls + '-noresize');\r
144 }\r
145\r
146 Ext.applyIf(me.renderData, {\r
147 collapseDir: me.getCollapseDirection(),\r
148 collapsible: (collapsible !== null) ? collapsible : target.collapsible\r
149 });\r
150 \r
151 me.ariaRenderAttributes = me.ariaRenderAttributes || {};\r
152 \r
153 // Calling getCollapseDirection() above will set the orientation property\r
154 me.ariaRenderAttributes['aria-orientation'] = me.orientation;\r
155\r
156 me.protoEl.unselectable();\r
157 },\r
158\r
159 onRender: function() {\r
160 var me = this,\r
161 collapseEl;\r
162\r
163 me.callParent(arguments);\r
164\r
165 // Add listeners on the mini-collapse tool unless performCollapse is set to false\r
166 if (me.performCollapse !== false) {\r
167 if (me.renderData.collapsible) {\r
168 me.mon(me.collapseEl, 'click', me.toggleTargetCmp, me);\r
169 }\r
170 if (me.collapseOnDblClick) {\r
171 me.mon(me.el, 'dblclick', me.toggleTargetCmp, me);\r
172 }\r
173 }\r
174\r
175 // Ensure the mini collapse icon is set to the correct direction when the target is collapsed/expanded by any means\r
176 me.getCollapseTarget().on({\r
177 collapse: me.onTargetCollapse,\r
178 expand: me.onTargetExpand,\r
179 beforeexpand: me.onBeforeTargetExpand,\r
180 beforecollapse: me.onBeforeTargetCollapse,\r
181 scope: me\r
182 });\r
183\r
184 if (me.canResize) {\r
185 me.tracker = Ext.create(me.getTrackerConfig());\r
186 // Relay the most important events to our owner (could open wider later):\r
187 me.relayEvents(me.tracker, [ 'beforedragstart', 'dragstart', 'dragend' ]);\r
188 }\r
189\r
190 collapseEl = me.collapseEl;\r
191 if (collapseEl) {\r
192 collapseEl.lastCollapseDirCls = me.collapseDirProps[me.collapseDirection].cls;\r
193 }\r
194 },\r
195\r
196 getCollapseDirection: function() {\r
197 var me = this,\r
198 dir = me.collapseDirection,\r
199 collapseTarget, idx, items, type;\r
200\r
201 if (!dir) {\r
202 collapseTarget = me.collapseTarget;\r
203 if (collapseTarget.isComponent) {\r
204 dir = collapseTarget.collapseDirection;\r
205 }\r
206\r
207 if (!dir) {\r
208 // Avoid duplication of string tests.\r
209 // Create a two bit truth table of the configuration of the Splitter:\r
210 // Collapse Target | orientation\r
211 // 0 0 = next, horizontal\r
212 // 0 1 = next, vertical\r
213 // 1 0 = prev, horizontal\r
214 // 1 1 = prev, vertical\r
215 type = me.ownerCt.layout.type;\r
216 if (collapseTarget.isComponent) {\r
217 items = me.ownerCt.items;\r
218 idx = Number(items.indexOf(collapseTarget) === items.indexOf(me) - 1) << 1 | Number(type === 'hbox');\r
219 } else {\r
220 idx = Number(me.collapseTarget === 'prev') << 1 | Number(type === 'hbox');\r
221 }\r
222\r
223 // Read the data out the truth table\r
224 dir = ['bottom', 'right', 'top', 'left'][idx];\r
225 }\r
226\r
227 me.collapseDirection = dir;\r
228 }\r
229\r
230 me.setOrientation((dir === 'top' || dir === 'bottom') ? 'horizontal' : 'vertical');\r
231\r
232 return dir;\r
233 },\r
234\r
235 getCollapseTarget: function() {\r
236 var me = this;\r
237\r
238 return me.collapseTarget.isComponent ? me.collapseTarget\r
239 : me.collapseTarget === 'prev' ? me.previousSibling() : me.nextSibling();\r
240 },\r
241 \r
242 setCollapseEl: function(display){\r
243 var el = this.collapseEl;\r
244 if (el) {\r
245 el.setDisplayed(display);\r
246 }\r
247 },\r
248 \r
249 onBeforeTargetExpand: function(target) {\r
250 this.setCollapseEl('none');\r
251 },\r
252 \r
253 onBeforeTargetCollapse: function(){\r
254 this.setCollapseEl('none');\r
255 },\r
256\r
257 onTargetCollapse: function(target) {\r
258 var me = this;\r
259\r
260 // Only add the collapsed class if the collapse was from our target (not bubbled from below as in a Dashboard Column)\r
261 // and was in the dimension which this Splitter controls.\r
262 if (target === me.getCollapseTarget() && target[me.orientation === 'vertical' ? 'collapsedHorizontal' : 'collapsedVertical']()) {\r
263 me.el.addCls(me.collapsedClsInternal + ' ' + (me.collapsedCls || ''));\r
264 }\r
265 me.setCollapseEl('');\r
266 },\r
267\r
268 onTargetExpand: function(target) {\r
269 var me = this;\r
270 \r
271 me.el.removeCls(me.collapsedClsInternal + ' ' + (me.collapsedCls || ''));\r
272 me.setCollapseEl('');\r
273 },\r
274\r
275 collapseDirProps: {\r
276 top: {\r
277 cls: Ext.baseCSSPrefix + 'layout-split-top'\r
278 },\r
279 right: {\r
280 cls: Ext.baseCSSPrefix + 'layout-split-right'\r
281 },\r
282 bottom: {\r
283 cls: Ext.baseCSSPrefix + 'layout-split-bottom'\r
284 },\r
285 left: {\r
286 cls: Ext.baseCSSPrefix + 'layout-split-left'\r
287 }\r
288 },\r
289\r
290 orientationProps: {\r
291 horizontal: {\r
292 opposite: 'vertical',\r
293 fixedAxis: 'height',\r
294 stretchedAxis: 'width'\r
295 },\r
296 vertical: {\r
297 opposite: 'horizontal',\r
298 fixedAxis: 'width',\r
299 stretchedAxis: 'height'\r
300 }\r
301 },\r
302\r
303 applyCollapseDirection: function () {\r
304 var me = this,\r
305 collapseEl = me.collapseEl,\r
306 collapseDirProps = me.collapseDirProps[me.collapseDirection],\r
307 cls;\r
308\r
309 if (collapseEl) {\r
310 cls = collapseEl.lastCollapseDirCls;\r
311 if (cls) {\r
312 collapseEl.removeCls(cls);\r
313 }\r
314\r
315 collapseEl.addCls(collapseEl.lastCollapseDirCls = collapseDirProps.cls);\r
316 }\r
317 },\r
318\r
319 applyOrientation: function () {\r
320 var me = this,\r
321 orientation = me.orientation,\r
322 orientationProps = me.orientationProps[orientation],\r
323 defaultSize = me.size,\r
324 fixedSizeProp = orientationProps.fixedAxis,\r
325 stretchSizeProp = orientationProps.stretchedAxis,\r
326 cls = me.baseCls + '-';\r
327\r
328 me[orientation] = true;\r
329 me[orientationProps.opposite] = false;\r
330\r
331 if (!me.hasOwnProperty(fixedSizeProp) || me[fixedSizeProp] === '100%') {\r
332 me[fixedSizeProp] = defaultSize;\r
333 }\r
334 if (!me.hasOwnProperty(stretchSizeProp) || me[stretchSizeProp] === defaultSize) {\r
335 me[stretchSizeProp] = '100%';\r
336 }\r
337\r
338 me.removeCls(cls + orientationProps.opposite);\r
339 me.addCls(cls + orientation);\r
340 },\r
341\r
342 setOrientation: function (orientation) {\r
343 var me = this;\r
344\r
345 if (me.orientation !== orientation) {\r
346 me.orientation = orientation;\r
347 me.applyOrientation();\r
348 }\r
349 },\r
350 \r
351 updateOrientation: function () {\r
352 delete this.collapseDirection; // recompute\r
353 this.getCollapseDirection();\r
354 this.applyCollapseDirection();\r
355 },\r
356\r
357 toggleTargetCmp: function(e, t) {\r
358 var cmp = this.getCollapseTarget(),\r
359 placeholder = cmp.placeholder,\r
360 toggle;\r
361\r
362 // We can only toggle the target if it offers the expand/collapse API\r
363 if (Ext.isFunction(cmp.expand) && Ext.isFunction(cmp.collapse)) {\r
364 if (placeholder && !placeholder.hidden) {\r
365 toggle = true;\r
366 } else {\r
367 toggle = !cmp.hidden;\r
368 }\r
369\r
370 if (toggle) {\r
371 if (cmp.collapsed) {\r
372 cmp.expand();\r
373 } else if (cmp.collapseDirection) {\r
374 cmp.collapse();\r
375 } else {\r
376 cmp.collapse(this.renderData.collapseDir);\r
377 }\r
378 }\r
379 }\r
380 },\r
381\r
382 /*\r
383 * Work around IE bug. %age margins do not get recalculated on element resize unless repaint called.\r
384 */\r
385 setSize: function() {\r
386 var me = this;\r
387 me.callParent(arguments);\r
388 if (Ext.isIE && me.el) {\r
389 me.el.repaint();\r
390 }\r
391 },\r
392 \r
393 beforeDestroy: function(){\r
394 Ext.destroy(this.tracker);\r
395 this.callParent();\r
396 }\r
397});\r