/** * Panels are most useful as Overlays - containers that float over your application. They contain extra styling such * that when you {@link #showBy} another component, the container will appear in a rounded black box with a 'tip' * pointing to a reference component. * * If you don't need this extra functionality, you should use {@link Ext.Container} instead. See the * [Overlays example](#!/example/overlays) for more use cases. * * @example miniphone preview * * var button = Ext.create('Ext.Button', { * text: 'Button', * id: 'rightButton' * }); * * Ext.create('Ext.Container', { * fullscreen: true, * items: [ * { * docked: 'top', * xtype: 'titlebar', * items: [ * button * ] * } * ] * }); * * Ext.create('Ext.Panel', { * html: 'Floating Panel', * left: 0, * padding: 10 * }).showBy(button); * */Ext.define('Ext.Panel', { extend: 'Ext.Container', xtype: 'panel', requires: [ 'Ext.util.LineSegment' ], alternateClassName: 'Ext.panel.Panel', defaultBindProperty: 'title', isPanel: true, config: { baseCls: Ext.baseCSSPrefix + 'panel', /** * @cfg border * @inheritdoc */ border: false, /** * @cfg {Number/Boolean/String} bodyPadding * A shortcut for setting a padding style on the body element. The value can either be * a number to be applied to all sides, or a normal CSS string describing padding. */ bodyPadding: null, /** * @cfg {Boolean} bodyBorder * - `true` to enable the border around the panel body (as defined by the theme) * Note that even when enabled, the bodyBorder is only visible when there are docked * items around the edges of the panel. Where the bodyBorder touches the panel's outer * border it is automatically collapsed into a single border. * * - `false` to disable the body border * * - `null` - use the value of {@link #border} as the value for bodyBorder */ bodyBorder: null, header: null, icon: null, iconCls: null, title: null, tools: null }, manageBorders: true, getElementConfig: function() { return { reference: 'element', classList: ['x-container', 'x-unsized'], children: [ { reference: 'innerElement', className: 'x-inner' }, { reference: 'tipElement', className: 'x-anchor', hidden: true } ] }; }, /** * Adds a CSS class to the body element. If not rendered, the class will * be added when the panel is rendered. * @param {String} cls The class to add * @return {Ext.panel.Panel} this */ addBodyCls: function(cls) { this.innerElement.addCls(cls); return this; }, /** * Removes a CSS class from the body element. * @param {String} cls The class to remove * @return {Ext.panel.Panel} this */ removeBodyCls: function(cls) { this.innerElement.removeCls(cls); return this; }, applyBodyPadding: function(bodyPadding) { if (bodyPadding === true) { bodyPadding = 5; } if (bodyPadding) { bodyPadding = Ext.dom.Element.unitizeBox(bodyPadding); } return bodyPadding; }, addTool: function (tool) { var header = this.ensureHeader(), // creates if header !== false items; if (header) { items = header.createTools(Ext.Array.from(tool), this); if (items && items.length) { items = header.add(items); } } return items; }, applyHeader: function (newHeader, oldHeader) { var me = this, header = oldHeader; if (newHeader === false) { if (header) { me.remove(header); header = null; } } else if (newHeader) { if (header) { if (newHeader !== true) { header.setConfig(newHeader); } } else { // add() will ensure we sort the header to the front by its "weight" header = me.add(me.createHeader(newHeader)); } } return header || null; }, applyTools: function (tools) { var header = this.ensureHeader(), // creates if header !== false items; if (header) { // Remove all tools (since we are the impl of a setTools([...]) call) header.clearTools(); items = header.createTools(tools, this); if (items && items.length) { header.add(items); } } // we don't return anything since the tools are "stored" on the Header }, createHeader: function (config) { var me = this, ret = { xtype: 'panelheader', docked: 'top', ui: me.getUi() }, icon, title; if (config && config !== true) { Ext.merge(ret, config); } if (me.initialized) { // Only attempt to configure title if we are not currently initializing. // During initialization the updater for title will run if present and apply // it to the header so there is no work to be done here. title = me.getTitle(); if (title != null) { if (typeof title === 'string') { title = { text: title }; } Ext.merge(ret, { title: title }); } icon = me.getIconCls(); if (icon != null) { ret.iconCls = icon; } else { icon = me.getIcon(); if (icon != null) { ret.icon = icon; } } } return ret; }, updateBorder: function(border, oldBorder) { this.callParent([ border, oldBorder ]); if (this.getBodyBorder() === null) { this.setBodyBorderEnabled(border !== false); } }, updateBodyPadding: function(newBodyPadding) { this.innerElement.setStyle('padding', newBodyPadding); }, updateBodyBorder: function(bodyBorder) { var border = (bodyBorder === null) ? this.getBorder() : bodyBorder; this.setBodyBorderEnabled(bodyBorder !== false); }, updateIcon: function (icon) { var header = this.ensureHeader(); // creates if header !== false if (header) { header.setIcon(icon); } }, updateIconCls: function (icon) { var header = this.ensureHeader(); // creates if header !== false if (header) { header.setIconCls(icon); } }, updateTitle: function (title) { var header = this.ensureHeader(); // creates if header !== false if (header) { header.setTitle(title); } }, updateUi: function(ui, oldUi) { var me = this, suffix = 'x-panel-inner-', innerElement = me.innerElement, // Let the header initter get the ui since ui is a cached config and // should not pull in non-cached cfgs at this early stage header = !me.isConfiguring && me.ensureHeader(); if (oldUi) { innerElement.removeCls(suffix + oldUi); } if (ui) { innerElement.addCls(suffix + ui); } if (header) { me.getTitle(); header.setUi(ui); } me.callParent([ui, oldUi]); }, alignTo: function(component, alignment) { var alignmentInfo = this.getAlignmentInfo(component, alignment); if(alignmentInfo.isAligned) return; var tipElement = this.tipElement; tipElement.hide(); if (this.currentTipPosition) { tipElement.removeCls('x-anchor-' + this.currentTipPosition); } this.callParent(arguments); var LineSegment = Ext.util.LineSegment, alignToElement = component.isComponent ? component.renderElement : component, element = this.renderElement, alignToBox = alignToElement.getBox(), box = element.getBox(), left = box.left, top = box.top, right = box.right, bottom = box.bottom, centerX = left + (box.width / 2), centerY = top + (box.height / 2), leftTopPoint = { x: left, y: top }, rightTopPoint = { x: right, y: top }, leftBottomPoint = { x: left, y: bottom }, rightBottomPoint = { x: right, y: bottom }, boxCenterPoint = { x: centerX, y: centerY }, alignToCenterX = alignToBox.left + (alignToBox.width / 2), alignToCenterY = alignToBox.top + (alignToBox.height / 2), alignToBoxCenterPoint = { x: alignToCenterX, y: alignToCenterY }, centerLineSegment = new LineSegment(boxCenterPoint, alignToBoxCenterPoint), offsetLeft = 0, offsetTop = 0, tipSize, tipWidth, tipHeight, tipPosition, tipX, tipY; tipElement.setVisibility(false); tipElement.show(); tipSize = tipElement.getSize(); tipWidth = tipSize.width; tipHeight = tipSize.height; if (centerLineSegment.intersects(new LineSegment(leftTopPoint, rightTopPoint))) { tipX = Math.min(Math.max(alignToCenterX, left + tipWidth), right - (tipWidth)); tipY = top; offsetTop = tipHeight + 10; tipPosition = 'top'; } else if (centerLineSegment.intersects(new LineSegment(leftTopPoint, leftBottomPoint))) { tipX = left; tipY = Math.min(Math.max(alignToCenterY + (tipWidth / 2), tipWidth * 1.6), bottom - (tipWidth / 2.2)); offsetLeft = tipHeight + 10; tipPosition = 'left'; } else if (centerLineSegment.intersects(new LineSegment(leftBottomPoint, rightBottomPoint))) { tipX = Math.min(Math.max(alignToCenterX, left + tipWidth), right - tipWidth); tipY = bottom; offsetTop = -tipHeight - 10; tipPosition = 'bottom'; } else if (centerLineSegment.intersects(new LineSegment(rightTopPoint, rightBottomPoint))) { tipX = right; tipY = Math.max(Math.min(alignToCenterY - tipHeight, bottom - tipWidth * 1.3), tipWidth / 2); offsetLeft = -tipHeight - 10; tipPosition = 'right'; } if (tipX || tipY) { this.currentTipPosition = tipPosition; tipElement.addCls('x-anchor-' + tipPosition); tipElement.setLeft(tipX - left); tipElement.setTop(tipY - top); tipElement.setVisibility(true); this.setLeft(this.getLeft() + offsetLeft); this.setTop(this.getTop() + offsetTop); } }, privates: { ensureHeader: function () { var me = this, header; me.getViewModel(); me.getItems(); header = me.getHeader(); if (!header && header !== false) { me.setHeader(true); header = me.getHeader(); } return header; }, setBodyBorderEnabled: function(enabled) { this.innerElement.setStyle('border-width', enabled ? '' : '0'); } }});