/** * This class functions **between siblings of a {@link Ext.layout.container.VBox VBox} or {@link Ext.layout.container.HBox HBox} * layout** to resize both immediate siblings. * * 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 * 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. * * A Splitter may be configured to show a centered mini-collapse tool orientated to collapse the {@link #collapseTarget}. * 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 * to perform the appropriate operation (depending on the sibling collapse state). To create the mini-collapse tool but take care * of collapsing yourself, configure the splitter with `{@link #performCollapse}: false`. */Ext.define('Ext.resizer.Splitter', { extend: 'Ext.Component', requires: ['Ext.XTemplate'], uses: ['Ext.resizer.SplitterTracker'], xtype: 'splitter', childEls: [ 'collapseEl' ], renderTpl: [ '<tpl if="collapsible===true">', '<div id="{id}-collapseEl" data-ref="collapseEl" role="presentation" class="', Ext.baseCSSPrefix, 'collapse-el ', Ext.baseCSSPrefix, 'layout-split-{collapseDir}{childElCls}"> ', '</div>', '</tpl>' ], isSplitter: true, baseCls: Ext.baseCSSPrefix + 'splitter', collapsedClsInternal: Ext.baseCSSPrefix + 'splitter-collapsed', // Default to tree, allow internal classes to disable resizing canResize: true, /** * @cfg {Boolean} [collapsible] * True to show a mini-collapse tool in the Splitter to toggle expand and collapse on the {@link #collapseTarget} Panel. * Defaults to the {@link Ext.panel.Panel#collapsible collapsible} setting of the Panel. */ collapsible: null, /** * @cfg {Boolean} performCollapse * Set to false to prevent this Splitter's mini-collapse tool from managing the collapse * state of the {@link #collapseTarget}. */ /** * @cfg {Boolean} collapseOnDblClick * True to enable dblclick to toggle expand and collapse on the {@link #collapseTarget} Panel. */ collapseOnDblClick: true, /** * @cfg {Number} defaultSplitMin * Provides a default minimum width or height for the two components * that the splitter is between. */ defaultSplitMin: 40, /** * @cfg {Number} defaultSplitMax * Provides a default maximum width or height for the two components * that the splitter is between. */ defaultSplitMax: 1000, /** * @cfg {String} collapsedCls * A class to add to the splitter when it is collapsed. See {@link #collapsible}. */ /** * @cfg {String/Ext.panel.Panel} collapseTarget * A string describing the relative position of the immediate sibling Panel to collapse. May be 'prev' or 'next'. * * Or the immediate sibling Panel to collapse. * * The orientation of the mini-collapse tool will be inferred from this setting. * * **Note that only Panels may be collapsed.** */ collapseTarget: 'next', /** * @property {String} orientation * Orientation of this Splitter. `'vertical'` when used in an hbox layout, `'horizontal'` * when used in a vbox layout. */ horizontal: false, vertical: false, /** * @cfg {Number} size * The size of the splitter. This becomes the height for vertical splitters and * width for horizontal splitters. */ size: 5, /** * @cfg {Object} [tracker] * Any configuration options to be passed to the underlying {@link Ext.resizer.SplitterTracker}. */ tracker: null, ariaRole: 'separator', focusable: true, tabIndex: 0, /** * Returns the config object (with an `xclass` property) for the splitter tracker. This * is overridden by {@link Ext.resizer.BorderSplitter BorderSplitter} to create a * {@link Ext.resizer.BorderSplitterTracker BorderSplitterTracker}. * @protected */ getTrackerConfig: function () { return Ext.apply({ xclass: 'Ext.resizer.SplitterTracker', el: this.el, splitter: this }, this.tracker); }, beforeRender: function() { var me = this, target = me.getCollapseTarget(), collapsible = me.collapsible; me.callParent(); if (target.collapsed) { me.addCls(me.collapsedClsInternal); } if (!me.canResize) { me.addCls(me.baseCls + '-noresize'); } Ext.applyIf(me.renderData, { collapseDir: me.getCollapseDirection(), collapsible: (collapsible !== null) ? collapsible : target.collapsible }); me.protoEl.unselectable(); }, onRender: function() { var me = this, target, collapseEl; me.callParent(arguments); // Add listeners on the mini-collapse tool unless performCollapse is set to false if (me.performCollapse !== false) { if (me.renderData.collapsible) { me.mon(me.collapseEl, 'click', me.toggleTargetCmp, me); } if (me.collapseOnDblClick) { me.mon(me.el, 'dblclick', me.toggleTargetCmp, me); } } // Ensure the mini collapse icon is set to the correct direction // when the target is collapsed/expanded by any means. // Make sure we're only listening to collapse/expand events on Panels! target = me.getCollapseTarget(); if (target && target.isPanel) { target.on({ collapse: me.onTargetCollapse, expand: me.onTargetExpand, beforeexpand: me.onBeforeTargetExpand, beforecollapse: me.onBeforeTargetCollapse, scope: me }); } if (me.canResize) { me.tracker = Ext.create(me.getTrackerConfig()); // Relay the most important events to our owner (could open wider later): me.relayEvents(me.tracker, [ 'beforedragstart', 'dragstart', 'dragend' ]); } collapseEl = me.collapseEl; if (collapseEl) { collapseEl.lastCollapseDirCls = me.collapseDirProps[me.collapseDirection].cls; } }, getCollapseDirection: function() { var me = this, dir = me.collapseDirection, collapseTarget, idx, items, type; if (!dir) { collapseTarget = me.collapseTarget; if (collapseTarget.isComponent) { dir = collapseTarget.collapseDirection; } if (!dir) { // Avoid duplication of string tests. // Create a two bit truth table of the configuration of the Splitter: // Collapse Target | orientation // 0 0 = next, horizontal // 0 1 = next, vertical // 1 0 = prev, horizontal // 1 1 = prev, vertical type = me.ownerCt.layout.type; if (collapseTarget.isComponent) { items = me.ownerCt.items; idx = Number(items.indexOf(collapseTarget) === items.indexOf(me) - 1) << 1 | Number(type === 'hbox'); } else { idx = Number(me.collapseTarget === 'prev') << 1 | Number(type === 'hbox'); } // Read the data out the truth table dir = ['bottom', 'right', 'top', 'left'][idx]; } me.collapseDirection = dir; } me.setOrientation((dir === 'top' || dir === 'bottom') ? 'horizontal' : 'vertical'); return dir; }, getCollapseTarget: function() { var me = this; return me.collapseTarget.isComponent ? me.collapseTarget : me.collapseTarget === 'prev' ? me.previousSibling() : me.nextSibling(); }, setCollapseEl: function(display){ var el = this.collapseEl; if (el) { el.setDisplayed(display); } }, onBeforeTargetExpand: function(target) { this.setCollapseEl('none'); }, onBeforeTargetCollapse: function(){ this.setCollapseEl('none'); }, onTargetCollapse: function(target) { var me = this; // Only add the collapsed class if the collapse was from our target (not bubbled from below as in a Dashboard Column) // and was in the dimension which this Splitter controls. if (target === me.getCollapseTarget() && target[me.orientation === 'vertical' ? 'collapsedHorizontal' : 'collapsedVertical']()) { me.el.addCls(me.collapsedClsInternal + ' ' + (me.collapsedCls || '')); } me.setCollapseEl(''); }, onTargetExpand: function(target) { var me = this; me.el.removeCls(me.collapsedClsInternal + ' ' + (me.collapsedCls || '')); me.setCollapseEl(''); }, collapseDirProps: { top: { cls: Ext.baseCSSPrefix + 'layout-split-top' }, right: { cls: Ext.baseCSSPrefix + 'layout-split-right' }, bottom: { cls: Ext.baseCSSPrefix + 'layout-split-bottom' }, left: { cls: Ext.baseCSSPrefix + 'layout-split-left' } }, orientationProps: { horizontal: { opposite: 'vertical', fixedAxis: 'height', stretchedAxis: 'width' }, vertical: { opposite: 'horizontal', fixedAxis: 'width', stretchedAxis: 'height' } }, applyCollapseDirection: function () { var me = this, collapseEl = me.collapseEl, collapseDirProps = me.collapseDirProps[me.collapseDirection], cls; if (collapseEl) { cls = collapseEl.lastCollapseDirCls; if (cls) { collapseEl.removeCls(cls); } collapseEl.addCls(collapseEl.lastCollapseDirCls = collapseDirProps.cls); } }, applyOrientation: function () { var me = this, orientation = me.orientation, orientationProps = me.orientationProps[orientation], defaultSize = me.size, fixedSizeProp = orientationProps.fixedAxis, stretchSizeProp = orientationProps.stretchedAxis, cls = me.baseCls + '-'; me[orientation] = true; me[orientationProps.opposite] = false; if (!me.hasOwnProperty(fixedSizeProp) || me[fixedSizeProp] === '100%') { me[fixedSizeProp] = defaultSize; } if (!me.hasOwnProperty(stretchSizeProp) || me[stretchSizeProp] === defaultSize) { me[stretchSizeProp] = '100%'; } me.removeCls(cls + orientationProps.opposite); me.addCls(cls + orientation); }, setOrientation: function (orientation) { var me = this; if (me.orientation !== orientation) { me.orientation = orientation; me.applyOrientation(); } }, updateOrientation: function () { delete this.collapseDirection; // recompute this.getCollapseDirection(); this.applyCollapseDirection(); }, toggleTargetCmp: function(e, t) { var cmp = this.getCollapseTarget(), placeholder = cmp.placeholder, toggle; // We can only toggle the target if it offers the expand/collapse API if (Ext.isFunction(cmp.expand) && Ext.isFunction(cmp.collapse)) { if (placeholder && !placeholder.hidden) { toggle = true; } else { toggle = !cmp.hidden; } if (toggle) { if (cmp.collapsed) { cmp.expand(); } else if (cmp.collapseDirection) { cmp.collapse(); } else { cmp.collapse(this.renderData.collapseDir); } } } }, /* * Work around IE bug. %age margins do not get recalculated on element resize unless repaint called. */ setSize: function() { var me = this; me.callParent(arguments); if (Ext.isIE && me.el) { me.el.repaint(); } }, beforeDestroy: function(){ Ext.destroy(this.tracker); this.callParent(); }});