/** * */Ext.define('Ext.container.DockingContainer', { requires: ['Ext.util.MixedCollection', 'Ext.Element' ], isDockingContainer: true, /** * @event dockedadd * Fires when any {@link Ext.Component} is added or inserted as a docked item. * @param {Ext.panel.Panel} this * @param {Ext.Component} component The component being added * @param {Number} index The index at which the component will be added docked items collection */ /** * @event dockedremove * Fires when any {@link Ext.Component} is removed from the docked items. * @param {Ext.panel.Panel} this * @param {Ext.Component} component The component being removed */ /** * @cfg {Object} defaultDockWeights * This object holds the default weights applied to dockedItems that have no weight. * These start with a weight of 1, to allow negative weights to insert before top items * and are odd numbers so that even weights can be used to get between different dock orders. * * To make default docking order match border layout, do this: * * Ext.panel.Panel.prototype.defaultDockWeights = { top: 1, bottom: 3, left: 5, right: 7 }; * * Changing these defaults as above or individually on this object will effect all Panels. * To change the defaults on a single panel, you should replace the entire object: * * initComponent: function() { * // NOTE: Don't change members of defaultDockWeights since the object is shared. * this.defaultDockWeights = { top: 1, bottom: 3, left: 5, right: 7 }; * * this.callParent(); * } * * To change only one of the default values, you do this: * * initComponent: function() { * // NOTE: Don't change members of defaultDockWeights since the object is shared. * this.defaultDockWeights = Ext.applyIf({ top: 10 }, this.defaultDockWeights); * * this.callParent(); * } */ defaultDockWeights: { top: { render: 1, visual: 1 }, left: { render: 3, visual: 5 }, right: { render: 5, visual: 7 }, bottom: { render: 7, visual: 3 } }, /** * @private * Values to decide which side of the body element docked items must go * This overides any weight. A left/top will *always* sort before a right/bottom * regardless of any weight value. Weights sort at either side of the "body" dividing point. */ dockOrder: { top: -1, left: -1, right: 1, bottom: 1 }, /** * @private * Number of dock 'left' and 'right' items. */ horizontalDocks: 0, /** * Adds docked item(s) to the container. * * @param {Object/Object[]} items The Component or array of components to add. The components * must include a 'dock' parameter on each component to indicate where it should be docked * ('top', 'right', 'bottom', 'left'). * @param {Number} [pos] The index at which the Component will be added * @return {Ext.Component[]} The added components. */ addDocked: function(items, pos) { var me = this, rendered = me.rendered, i = 0, dockedItems = me.dockedItems, lastIndex = dockedItems.getCount(), index, instanced, item, length; items = me.prepareItems(items); length = items.length; if (rendered) { Ext.suspendLayouts(); } if (pos === undefined) { pos = lastIndex; } else { pos = Math.min(pos, lastIndex); } for (; i < length; i++) { item = items[i]; if (item.isDetached) { item.reattachToBody(); } item.dock = item.dock || 'top'; if (item.dock === 'left' || item.dock === 'right') { me.horizontalDocks++; } index = pos + i; dockedItems.insert(index, item); instanced = !!item.instancedCmp; delete item.instancedCmp; item.onAdded(me, index, instanced); delete item.$initParent; if (me.onDockedAdd !== Ext.emptyFn) { me.onDockedAdd(item); } if (me.hasListeners.dockedadd) { me.fireEvent('dockedadd', me, item, index); } } if (me.rendered) { me.updateLayout(); Ext.resumeLayouts(true); } return items; }, destroyDockedItems: function() { var dockedItems = this.dockedItems, c; if (dockedItems) { while ((c = dockedItems.first())) { this.removeDocked(c, { destroy: true }); } } }, doRenderDockedItems: function(out, renderData, after) { // Careful! This method is bolted on to the frameTpl and renderTpl so all we get for // context is the renderData! The "this" pointer is either the frameTpl or the // renderTpl instance! var me = renderData.$comp, layout = me.componentLayout, // eslint-disable-next-line no-unused-vars tabGuard = me.tabGuard && me.lookupTpl('tabGuardTpl'), items, tree; if (layout.getDockedItems) { items = layout.getDockedItems('render', !after); tree = items && layout.getItemsRenderTree(items); if (tree) { Ext.DomHelper.generateMarkup(tree, out); } } }, /** * Finds a docked component by id, itemId or position. Also see {@link #getDockedItems} * @param {String/Number} comp The id, itemId or position of the docked component * (see {@link Ext.container.Container#getComponent getComponent} for details) * @return {Ext.Component} The docked component (if found) */ getDockedComponent: function(comp) { if (Ext.isObject(comp)) { comp = comp.getItemId(); } return this.dockedItems.get(comp); }, /** * Retrieves an array of all currently docked Components. * * For example to find a toolbar that has been docked at top: * * panel.getDockedItems('toolbar[dock="top"]'); * * @param {String} selector A {@link Ext.ComponentQuery ComponentQuery} selector string * to filter the returned items. * @param {Boolean} beforeBody An optional flag to limit the set of items to only those * before the body (true) or after the body (false). All components are returned by default. * @return {Ext.Component[]} The array of docked components meeting the specified criteria. */ getDockedItems: function(selector, beforeBody) { var dockedItems = this.getComponentLayout().getDockedItems('render', beforeBody); if (selector && dockedItems.length) { dockedItems = Ext.ComponentQuery.query(selector, dockedItems); } return dockedItems; }, getDockingRefItems: function(deep, containerItems) { // deep fetches the docked items and their descendants using '*' and then '* *' var selector = deep && '*,* *', // start with only the top/left docked items (and maybe their children) dockedItems = this.getDockedItems(selector, true), items; // push container items (and maybe their children) after top/left docked items: dockedItems.push.apply(dockedItems, containerItems); // push right/bottom docked items (and maybe their children) after container items: items = this.getDockedItems(selector, false); dockedItems.push.apply(dockedItems, items); return dockedItems; }, /** * @protected * Initializes an Item Collection for docked items to be inserted into. If items * already exist, they will be added to the new collection. */ initDockingItems: function() { var me = this, items = me.dockedItems; if (!items || !items.isMixedCollection) { me.dockedItems = new Ext.util.ItemCollection(); if (items) { me.addDocked(items); } } }, /** * Inserts docked item(s) to the panel at the indicated position. * @param {Number} pos The index at which the Component will be inserted * @param {Object/Object[]} items The Component or array of components to add. The components * must include a 'dock' paramater on each component to indicate where it should be docked * ('top', 'right', 'bottom', 'left'). */ insertDocked: function(pos, items) { this.addDocked(items, pos); }, // Placeholder empty functions /** * @method * Invoked after a docked item is added to the Panel. * @param {Ext.Component} component * @template * @protected */ onDockedAdd: Ext.emptyFn, /** * @method * Invoked after a docked item is removed from the Panel. * @param {Ext.Component} component * @template * @protected */ onDockedRemove: Ext.emptyFn, /** * Removes the docked item from the panel. * @param {Ext.Component} item The Component to remove. * @param {Boolean/Object} [flags] Pass `true` to destroy the item or an * object with multiple options. If not passed, {@link #cfg!autoDestroy} will * determine if the item is destroyed. * @param {Boolean} [flags.destroy] Pass `true` to destroy the item. * @param {Boolean} [flags.detach] Pass `true` to put the item in the * {@link Ext#getDetachedBody detachedBody element}. */ removeDocked: function(item, flags) { var me = this, layout, hasLayout, doDestroy, doDetach; if (!me.dockedItems.contains(item)) { return item; } // Ensure the flags are set correctly if (flags == null) { doDestroy = me.autoDestroy; } else if (typeof flags === 'boolean') { doDestroy = flags; } else { doDestroy = ('destroy' in flags) && flags.destroy; doDetach = ('detach' in flags) && flags.detach; } if (item.dock === 'left' || item.dock === 'right') { me.horizontalDocks--; } layout = me.componentLayout; hasLayout = layout && me.rendered; if (hasLayout) { layout.onRemove(item); } me.dockedItems.remove(item); // destroying flag is true if the removal is taking place as part of destruction, // OR if removal is intended to *cause* destruction if (!item.destroyed) { item.onRemoved(item.destroying || doDestroy); } me.onDockedRemove(item); if (doDestroy) { item.destroy(); } else if (!me.destroyed) { if (hasLayout) { // not destroying, make any layout related removals layout.afterRemove(item); } if (doDetach && item.rendered) { item.detachFromBody(); } } if (me.hasListeners.dockedremove) { me.fireEvent('dockedremove', me, item); } if (!me.destroying) { me.updateLayout(); } return item; }, /** * Moves a docked item to a different side. * @param {Ext.Component} item * @param {'top'/'right'/'bottom'/'left'} side * @private */ moveDocked: function(item, side) { var me = this; if (me.rendered) { Ext.suspendLayouts(); } me.removeDocked(item, { destroy: false, detach: true }); item.dock = side; me.addDocked(item); if (me.rendered) { if (item.frame) { item.updateFrame(); } Ext.resumeLayouts(true); } }, setupDockingRenderTpl: function(renderTpl) { renderTpl.renderDockedItems = this.doRenderDockedItems; }});