/**
* 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) {
var me = this,
iconEl;
icon = icon || '';
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) {
var me = this,
iconEl;
cls = cls || '';
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);
}
}
}
});