]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * A basic title component for a Panel Header\r | |
3 | */\r | |
4 | Ext.define('Ext.panel.Title', {\r | |
5 | extend: 'Ext.Component',\r | |
6 | xtype: 'title',\r | |
7 | \r | |
8 | isTitle: true,\r | |
9 | \r | |
10 | // layout system optimization. Allows autocomponent layout to measure height without\r | |
11 | // having to first know the width.\r | |
12 | noWrap: true,\r | |
13 | \r | |
14 | // For performance reasons we give the following configs their default values on\r | |
15 | // the class body. This prevents the updaters from running on initialization in the\r | |
16 | // default configuration scenario\r | |
17 | textAlign: 'left',\r | |
18 | iconAlign: 'left',\r | |
19 | rotation: 0,\r | |
20 | text: ' ',\r | |
21 | \r | |
22 | beforeRenderConfig: {\r | |
23 | /**\r | |
24 | * @cfg [textAlign='left']\r | |
25 | * @inheritdoc Ext.panel.Header#cfg-titleAlign\r | |
26 | * @accessor\r | |
27 | */\r | |
28 | textAlign: null,\r | |
29 | \r | |
30 | /**\r | |
31 | * @cfg {String}\r | |
32 | * The title's text (can contain html tags/entities)\r | |
33 | * @accessor\r | |
34 | */\r | |
35 | text: null,\r | |
36 | \r | |
37 | /**\r | |
38 | * @cfg glyph\r | |
39 | * @inheritdoc Ext.panel.Header#cfg-glyph\r | |
40 | * @accessor\r | |
41 | */\r | |
42 | glyph: null,\r | |
43 | \r | |
44 | /**\r | |
45 | * @cfg icon\r | |
46 | * @inheritdoc Ext.panel.Header#cfg-icon\r | |
47 | * @accessor\r | |
48 | */\r | |
49 | icon: null,\r | |
50 | \r | |
51 | /**\r | |
52 | * @cfg {'top'/'right'/'bottom'/'left'} [iconAlign='left']\r | |
53 | * alignment of the icon\r | |
54 | * @accessor\r | |
55 | */\r | |
56 | iconAlign: null,\r | |
57 | \r | |
58 | /**\r | |
59 | * @cfg iconCls\r | |
60 | * @inheritdoc Ext.panel.Header#cfg-iconCls\r | |
61 | * @accessor\r | |
62 | */\r | |
63 | iconCls: null,\r | |
64 | \r | |
65 | /**\r | |
66 | * @cfg rotation\r | |
67 | * @inheritdoc Ext.panel.Header#cfg-titleRotation\r | |
68 | * @accessor\r | |
69 | */\r | |
70 | rotation: null\r | |
71 | },\r | |
72 | \r | |
73 | autoEl: {\r | |
74 | role: 'presentation',\r | |
75 | // Required for Opera\r | |
76 | unselectable: 'on'\r | |
77 | },\r | |
78 | \r | |
79 | // In most cases the panel header title is purely presentational\r | |
80 | // and does not have any structural significance wrt Assistive Technologies.\r | |
81 | // The only exception is when the panel participates in Accordion layout;\r | |
82 | // in that case the title component has the role of 'tab' and its textEl\r | |
83 | // should not have any role to expose the text as the tab's accessible name.\r | |
84 | // Header component is aware of this participation and will reset textElRole.\r | |
85 | textElRole: 'presentation',\r | |
86 | \r | |
87 | // By default, panel title is not focusable; this only happens in Accordion layout.\r | |
88 | // This config option is overridable, and it will prime tabIndex to be used\r | |
89 | // without hardcoding it.\r | |
90 | tabIndex: 0,\r | |
91 | \r | |
92 | childEls: [\r | |
93 | 'textEl',\r | |
94 | 'iconEl',\r | |
95 | 'iconWrapEl'\r | |
96 | ],\r | |
97 | \r | |
98 | renderTpl:\r | |
99 | '<tpl if="iconMarkup && iconBeforeTitle">{iconMarkup}</tpl>' +\r | |
100 | // unselectable="on" is required for Opera, other browsers\r | |
101 | // inherit unselectability from the header\r | |
102 | '<div id="{id}-textEl" data-ref="textEl"' +\r | |
103 | ' class="{textCls} {textCls}-{ui} {itemCls}{childElCls}" unselectable="on"' +\r | |
104 | '<tpl if="textElRole"> role="{textElRole}"</tpl>' +\r | |
105 | '>' +\r | |
106 | '{text}' +\r | |
107 | '</div>' +\r | |
108 | '<tpl if="iconMarkup && !iconBeforeTitle">{iconMarkup}</tpl>',\r | |
109 | \r | |
110 | iconTpl:\r | |
111 | '<div id="{id}-iconWrapEl" data-ref="iconWrapEl" role="presentation" ' +\r | |
112 | 'class="{iconWrapCls} {iconWrapCls}-{ui} {iconAlignCls} {itemCls}{childElCls}"' +\r | |
113 | '<tpl if="iconWrapStyle"> style="{iconWrapStyle}"</tpl>>' +\r | |
114 | '<div id="{id}-iconEl" data-ref="iconEl" role="presentation" unselectable="on" ' +\r | |
115 | 'class="{baseIconCls} {baseIconCls}-{ui} {iconCls} {glyphCls}" style="' +\r | |
116 | '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>' +\r | |
117 | '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">' +\r | |
118 | '<tpl if="glyph">&#{glyph};</tpl>' +\r | |
119 | '</div>' +\r | |
120 | '</div>',\r | |
121 | \r | |
122 | _textAlignClasses: {\r | |
123 | left: Ext.baseCSSPrefix + 'title-align-left',\r | |
124 | center: Ext.baseCSSPrefix + 'title-align-center',\r | |
125 | right: Ext.baseCSSPrefix + 'title-align-right'\r | |
126 | },\r | |
127 | \r | |
128 | _iconAlignClasses: {\r | |
129 | top: Ext.baseCSSPrefix + 'title-icon-top',\r | |
130 | right: Ext.baseCSSPrefix + 'title-icon-right',\r | |
131 | bottom: Ext.baseCSSPrefix + 'title-icon-bottom',\r | |
132 | left: Ext.baseCSSPrefix + 'title-icon-left'\r | |
133 | },\r | |
134 | \r | |
135 | _rotationClasses: {\r | |
136 | 0: Ext.baseCSSPrefix + 'title-rotate-none',\r | |
137 | 1: Ext.baseCSSPrefix + 'title-rotate-right',\r | |
138 | 2: Ext.baseCSSPrefix + 'title-rotate-left'\r | |
139 | },\r | |
140 | \r | |
141 | _rotationAngles: {\r | |
142 | 1: 90,\r | |
143 | 2: 270\r | |
144 | },\r | |
145 | \r | |
146 | baseCls: Ext.baseCSSPrefix + 'title',\r | |
147 | _titleSuffix: '-title',\r | |
148 | _glyphCls: Ext.baseCSSPrefix + 'title-glyph',\r | |
149 | _iconWrapCls: Ext.baseCSSPrefix + 'title-icon-wrap',\r | |
150 | _baseIconCls: Ext.baseCSSPrefix + 'title-icon',\r | |
151 | _itemCls: Ext.baseCSSPrefix + 'title-item',\r | |
152 | _textCls: Ext.baseCSSPrefix + 'title-text',\r | |
153 | \r | |
154 | afterComponentLayout: function() {\r | |
155 | var me = this,\r | |
156 | rotation = me.getRotation(),\r | |
157 | lastBox, lastX, el;\r | |
158 | \r | |
159 | if (rotation && !Ext.isIE8) {\r | |
160 | // In IE8 we use a BasicImage filter to rotate the title\r | |
161 | // element 90 degrees. The result is that what was the bottom left\r | |
162 | // corner is positioned exactly where the top left corner was\r | |
163 | // originally. Since this is the desired result, no additional\r | |
164 | // positioning is needed in IE8. In browsers that support CSS3 transform,\r | |
165 | // however, we use transform: rotate(90deg) to rotate the element.\r | |
166 | // CSS3 also provides a way to specify the position the rotated element\r | |
167 | // by changing the axis on which it is rotated using the transform-origin\r | |
168 | // property, but the required transform origin varies based on the\r | |
169 | // elements size, and would require some complex math to calculate.\r | |
170 | // To achieve the desired rotated position in modern browsers we use\r | |
171 | // a transform-origin of "0, 0" which means the top left corner of\r | |
172 | // the element is the rotation axis. After rotating 90 degrees we\r | |
173 | // simply move the element to the right by the same number of pixels\r | |
174 | // as its width.\r | |
175 | el = me.el;\r | |
176 | lastBox = me.lastBox;\r | |
177 | lastX = lastBox.x;\r | |
178 | el.setStyle(\r | |
179 | me._getVerticalAdjustDirection(),\r | |
180 | (lastX + ((rotation === 1) ? lastBox.width : -lastBox.height)) + 'px'\r | |
181 | );\r | |
182 | }\r | |
183 | this.callParent();\r | |
184 | },\r | |
185 | \r | |
186 | onRender: function() {\r | |
187 | var me = this,\r | |
188 | rotation = me.getRotation(),\r | |
189 | el = me.el;\r | |
190 | \r | |
191 | me.callParent();\r | |
192 | \r | |
193 | if (rotation) {\r | |
194 | el.setVertical(me._rotationAngles[rotation]);\r | |
195 | }\r | |
196 | \r | |
197 | if (Ext.supports.FixedTableWidthBug) {\r | |
198 | // Workaround for https://bugs.webkit.org/show_bug.cgi?id=130239 and\r | |
199 | // https://code.google.com/p/chromium/issues/detail?id=377190\r | |
200 | // See styleHooks for more details\r | |
201 | el._needsTableWidthFix = true;\r | |
202 | }\r | |
203 | },\r | |
204 | \r | |
205 | applyText: function(text) {\r | |
206 | if (!text) {\r | |
207 | text = ' ';\r | |
208 | }\r | |
209 | return text;\r | |
210 | },\r | |
211 | \r | |
212 | beforeRender: function() {\r | |
213 | var me = this;\r | |
214 | \r | |
215 | me.callParent();\r | |
216 | \r | |
217 | me.addCls(me._rotationClasses[me.getRotation()]);\r | |
218 | me.addCls(me._textAlignClasses[me.getTextAlign()]);\r | |
219 | },\r | |
220 | \r | |
221 | getIconMarkup: function() {\r | |
222 | return this.getTpl('iconTpl').apply(this.getIconRenderData());\r | |
223 | },\r | |
224 | \r | |
225 | getIconRenderData: function() {\r | |
226 | var me = this,\r | |
227 | icon = me.getIcon(),\r | |
228 | iconCls = me.getIconCls(),\r | |
229 | glyph = me.getGlyph(),\r | |
230 | glyphFontFamily = Ext._glyphFontFamily,\r | |
231 | iconAlign = me.getIconAlign(),\r | |
232 | glyphParts;\r | |
233 | \r | |
234 | \r | |
235 | if (typeof glyph === 'string') {\r | |
236 | glyphParts = glyph.split('@');\r | |
237 | glyph = glyphParts[0];\r | |
238 | glyphFontFamily = glyphParts[1];\r | |
239 | }\r | |
240 | \r | |
241 | return {\r | |
242 | id: me.id,\r | |
243 | ui: me.ui,\r | |
244 | itemCls: me._itemCls,\r | |
245 | iconUrl: icon,\r | |
246 | iconCls: iconCls,\r | |
247 | iconWrapCls: me._iconWrapCls,\r | |
248 | baseIconCls: me._baseIconCls,\r | |
249 | iconAlignCls: me._iconAlignClasses[iconAlign],\r | |
250 | glyph: glyph,\r | |
251 | glyphCls: glyph ? me._glyphCls : '',\r | |
252 | glyphFontFamily: glyphFontFamily\r | |
253 | };\r | |
254 | },\r | |
255 | \r | |
256 | initRenderData: function() {\r | |
257 | var me = this,\r | |
258 | iconAlign, renderData;\r | |
259 | \r | |
260 | renderData = Ext.apply({\r | |
261 | text: me.getText(),\r | |
262 | textElRole: me.textElRole,\r | |
263 | id: me.id,\r | |
264 | ui: me.ui,\r | |
265 | itemCls: me._itemCls,\r | |
266 | textCls: me._textCls,\r | |
267 | iconMarkup: null,\r | |
268 | iconBeforeTitle: null\r | |
269 | }, me.callParent());\r | |
270 | \r | |
271 | if (me._hasIcon()) {\r | |
272 | iconAlign = me.getIconAlign();\r | |
273 | renderData.iconMarkup = me.getIconMarkup();\r | |
274 | renderData.iconBeforeTitle = (iconAlign === 'top' || iconAlign === 'left');\r | |
275 | }\r | |
276 | \r | |
277 | return renderData;\r | |
278 | },\r | |
279 | \r | |
280 | onAdded: function(container, pos, instanced) {\r | |
281 | var me = this,\r | |
282 | suffix = me._titleSuffix,\r | |
283 | baseCls = container.baseCls;\r | |
284 | \r | |
285 | me.addCls([\r | |
286 | baseCls + suffix,\r | |
287 | baseCls + suffix + '-' + container.ui\r | |
288 | ]);\r | |
289 | \r | |
290 | me.callParent([container, pos, instanced]);\r | |
291 | },\r | |
292 | \r | |
293 | updateGlyph: function(glyph, oldGlyph) {\r | |
294 | glyph = glyph || 0;\r | |
295 | var me = this,\r | |
296 | glyphCls = me._glyphCls,\r | |
297 | iconEl, fontFamily, glyphParts;\r | |
298 | \r | |
299 | me.glyph = glyph;\r | |
300 | \r | |
301 | if (me.rendered) {\r | |
302 | me._syncIconVisibility();\r | |
303 | iconEl = me.iconEl;\r | |
304 | \r | |
305 | if (typeof glyph === 'string') {\r | |
306 | glyphParts = glyph.split('@');\r | |
307 | glyph = glyphParts[0];\r | |
308 | fontFamily = glyphParts[1] || Ext._glyphFontFamily;\r | |
309 | }\r | |
310 | \r | |
311 | if (!glyph) {\r | |
312 | iconEl.dom.innerHTML = '';\r | |
313 | iconEl.removeCls(glyphCls);\r | |
314 | } else if (oldGlyph !== glyph) {\r | |
315 | iconEl.dom.innerHTML = '&#' + glyph + ';';\r | |
316 | iconEl.addCls(glyphCls);\r | |
317 | }\r | |
318 | \r | |
319 | if (fontFamily) {\r | |
320 | iconEl.setStyle('font-family', fontFamily);\r | |
321 | }\r | |
322 | if (me._didIconStateChange(oldGlyph, glyph)) {\r | |
323 | me.updateLayout();\r | |
324 | }\r | |
325 | }\r | |
326 | },\r | |
327 | \r | |
328 | updateIcon: function(icon, oldIcon) {\r | |
329 | icon = icon || '';\r | |
330 | var me = this,\r | |
331 | iconEl;\r | |
332 | \r | |
333 | if (me.rendered && icon !== oldIcon) {\r | |
334 | me._syncIconVisibility();\r | |
335 | iconEl = me.iconEl;\r | |
336 | \r | |
337 | iconEl.setStyle('background-image', icon ? 'url(' + icon + ')': '');\r | |
338 | if (me._didIconStateChange(oldIcon, icon)) {\r | |
339 | me.updateLayout();\r | |
340 | }\r | |
341 | }\r | |
342 | },\r | |
343 | \r | |
344 | updateIconAlign: function(align, oldAlign) {\r | |
345 | var me = this,\r | |
346 | iconWrapEl = me.iconWrapEl,\r | |
347 | el, iconAlignClasses;\r | |
348 | \r | |
349 | if (me.iconWrapEl) {\r | |
350 | el = me.el;\r | |
351 | iconAlignClasses = me._iconAlignClasses;\r | |
352 | \r | |
353 | if (oldAlign) {\r | |
354 | iconWrapEl.removeCls(iconAlignClasses[oldAlign]);\r | |
355 | }\r | |
356 | iconWrapEl.addCls(iconAlignClasses[align]);\r | |
357 | \r | |
358 | // here we move the iconWrap to the correct position in the dom - before the\r | |
359 | // title el for top/left alignments, and after the title el for right/bottom\r | |
360 | if (align === 'top' || align === 'left') {\r | |
361 | el.insertFirst(iconWrapEl);\r | |
362 | } else {\r | |
363 | el.appendChild(iconWrapEl);\r | |
364 | }\r | |
365 | \r | |
366 | me.updateLayout();\r | |
367 | }\r | |
368 | },\r | |
369 | \r | |
370 | updateIconCls: function(cls, oldCls) {\r | |
371 | cls = cls || '';\r | |
372 | var me = this,\r | |
373 | iconEl;\r | |
374 | \r | |
375 | if (me.rendered && oldCls !== cls) {\r | |
376 | me._syncIconVisibility();\r | |
377 | iconEl = me.iconEl;\r | |
378 | \r | |
379 | if (oldCls) {\r | |
380 | iconEl.removeCls(oldCls);\r | |
381 | }\r | |
382 | iconEl.addCls(cls);\r | |
383 | if (me._didIconStateChange(oldCls, cls)) {\r | |
384 | me.updateLayout();\r | |
385 | }\r | |
386 | }\r | |
387 | },\r | |
388 | \r | |
389 | updateRotation: function(rotation, oldRotation) {\r | |
390 | var me = this,\r | |
391 | el, rotationClasses;\r | |
392 | \r | |
393 | if (me.rendered) {\r | |
394 | el = me.el;\r | |
395 | rotationClasses = me._rotationClasses;\r | |
396 | \r | |
397 | me.removeCls(rotationClasses[oldRotation]);\r | |
398 | me.addCls(rotationClasses[rotation]);\r | |
399 | \r | |
400 | el.setHorizontal();\r | |
401 | if (rotation) {\r | |
402 | el.setVertical(me._rotationAngles[rotation]);\r | |
403 | }\r | |
404 | \r | |
405 | // reset styles set by adjustTitlePosition (handles both rtl/ltr), and sizing\r | |
406 | // set by last layout run (this prevents parallel size from becoming perpendicular\r | |
407 | // size after rotation)\r | |
408 | el.setStyle({\r | |
409 | right: '',\r | |
410 | left: '',\r | |
411 | top: '',\r | |
412 | height: '',\r | |
413 | width: ''\r | |
414 | });\r | |
415 | \r | |
416 | me.lastBox = null;\r | |
417 | \r | |
418 | me.updateLayout();\r | |
419 | }\r | |
420 | },\r | |
421 | \r | |
422 | updateText: function(text) {\r | |
423 | if (this.rendered) {\r | |
424 | this.textEl.setHtml(text);\r | |
425 | this.updateLayout();\r | |
426 | }\r | |
427 | },\r | |
428 | \r | |
429 | updateTextAlign: function(align, oldAlign) {\r | |
430 | var me = this,\r | |
431 | textAlignClasses = me._textAlignClasses;\r | |
432 | \r | |
433 | if (me.rendered) {\r | |
434 | if (oldAlign) {\r | |
435 | me.removeCls(textAlignClasses[oldAlign]);\r | |
436 | }\r | |
437 | me.addCls(textAlignClasses[align]);\r | |
438 | \r | |
439 | me.updateLayout();\r | |
440 | }\r | |
441 | },\r | |
442 | \r | |
443 | privates: {\r | |
444 | // rtl hook\r | |
445 | _getVerticalAdjustDirection: function() {\r | |
446 | return 'left';\r | |
447 | },\r | |
448 | \r | |
449 | _didIconStateChange: function(old, current) {\r | |
450 | var currentEmpty = Ext.isEmpty(current);\r | |
451 | return Ext.isEmpty(old) ? !currentEmpty : currentEmpty;\r | |
452 | },\r | |
453 | \r | |
454 | _hasIcon: function() {\r | |
455 | return !!(this.getIcon() || this.getIconCls() || this.getGlyph());\r | |
456 | },\r | |
457 | \r | |
458 | _syncIconVisibility: function() {\r | |
459 | var me = this,\r | |
460 | el = me.el,\r | |
461 | hasIcon = me._hasIcon(),\r | |
462 | iconWrapEl = me.iconWrapEl,\r | |
463 | isBefore, iconAlign;\r | |
464 | \r | |
465 | if (hasIcon && !iconWrapEl) {\r | |
466 | // if an icon was configured, but we have not yet rendered an icon\r | |
467 | // element, we need to render it now.\r | |
468 | iconAlign = me.iconAlign;\r | |
469 | isBefore = (iconAlign === 'left' || iconAlign === 'top');\r | |
470 | \r | |
471 | el.dom.insertAdjacentHTML(\r | |
472 | isBefore ? 'afterbegin' : 'beforeend',\r | |
473 | me.getIconMarkup()\r | |
474 | );\r | |
475 | \r | |
476 | iconWrapEl = me.iconWrapEl = el[isBefore ? 'first' : 'last']();\r | |
477 | me.iconEl = iconWrapEl.first();\r | |
478 | }\r | |
479 | \r | |
480 | if (iconWrapEl) {\r | |
481 | iconWrapEl.setDisplayed(hasIcon);\r | |
482 | }\r | |
483 | }\r | |
484 | }\r | |
485 | });\r |