/** * This class implements the configurator panel. */Ext.define('Ext.pivot.plugin.configurator.Panel', { extend: 'Ext.panel.Panel', requires: [ 'Ext.pivot.plugin.configurator.Container', 'Ext.pivot.plugin.configurator.DragZone', 'Ext.pivot.plugin.configurator.DropZone', 'Ext.layout.container.HBox', 'Ext.layout.container.VBox' ], mixins: [ 'Ext.util.FocusableContainer' ], alias: 'widget.pivotconfigpanel', weight: 50, // the column header container has a weight of 100 so we want to dock it before that. defaultMinHeight: 70, defaultMinWidth: 250, dock: 'right', header: false, title: 'Configurator', collapsible: true, collapseMode: 'placeholder', /** * @cfg {String} panelAllFieldsText Text displayed in the container reserved for all available fields * when docked to top or bottom. */ panelAllFieldsText: 'Drop Unused Fields Here', /** * @cfg {String} panelAllFieldsTitle Text displayed in the container reserved for all available fields * when docked to left or right. */ panelAllFieldsTitle: 'All fields', /** * @cfg {String} panelTopFieldsText Text displayed in the container reserved for all top axis fields * when docked to top or bottom. */ panelTopFieldsText: 'Drop Column Fields Here', /** * @cfg {String} panelTopFieldsTitle Text displayed in the container reserved for all top axis fields * when docked to left or right. */ panelTopFieldsTitle: 'Column labels', /** * @cfg {String} panelLeftFieldsText Text displayed in the container reserved for all left axis fields * when docked to top or bottom. */ panelLeftFieldsText: 'Drop Row Fields Here', /** * @cfg {String} panelLeftFieldsTitle Text displayed in the container reserved for all left axis fields * when docked to left or right. */ panelLeftFieldsTitle: 'Row labels', /** * @cfg {String} panelAggFieldsText Text displayed in the container reserved for all aggregate fields * when docked to top or bottom. */ panelAggFieldsText: 'Drop Agg Fields Here', /** * @cfg {String} panelAggFieldsTitle Text displayed in the container reserved for all aggregate fields * when docked to left or right. */ panelAggFieldsTitle: 'Values', /** * @cfg {String} addToText Text displayed in the field menu */ addToText: 'Add to {0}', /** * @cfg {String} moveToText Text displayed in the field menu */ moveToText: 'Move to {0}', /** * @cfg {String} removeFieldText Text displayed in the field menu */ removeFieldText: 'Remove field', /** * @cfg {String} moveUpText Text displayed in the field menu */ moveUpText: 'Move up', /** * @cfg {String} moveDownText Text displayed in the field menu */ moveDownText: 'Move down', /** * @cfg {String} moveBeginText Text displayed in the field menu */ moveBeginText: 'Move to beginning', /** * @cfg {String} moveEndText Text displayed in the field menu */ moveEndText: 'Move to end', /** * @cfg {String} fieldSettingsText Text displayed in the field menu */ fieldSettingsText: 'Field settings', headerContainerCls: Ext.baseCSSPrefix + 'pivot-grid-config-container-header', keyEventRe: /^key/, config: { fields: [], refreshDelay: 300, pivot: null }, initComponent: function(){ var me = this, listeners = { configchange: me.applyChanges, scope: me }; Ext.apply(me, Ext.Array.indexOf(['top', 'bottom'], me.dock) >= 0 ? me.getHorizontalConfig() : me.getVerticalConfig()); me.callParent(arguments); me.getAllFieldsContainer().on(listeners); me.getLeftAxisContainer().on(listeners); me.getTopAxisContainer().on(listeners); me.getAggregateContainer().on(listeners); me.pivotListeners = me.getPivot().getMatrix().on({ done: me.onPivotDone, scope: me, destroyable:true }); me.task = new Ext.util.DelayedTask(me.reconfigurePivot, me); }, destroy: function(){ var me = this, toDestroy = [ 'relayers', 'pivotListeners', 'menu', 'dragZone', 'dropZone' ], length = toDestroy.length, i; for(i = 0; i < length; i++){ Ext.destroy(me[toDestroy[i]]); me[toDestroy[i]] = null; } me.task.cancel(); me.task = me.lastFocusedField = null; me.callParent(); }, enable: function(){ var me = this; if(me.rendered){ me.dragZone.enable(); me.dropZone.enable(); me.initPivotFields(); } me.show(); }, disable: function(){ var me = this; if(me.rendered){ me.dragZone.disable(); me.dropZone.disable(); } me.hide(); }, afterRender: function(){ var me = this, el = me.getEl(); me.callParent(arguments); me.mon(el, { scope: me, delegate: '.' + Ext.baseCSSPrefix + 'pivot-grid-config-column', click: me.handleEvent, keypress: me.handleEvent }); me.dragZone = new Ext.pivot.plugin.configurator.DragZone(me); me.dropZone = new Ext.pivot.plugin.configurator.DropZone(me); el.unselectable(); }, handleEvent: function(e){ var me = this, isKeyEvent = me.keyEventRe.test(e.type), pivot = me.getPivot(), fly, cmp, menuCfg, options; if( (isKeyEvent && e.getKey() === e.SPACE) || (e.button === 0) ){ fly = Ext.fly(e.target); if(fly && (cmp = fly.component)){ e.stopEvent(); cmp.focus(); Ext.destroy(me.menu); menuCfg = me.getMenuConfig(cmp); if(menuCfg){ me.menu = new Ext.menu.Menu(menuCfg); options = { menu: me.menu, field: cmp.getField(), container: cmp.getFieldType() }; if(pivot.fireEvent('beforeshowconfigfieldmenu', me, options) !== false) { me.menu.showBy(cmp); me.menu.focus(); pivot.fireEvent('showconfigfieldmenu', me, options); }else{ Ext.destroy(me.menu); } } } } }, getPanelConfigHeader: function(config){ return Ext.apply({ xtype: 'header', // make it look like a panel header but with a different padding baseCls: Ext.baseCSSPrefix + 'panel-header', cls: this.headerContainerCls, border: 1, width: 100 }, config || {}); }, getHorizontalConfig: function(){ var me = this; return { minHeight: me.defaultMinHeight, headerPosition: me.dock == 'top' ? 'bottom' : 'top', collapseDirection: me.dock, defaults: { xtype: 'container', layout: { type: 'hbox', align: 'stretchmax' }, minHeight: me.defaultMinHeight/3 }, items: [{ items: [me.getPanelConfigHeader({ title: me.panelAllFieldsTitle, tools: me.collapsible ? [{ type: me.dock == 'top' ? 'up' : 'down', handler: me.collapseMe, scope: me }] : [] }),{ itemId: 'fieldsCt', xtype: 'pivotconfigcontainer', fieldType: 'all', dragDropText: me.panelAllFieldsText, position: me.dock, flex: 1 }] },{ items: [me.getPanelConfigHeader({ title: me.panelAggFieldsTitle }),{ itemId: 'fieldsAggCt', xtype: 'pivotconfigcontainer', fieldType: 'aggregate', dragDropText: me.panelAggFieldsText, position: me.dock, flex: 1 }] },{ defaults: { xtype: 'pivotconfigcontainer', minHeight: me.defaultMinHeight/3, position: me.dock }, items: [me.getPanelConfigHeader({ title: me.panelLeftFieldsTitle }),{ itemId: 'fieldsLeftCt', fieldType: 'leftAxis', dragDropText: me.panelLeftFieldsText, flex: 1 },me.getPanelConfigHeader({ title: me.panelTopFieldsTitle }),{ itemId: 'fieldsTopCt', fieldType: 'topAxis', dragDropText: me.panelTopFieldsText, flex: 1 }] }] }; }, getVerticalConfig: function(){ var me = this; return { layout: { type: 'hbox', align: 'stretch' }, width: me.defaultMinWidth, minWidth: me.defaultMinWidth, headerPosition: me.dock == 'right' ? 'left' : 'right', collapseDirection: me.dock, defaults: { flex: 1 }, items: [{ itemId: 'fieldsCt', xtype: 'pivotconfigcontainer', position: me.dock, title: me.panelAllFieldsTitle, fieldType: 'all', dragDropText: me.panelAllFieldsText, autoScroll: true, header: { cls: me.headerContainerCls }, tools: me.collapsible ? [{ type: me.dock, handler: me.collapseMe, scope: me }] : [] },{ xtype: 'container', defaults: { xtype: 'pivotconfigcontainer', flex: 1, autoScroll: true, position: me.dock, header: { cls: me.headerContainerCls } }, layout: { type: 'vbox', align: 'stretch' }, items: [{ itemId: 'fieldsAggCt', title: me.panelAggFieldsTitle, fieldType: 'aggregate', dragDropText: me.panelAggFieldsText },{ itemId: 'fieldsLeftCt', title: me.panelLeftFieldsTitle, fieldType: 'leftAxis', dragDropText: me.panelLeftFieldsText },{ itemId: 'fieldsTopCt', title: me.panelTopFieldsTitle, fieldType: 'topAxis', dragDropText: me.panelTopFieldsText }] }] }; }, /** * Returns the container that stores all unused fields. * * @return {Ext.pivot.plugin.configurator.Container} */ getAllFieldsContainer: function(){ return this.down('#fieldsCt'); }, /** * Returns the header of the container that stores all unused fields. * * @return {Ext.panel.Header} */ getAllFieldsHeader: function(){ var dock = this.dock, ct = this.getAllFieldsContainer(); return (dock === 'top' || dock === 'bottom') ? ct.prev() : ct.getHeader(); }, /** * Set visibility of the "All fields" header and container * @param {Boolean} visible */ setAllFieldsContainerVisible: function(visible){ this.getAllFieldsContainer().setVisible(visible); this.getAllFieldsHeader().setVisible(visible); }, /** * Returns the container that stores all fields configured on the left axis. * * @return {Ext.pivot.plugin.configurator.Container} */ getLeftAxisContainer: function(){ return this.down('#fieldsLeftCt'); }, /** * Returns the header of the container that stores all fields configured on the left axis. * * @return {Ext.panel.Header} */ getLeftAxisHeader: function(){ var dock = this.dock, ct = this.getLeftAxisContainer(); return (dock === 'top' || dock === 'bottom') ? ct.prev() : ct.getHeader(); }, /** * Set visibility of the "Row labels" header and container * @param {Boolean} visible */ setLeftAxisContainerVisible: function(visible){ this.getLeftAxisContainer().setVisible(visible); this.getLeftAxisHeader().setVisible(visible); }, /** * Returns the container that stores all fields configured on the top axis. * * @return {Ext.pivot.plugin.configurator.Container} */ getTopAxisContainer: function(){ return this.down('#fieldsTopCt'); }, /** * Returns the header of the container that stores all fields configured on the top axis. * * @return {Ext.panel.Header} */ getTopAxisHeader: function(){ var dock = this.dock, ct = this.getTopAxisContainer(); return (dock === 'top' || dock === 'bottom') ? ct.prev() : ct.getHeader(); }, /** * Set visibility of the "Column labels" header and container * @param {Boolean} visible */ setTopAxisContainerVisible: function(visible){ this.getTopAxisContainer().setVisible(visible); this.getTopAxisHeader().setVisible(visible); }, /** * Returns the container that stores all fields configured on the aggregate. * * @return {Ext.pivot.plugin.configurator.Container} */ getAggregateContainer: function(){ return this.down('#fieldsAggCt'); }, /** * Returns the header of the container that stores all fields configured on the aggregate. * * @return {Ext.panel.Header} */ getAggregateHeader: function(){ var dock = this.dock, ct = this.getAggregateContainer(); return (dock === 'top' || dock === 'bottom') ? ct.prev() : ct.getHeader(); }, /** * Set visibility of the "Values" header and container * @param {Boolean} visible */ setAggregateContainerVisible: function(visible){ this.getAggregateContainer().setVisible(visible); this.getAggregateHeader().setVisible(visible); }, /** * Apply configurator changes to the pivot component. * * This function will trigger the delayed task which is actually reconfiguring the pivot component * with the new configuration. * */ applyChanges: function(field){ var me = this; if(me.disabled) { // if the plugin is disabled don't do anything return; } if(field) { me.lastFocusedField = field; } me.task.delay(me.getRefreshDelay()); }, collapseMe: function (){ this.collapse(this.dock); }, /** * This function is used to retrieve all configured fields in a fields container. * * @private */ getFieldsFromContainer: function(ct, justConfigs){ var fields = [], len = ct.items.getCount(), i, item; for(i = 0; i < len; i++){ item = ct.items.getAt(i); fields.push(justConfigs === true ? item.getField().getConfiguration() : item.getField()); } return fields; }, /** * Initialize all container fields fetching the configuration from the pivot grid. * * @private */ initPivotFields: function(){ var me = this, matrix = me.getPivot().getMatrix(), fieldsAllCt = me.getAllFieldsContainer(), fieldsLeftCt = me.getLeftAxisContainer(), fieldsTopCt = me.getTopAxisContainer(), fieldsAggCt = me.getAggregateContainer(), fieldsTop, fieldsLeft, fieldsAgg, fields; fields = me.getFields().clone(); Ext.suspendLayouts(); // remove all previously created columns fieldsAllCt.removeAll(); fieldsTopCt.removeAll(); fieldsLeftCt.removeAll(); fieldsAggCt.removeAll(); fieldsTop = me.getConfigFields(matrix.topAxis.dimensions.getRange()); fieldsLeft = me.getConfigFields(matrix.leftAxis.dimensions.getRange()); fieldsAgg = me.getConfigFields(matrix.aggregate.getRange()); // the "All fields" will always contain all available fields (both defined on the plugin and existing in the matrix configuration) me.addFieldsToConfigurator(fields.getRange(), fieldsAllCt); me.addFieldsToConfigurator(fieldsTop, fieldsTopCt); me.addFieldsToConfigurator(fieldsLeft, fieldsLeftCt); me.addFieldsToConfigurator(fieldsAgg, fieldsAggCt); Ext.resumeLayouts(true); }, /** * Easy function for assigning fields to a container. * * @private */ addFieldsToConfigurator: function(fields, fieldsCt){ var len = fields.length, i; for(i = 0; i < len; i++){ fieldsCt.addField(fields[i], -1); } }, /** * Build the fields array for each container by parsing all given fields or from the pivot config. * * @private */ getConfigFields: function(items){ var len = items.length, fields = this.getFields(), list = [], i, field, item; for(i = 0; i < len; i++){ item = items[i]; field = fields.byDataIndex.get(item.dataIndex); if(field){ // we need to clone the field that includes all constraints // and apply the configs from the original field field = field.clone(); field.setConfig(item.getInitialConfig()); list.push(field); } } return list; }, getMenuConfig: function(field){ var me = this, fieldType = field.getFieldType(), items = [], menu = field.getMenuConfig() || {}, container = field.up('pivotconfigcontainer'), siblings = container.items.getCount(), fieldIdx = container.items.indexOf(field), dimension = field.getField(), settings = dimension.getSettings(), fieldsLeftCt = me.getLeftAxisContainer(), fieldsTopCt = me.getTopAxisContainer(), fieldsAggCt = me.getAggregateContainer(), titleLeft = me.getLeftAxisHeader().getTitle().getText(), titleTop = me.getTopAxisHeader().getTitle().getText(), titleAgg = me.getAggregateHeader().getTitle().getText(); menu.items = menu.items || []; if(fieldType == 'all'){ items.push({ text: Ext.String.format(me.addToText, titleLeft), disabled: !settings.isAllowed(fieldsLeftCt), handler: Ext.bind(me.dragDropField, me, [fieldsLeftCt, field, 'after']), hidden: fieldsLeftCt.isHidden() },{ text: Ext.String.format(me.addToText, titleTop), disabled: !settings.isAllowed(fieldsTopCt), handler: Ext.bind(me.dragDropField, me, [fieldsTopCt, field, 'after']), hidden: fieldsTopCt.isHidden() },{ text: Ext.String.format(me.addToText, titleAgg), disabled: !settings.isAllowed(fieldsAggCt), handler: Ext.bind(me.dragDropField, me, [fieldsAggCt, field, 'after']), hidden: fieldsAggCt.isHidden() }); }else{ items.push({ text: me.moveUpText, disabled: (siblings == 1 || fieldIdx == 0), handler: Ext.bind(me.dragDropField, me, [field.previousSibling(), field, 'before']) },{ text: me.moveDownText, disabled: (siblings == 1 || fieldIdx == siblings - 1), handler: Ext.bind(me.dragDropField, me, [field.nextSibling(), field, 'after']) },{ text: me.moveBeginText, disabled: (siblings == 1 || fieldIdx == 0), handler: Ext.bind(me.dragDropField, me, [container.items.first(), field, 'before']) },{ text: me.moveEndText, disabled: (siblings == 1 || fieldIdx == siblings - 1), handler: Ext.bind(me.dragDropField, me, [container.items.last(), field, 'after']) },{ xtype: 'menuseparator' },{ text: Ext.String.format(me.moveToText, me.panelLeftFieldsTitle), disabled: (fieldType == 'leftAxis' || !settings.isAllowed(fieldsLeftCt) || settings.isFixed(container)), handler: Ext.bind(me.dragDropField, me, [fieldsLeftCt, field, 'after']) },{ text: Ext.String.format(me.moveToText, me.panelTopFieldsTitle), disabled: (fieldType == 'topAxis' || !settings.isAllowed(fieldsTopCt) || settings.isFixed(container)), handler: Ext.bind(me.dragDropField, me, [fieldsTopCt, field, 'after']) },{ text: Ext.String.format(me.moveToText, me.panelAggFieldsTitle), disabled: (fieldType == 'aggregate' || !settings.isAllowed(fieldsAggCt) || settings.isFixed(container)), handler: Ext.bind(me.dragDropField, me, [fieldsAggCt, field, 'after']) },{ xtype: 'menuseparator' },{ text: me.removeFieldText, disabled: settings.isFixed(container), handler: Ext.bind(me.dragDropField, me, [me.getAllFieldsContainer(), field, 'after']) }); } if(fieldType == 'aggregate'){ items.push({ xtype: 'menuseparator' }, { text: me.fieldSettingsText, handler: Ext.bind(me.openFieldSettings, me, [field]) }); } if(menu.items.length) { items.push({ xtype: 'menuseparator' }); } Ext.Array.insert(menu.items, 0, items); return Ext.apply({ ownerCmp: me, floating: true }, menu); }, openFieldSettings: function(field){ var pivot = this.getPivot(), win = new Ext.pivot.plugin.configurator.window.FieldSettings({ field: field.getField(), listeners: { applysettings: Ext.bind(this.applyFieldSettings, this, [field], 0) } }), settings = field.getField().getConfig(); if(pivot.fireEvent('beforeshowconfigfieldsettings', this, { container: win, settings: settings }) !== false){ win.loadSettings(settings); win.show(); pivot.fireEvent('showconfigfieldsettings', this, { container: win, settings: settings }); }else{ Ext.destroy(win); } }, applyFieldSettings: function(field, win, settings){ var pivot = this.getPivot(), fieldCfg = field.getField(); if(pivot.fireEvent('beforeapplyconfigfieldsettings', this, { container: win, settings: settings }) !== false) { fieldCfg.setConfig(settings || {}); if (field.rendered) { field.textCol.setHtml(fieldCfg.getFieldText()); field.textCol.dom.setAttribute('data-qtip', fieldCfg.getFieldText()); } pivot.fireEvent('applyconfigfieldsettings', this, { container: win, settings: settings }); this.applyChanges(field); }else{ return false; } }, /** * This function either moves or copies the dragged field from one container to another. * * @param {Ext.pivot.plugin.configurator.Container/Ext.pivot.plugin.configurator.Column} toTarget * @param {Ext.pivot.plugin.configurator.Column} column * @param {String} pos Position: `after` or `before` * * @private */ dragDropField: function(toTarget, column, pos){ var me = this, pivot = me.getPivot(), field = column.getField(), fromContainer = column.ownerCt, toContainer = toTarget.isConfiguratorContainer ? toTarget : toTarget.ownerCt, toField = toTarget.isConfiguratorField ? toTarget : toTarget.items.last(), fromFieldType = fromContainer.getFieldType(), toFieldType = toContainer.getFieldType(), topAxisCt = me.getTopAxisContainer(), leftAxisCt = me.getLeftAxisContainer(), newPos, item, toFocus; if (pivot.fireEvent('beforemoveconfigfield', this, { fromContainer: fromContainer, toContainer: toContainer, field: field }) !== false) { if (fromContainer !== toContainer){ if (toField) { newPos = toContainer.items.findIndex('id', toField.id); newPos = (pos === 'before') ? newPos : newPos + 1; } else { newPos = -1; } if (toFieldType === 'all') { // source is "Row labels"/"Column labels"/"Values" // destination is "All fields" // we just remove the field from the source toField.focus(); fromContainer.removeField(column); } else if (toFieldType === 'aggregate') { // source is "Row labels"/"Column labels"/"All fields" // destination is "Values" // we copy the field to destination toFocus = toContainer.addField(field.clone(), newPos, true); if (fromFieldType !== 'all'){ // remove the field from the left/top axis fromContainer.remove(column); } } else { // source is "Row labels"/"Column labels"/"Values"/"All fields" // destination is "Row labels"/"Column labels" // first let's check if the field is already in the destination container item = me.findFieldInContainer(field, toContainer); if (item) { // the destination has the field already return; } // See if it was on another axis. if (toFieldType === 'leftAxis') { item = me.findFieldInContainer(field, topAxisCt); } else { item = me.findFieldInContainer(field, leftAxisCt); } // If so, move it here. if (item) { toContainer.add(item); return me.applyChanges(item); } else { if(fromFieldType === 'aggregate'){ // we need to remove the dragged field because it was found on one of the axis fromContainer.remove(column); } toFocus = toContainer.addField(field.clone(), newPos, true); } } } else { toContainer.moveField(column.id, toField.id, pos); } // Ensure that any removal does not allow focus to escape to the body if (toFocus) { toFocus.focus(); toFocus.el.resumeFocusEvents(); } } }, isAllowed: function (toTarget, column) { var allowed = true, field = column.getField(), fromContainer = column.ownerCt, toContainer = toTarget.isConfiguratorContainer ? toTarget : toTarget.ownerCt, fromFieldType = fromContainer.getFieldType(), toFieldType = toContainer.getFieldType(); if (fromFieldType === 'aggregate' && (toFieldType === 'leftAxis' || toFieldType === 'topAxis')) { allowed = !this.findFieldInContainer(field, toContainer); } return allowed; }, /** * * @param {Ext.pivot.plugin.configurator.Field} field * @param {Ext.pivot.plugin.configurator.Container} container * @returns {Ext.pivot.plugin.configurator.Column} * * @private */ findFieldInContainer: function(field, container){ var length = container.items.getCount(), i, item; for(i = 0; i < length; i++){ item = container.items.getAt(i); if(item.getField().getDataIndex() == field.getDataIndex()){ return item; } } }, /** * Listener for the 'pivotdone' event. Initialize configurator fields or restore last field focus. * * @private */ onPivotDone: function(){ var me = this, field = me.lastFocusedField; if(me.internalReconfiguration){ me.internalReconfiguration = false; // restore focus if(field && field.isConfiguratorContainer){ field = field.items.first(); } if(!field){ field = me.getAllFieldsContainer().items.first(); } if(field) { field.focus(); }else{ me.getPivot().focus(); } }else { me.initPivotFields(); } }, /** * Collect configurator changes and reconfigure the pivot component * * @private */ reconfigurePivot: function(){ var me = this, pivot = me.getPivot(), obj = { topAxis: me.getFieldsFromContainer(me.getTopAxisContainer(), true), leftAxis: me.getFieldsFromContainer(me.getLeftAxisContainer(), true), aggregate: me.getFieldsFromContainer(me.getAggregateContainer(), true) }; me.internalReconfiguration = true; if(pivot.fireEvent('beforeconfigchange', me, obj) !== false){ pivot.getMatrix().reconfigure(obj); pivot.fireEvent('configchange', me, obj); } }, /** * This function is temporarily added here until the placeholder expanding/collpasing * is fixed for docked panels. * * @param direction * @param animate * @returns {Ext.pivot.plugin.configurator.Panel} * @private */ placeholderCollapse: function(direction, animate) { var me = this, ownerCt = me.ownerCt, collapseDir = direction || me.collapseDirection, floatCls = Ext.panel.Panel.floatCls, placeholder = me.getPlaceholder(collapseDir), slideInDirection; me.isCollapsingOrExpanding = 1; // Upcoming layout run will ignore this Component me.setHiddenState(true); me.collapsed = collapseDir; if (placeholder.rendered) { // We may have been added to another Container from that in which we rendered the placeholder if (placeholder.el.dom.parentNode !== me.el.dom.parentNode) { me.el.dom.parentNode.insertBefore(placeholder.el.dom, me.el.dom); } placeholder.hidden = false; placeholder.setHiddenState(false); placeholder.el.show(); ownerCt.updateLayout(); } else { //ATE - this is the fix if(me.dock){ placeholder.dock = me.dock; ownerCt.addDocked(placeholder); }else{ ownerCt.insert(ownerCt.items.indexOf(me), placeholder); } } if (me.rendered) { // We assume that if collapse was caused by keyboard action // on focused collapse tool, the logical focus transition // is to placeholder's expand tool. Note that it may not be // the case when the user *clicked* collapse tool while focus // was elsewhere; in that case we dare not touch focus // to avoid sudden jumps. if (Ext.ComponentManager.getActiveComponent() === me.collapseTool) { me.focusPlaceholderExpandTool = true; } // We MUST NOT hide using display because that resets all scroll information. me.el.setVisibilityMode(me.placeholderCollapseHideMode); if (animate) { me.el.addCls(floatCls); placeholder.el.hide(); slideInDirection = me.convertCollapseDir(collapseDir); me.el.slideOut(slideInDirection, { preserveScroll: true, duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration), listeners: { scope: me, afteranimate: function() { var me = this; me.el.removeCls(floatCls); /* We need to show the element so that slideIn will work correctly. * However, if we leave it visible then it can be seen before * the animation starts, causing a flicker. The solution, * borrowed from date picker, is to hide it using display:none. * The slideIn effect includes a call to fixDisplay() that will * undo the display none at the appropriate time. */ me.placeholder.el.show().setStyle('display', 'none').slideIn(slideInDirection, { easing: 'linear', duration: 100, listeners: { afteranimate: me.doPlaceholderCollapse, scope: me } }); } } }); } else { me.el.hide(); me.doPlaceholderCollapse(); } } else { me.isCollapsingOrExpanding = 0; if (!me.preventCollapseFire) { me.fireEvent('collapse', me); } } return me; }, /** * This function is temporarily added here until the placeholder expanding/collpasing * is fixed for docked panels. * * @param animate * @returns {Ext.pivot.plugin.configurator.Panel} * @private */ placeholderExpand: function(animate) { var me = this, collapseDir = me.collapsed, expandTool = me.placeholder.expandTool, floatCls = Ext.panel.Panel.floatCls, center = me.ownerLayout ? me.ownerLayout.centerRegion: null, finalPos, floatedPos; // Layouts suspended - don't bother with animation shenanigans if (Ext.Component.layoutSuspendCount) { animate = false; } if (me.floatedFromCollapse) { floatedPos = me.getPosition(true); // these are the same cleanups performed by the normal slideOut mechanism: me.slideOutFloatedPanelBegin(); me.slideOutFloatedPanelEnd(); me.floated = false; } // We assume that if expand was caused by keyboard action on focused // placeholder expand tool, the logical focus transition is to the // panel header's collapse tool. // Note that it may not be the case when the user *clicked* expand tool // while focus was elsewhere; in that case we dare not touch focus to avoid // sudden jumps. if (Ext.ComponentManager.getActiveComponent() === expandTool) { me.focusHeaderCollapseTool = true; // There is an odd issue with JAWS screen reader: when expanding a panel, // it will announce Expand tool again before focus is forced to Collapse // tool. I'm not sure why that happens since focus does not move from // Expand tool during animation; this hack should work around // the problem until we come up with more understanding and a proper // solution. The attributes are restored below in doPlaceholderExpand. expandTool._ariaRole = expandTool.ariaEl.dom.getAttribute('role'); expandTool._ariaLabel = expandTool.ariaEl.dom.getAttribute('aria-label'); expandTool.ariaEl.dom.setAttribute('role', 'presentation'); expandTool.ariaEl.dom.removeAttribute('aria-label'); } if (animate) { // Expand me and hide the placeholder Ext.suspendLayouts(); me.placeholder.hide(); me.el.show(); me.collapsed = false; me.setHiddenState(false); // Stop the center region from moving when laid out without the placeholder there. // Unless we are expanding from a floated out situation. In that case, it's laid out immediately. if (center && !floatedPos) { center.hidden = true; } Ext.resumeLayouts(true); //ATE - this is the fix if(center) { center.hidden = false; } me.el.addCls(floatCls); // At this point, this Panel is arranged in its correct, expanded layout. // The center region has not been affected because it has been flagged as hidden. // // If we are proceeding from floated, the center region has also been arranged // in its new layout to accommodate this expansion, so no further layout is needed, just // element animation. // // If we are proceeding from fully collapsed, the center region has *not* been relayed out because // the UI look and feel dictates that it stays stable until the expanding panel has slid in all the // way, and *then* it snaps into place. me.isCollapsingOrExpanding = 2; // Floated, move it back to the floated pos, and thence into the correct place if (floatedPos) { finalPos = me.getXY(); me.setLocalXY(floatedPos[0], floatedPos[1]); me.setXY([finalPos[0], finalPos[1]], { duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration), listeners: { scope: me, afteranimate: function() { var me = this; me.el.removeCls(floatCls); me.isCollapsingOrExpanding = 0; me.fireEvent('expand', me); } } }); } // Not floated, slide it in to the correct place else { me.el.hide(); me.placeholder.el.show(); me.placeholder.hidden = false; // Slide this Component's el back into place, after which we lay out AGAIN me.setHiddenState(false); me.el.slideIn(me.convertCollapseDir(collapseDir), { preserveScroll: true, duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration), listeners: { afteranimate: me.doPlaceholderExpand, scope: me } }); } } else { me.floated = me.collapsed = false; me.doPlaceholderExpand(true); } return me; } });