/** * A basic title component for a Panel Header */Ext.define('Ext.panel.Title', { extend: 'Ext.Component', xtype: 'title', requires: [ 'Ext.Glyph' ], isTitle: true, // layout system optimization. Allows autocomponent layout to measure height without // having to first know the width. noWrap: true, // For performance reasons we give the following configs their default values on // the class body. This prevents the updaters from running on initialization in the // default configuration scenario textAlign: 'left', iconAlign: 'left', rotation: 0, text: ' ', beforeRenderConfig: { /** * @cfg [textAlign='left'] * @inheritdoc Ext.panel.Header#cfg-titleAlign * @accessor */ textAlign: null, /** * @cfg {String} [text=' '] * The title's text (can contain html tags/entities) * @accessor */ text: null, /** * @cfg glyph * @inheritdoc Ext.panel.Header#cfg-glyph * @accessor */ glyph: null, /** * @cfg icon * @inheritdoc Ext.panel.Header#cfg-icon * @accessor */ icon: null, /** * @cfg {'top'/'right'/'bottom'/'left'} [iconAlign='left'] * alignment of the icon * @accessor */ iconAlign: null, /** * @cfg iconCls * @inheritdoc Ext.panel.Header#cfg-iconCls * @accessor */ iconCls: null, /** * @cfg [rotation=0] * @inheritdoc Ext.panel.Header#cfg-titleRotation * @accessor */ rotation: null }, /** * @cfg autoEl * @inheritdoc */ autoEl: { role: 'presentation', // Required for Opera unselectable: 'on' }, // In most cases the panel header title is purely presentational // and does not have any structural significance wrt Assistive Technologies. // The only exception is when the panel participates in Accordion layout; // in that case the title component has the role of 'tab' and its textEl // should not have any role to expose the text as the tab's accessible name. // Header component is aware of this participation and will reset textElRole. textElRole: 'presentation', // By default, panel title is not focusable; this only happens in Accordion layout. // This config option is overridable, and it will prime tabIndex to be used // without hardcoding it. /** * @cfg tabIndex * @inheritdoc */ tabIndex: 0, /** * @cfg childEls * @inheritdoc */ childEls: [ 'textEl', 'iconEl', 'iconWrapEl' ], /** * @cfg renderTpl * @inheritdoc */ renderTpl: '<tpl if="iconMarkup && iconBeforeTitle">{iconMarkup}</tpl>' + // unselectable="on" is required for Opera, other browsers // inherit unselectability from the header '<div id="{id}-textEl" data-ref="textEl"' + ' class="{textCls} {textCls}-{ui} {itemCls}{childElCls}" unselectable="on"' + '<tpl if="textElRole"> role="{textElRole}"</tpl>' + '>' + '{text}' + '</div>' + '<tpl if="iconMarkup && !iconBeforeTitle">{iconMarkup}</tpl>', iconTpl: '<div id="{id}-iconWrapEl" data-ref="iconWrapEl" role="presentation" ' + 'class="{iconWrapCls} {iconWrapCls}-{ui} {iconAlignCls} {itemCls}{childElCls}"' + '<tpl if="iconWrapStyle"> style="{iconWrapStyle}"</tpl>>' + '<div id="{id}-iconEl" data-ref="iconEl" role="presentation" unselectable="on" ' + 'class="{baseIconCls} {baseIconCls}-{ui} {iconCls} {glyphCls}" style="' + '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>' + '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">' + '<tpl if="glyph">{glyph}</tpl>' + '</div>' + '</div>', _textAlignClasses: { left: Ext.baseCSSPrefix + 'title-align-left', center: Ext.baseCSSPrefix + 'title-align-center', right: Ext.baseCSSPrefix + 'title-align-right' }, _iconAlignClasses: { top: Ext.baseCSSPrefix + 'title-icon-top', right: Ext.baseCSSPrefix + 'title-icon-right', bottom: Ext.baseCSSPrefix + 'title-icon-bottom', left: Ext.baseCSSPrefix + 'title-icon-left' }, _rotationClasses: { 0: Ext.baseCSSPrefix + 'title-rotate-none', 1: Ext.baseCSSPrefix + 'title-rotate-right', 2: Ext.baseCSSPrefix + 'title-rotate-left' }, _rotationAngles: { 1: 90, 2: 270 }, /** * @cfg baseCls * @inheritdoc */ baseCls: Ext.baseCSSPrefix + 'title', _titleSuffix: '-title', _glyphCls: Ext.baseCSSPrefix + 'title-glyph', _iconWrapCls: Ext.baseCSSPrefix + 'title-icon-wrap', _baseIconCls: Ext.baseCSSPrefix + 'title-icon', _itemCls: Ext.baseCSSPrefix + 'title-item', _textCls: Ext.baseCSSPrefix + 'title-text', afterComponentLayout: function() { var me = this, rotation = me.getRotation(), lastBox, lastX, el; if (rotation && !Ext.isIE8) { // In IE8 we use a BasicImage filter to rotate the title // element 90 degrees. The result is that what was the bottom left // corner is positioned exactly where the top left corner was // originally. Since this is the desired result, no additional // positioning is needed in IE8. In browsers that support CSS3 transform, // however, we use transform: rotate(90deg) to rotate the element. // CSS3 also provides a way to specify the position the rotated element // by changing the axis on which it is rotated using the transform-origin // property, but the required transform origin varies based on the // elements size, and would require some complex math to calculate. // To achieve the desired rotated position in modern browsers we use // a transform-origin of "0, 0" which means the top left corner of // the element is the rotation axis. After rotating 90 degrees we // simply move the element to the right by the same number of pixels // as its width. el = me.el; lastBox = me.lastBox; lastX = lastBox.x; el.setStyle( me._getVerticalAdjustDirection(), (lastX + ((rotation === 1) ? lastBox.width : -lastBox.height)) + 'px' ); } this.callParent(); }, onRender: function() { var me = this, rotation = me.getRotation(), el = me.el; me.callParent(); if (rotation) { el.setVertical(me._rotationAngles[rotation]); } if (Ext.supports.FixedTableWidthBug) { // Workaround for https://bugs.webkit.org/show_bug.cgi?id=130239 and // https://code.google.com/p/chromium/issues/detail?id=377190 // See styleHooks for more details el._needsTableWidthFix = true; } }, applyText: function(text) { if (!text) { text = ' '; } return text; }, beforeRender: function() { var me = this; me.callParent(); me.addCls(me._rotationClasses[me.getRotation()]); me.addCls(me._textAlignClasses[me.getTextAlign()]); }, getIconMarkup: function() { return this.lookupTpl('iconTpl').apply(this.getIconRenderData()); }, getIconRenderData: function() { var me = this, icon = me.getIcon(), iconCls = me.getIconCls(), glyph = me.getGlyph(), glyphFontFamily, iconAlign = me.getIconAlign(); // Transform Glyph to the useful parts if (glyph) { glyphFontFamily = glyph.fontFamily; glyph = glyph.character; } return { id: me.id, ui: me.ui, itemCls: me._itemCls, iconUrl: icon, iconCls: iconCls, iconWrapCls: me._iconWrapCls, baseIconCls: me._baseIconCls, iconAlignCls: me._iconAlignClasses[iconAlign], glyph: glyph, glyphCls: glyph ? me._glyphCls : '', glyphFontFamily: glyphFontFamily }; }, initRenderData: function() { var me = this, iconAlign, renderData; renderData = Ext.apply({ text: me.getText(), textElRole: me.textElRole, id: me.id, ui: me.ui, itemCls: me._itemCls, textCls: me._textCls, iconMarkup: null, iconBeforeTitle: null }, me.callParent()); if (me._hasIcon()) { iconAlign = me.getIconAlign(); renderData.iconMarkup = me.getIconMarkup(); renderData.iconBeforeTitle = (iconAlign === 'top' || iconAlign === 'left'); } return renderData; }, onAdded: function(container, pos, instanced) { var me = this, suffix = me._titleSuffix, baseCls = container.baseCls; me.addCls([ baseCls + suffix, baseCls + suffix + '-' + container.ui ]); me.callParent([container, pos, instanced]); }, applyGlyph: function(glyph, oldGlyph) { if (glyph) { if (!glyph.isGlyph) { glyph = new Ext.Glyph(glyph); } if (glyph.isEqual(oldGlyph)) { glyph = undefined; } } return glyph; }, updateGlyph: function(glyph, oldGlyph) { var me = this, glyphCls = me._glyphCls, iconEl; if (me.rendered) { me._syncIconVisibility(); iconEl = me.iconEl; if (glyph) { iconEl.dom.innerHTML = glyph.character; iconEl.addCls(glyphCls); iconEl.setStyle('font-family', glyph.fontFamily); } else if (oldGlyph !== glyph) { iconEl.dom.innerHTML = ''; iconEl.removeCls(glyphCls); } if (me._didIconStateChange(oldGlyph, glyph)) { me.updateLayout(); } } }, updateIcon: function(icon, oldIcon) { icon = icon || ''; var me = this, iconEl; if (me.rendered && icon !== oldIcon) { me._syncIconVisibility(); iconEl = me.iconEl; iconEl.setStyle('background-image', icon ? 'url(' + icon + ')': ''); if (me._didIconStateChange(oldIcon, icon)) { me.updateLayout(); } } }, updateIconAlign: function(align, oldAlign) { var me = this, iconWrapEl = me.iconWrapEl, el, iconAlignClasses; if (me.iconWrapEl) { el = me.el; iconAlignClasses = me._iconAlignClasses; if (oldAlign) { iconWrapEl.removeCls(iconAlignClasses[oldAlign]); } iconWrapEl.addCls(iconAlignClasses[align]); // here we move the iconWrap to the correct position in the dom - before the // title el for top/left alignments, and after the title el for right/bottom if (align === 'top' || align === 'left') { el.insertFirst(iconWrapEl); } else { el.appendChild(iconWrapEl); } me.updateLayout(); } }, updateIconCls: function(cls, oldCls) { cls = cls || ''; var me = this, iconEl; if (me.rendered && oldCls !== cls) { me._syncIconVisibility(); iconEl = me.iconEl; if (oldCls) { iconEl.removeCls(oldCls); } iconEl.addCls(cls); if (me._didIconStateChange(oldCls, cls)) { me.updateLayout(); } } }, updateRotation: function(rotation, oldRotation) { var me = this, el, rotationClasses; if (me.rendered) { el = me.el; rotationClasses = me._rotationClasses; me.removeCls(rotationClasses[oldRotation]); me.addCls(rotationClasses[rotation]); el.setHorizontal(); if (rotation) { el.setVertical(me._rotationAngles[rotation]); } // reset styles set by adjustTitlePosition (handles both rtl/ltr), and sizing // set by last layout run (this prevents parallel size from becoming perpendicular // size after rotation) el.setStyle({ right: '', left: '', top: '', height: '', width: '' }); me.lastBox = null; me.updateLayout(); } }, updateText: function(text) { if (this.rendered) { this.textEl.setHtml(text); this.updateLayout(); } }, updateTextAlign: function(align, oldAlign) { var me = this, textAlignClasses = me._textAlignClasses; if (me.rendered) { if (oldAlign) { me.removeCls(textAlignClasses[oldAlign]); } me.addCls(textAlignClasses[align]); me.updateLayout(); } }, privates: { // rtl hook _getVerticalAdjustDirection: function() { return 'left'; }, _didIconStateChange: function(old, current) { var currentEmpty = Ext.isEmpty(current); return Ext.isEmpty(old) ? !currentEmpty : currentEmpty; }, _hasIcon: function() { return !!(this.getIcon() || this.getIconCls() || this.getGlyph()); }, _syncIconVisibility: function() { var me = this, el = me.el, hasIcon = me._hasIcon(), iconWrapEl = me.iconWrapEl, isBefore, iconAlign; if (hasIcon && !iconWrapEl) { // if an icon was configured, but we have not yet rendered an icon // element, we need to render it now. iconAlign = me.iconAlign; isBefore = (iconAlign === 'left' || iconAlign === 'top'); el.dom.insertAdjacentHTML( isBefore ? 'afterbegin' : 'beforeend', me.getIconMarkup() ); iconWrapEl = me.iconWrapEl = el[isBefore ? 'first' : 'last'](); me.iconEl = iconWrapEl.first(); } if (iconWrapEl) { iconWrapEl.setDisplayed(hasIcon); } } }});