]> git.proxmox.com Git - extjs.git/blame - extjs/modern/modern/src/navigation/Bar.js
add extjs 6.0.1 sources
[extjs.git] / extjs / modern / modern / src / navigation / Bar.js
CommitLineData
6527f429
DM
1/**\r
2 * This component is used in {@link Ext.navigation.View} to control animations in the toolbar. You should never need to\r
3 * interact with the component directly, unless you are subclassing it.\r
4 * @private\r
5 */\r
6Ext.define('Ext.navigation.Bar', {\r
7 extend: 'Ext.TitleBar',\r
8\r
9 requires: [\r
10 'Ext.Button',\r
11 'Ext.Spacer'\r
12 ],\r
13\r
14 /**\r
15 * @private\r
16 */\r
17 isToolbar: true,\r
18\r
19 config: {\r
20 /**\r
21 * @cfg\r
22 * @inheritdoc\r
23 */\r
24 baseCls: Ext.baseCSSPrefix + 'toolbar',\r
25\r
26 /**\r
27 * @cfg\r
28 * @inheritdoc\r
29 */\r
30 cls: Ext.baseCSSPrefix + 'navigation-bar',\r
31\r
32 /**\r
33 * @cfg {String} ui\r
34 * Style options for Toolbar. Either 'light' or 'dark'.\r
35 * @accessor\r
36 */\r
37 ui: 'dark',\r
38\r
39 /**\r
40 * @cfg {String} title\r
41 * The title of the toolbar. You should NEVER set this, it is used internally. You set the title of the\r
42 * navigation bar by giving a navigation views children a title configuration.\r
43 * @private\r
44 * @accessor\r
45 */\r
46 title: null,\r
47\r
48 /**\r
49 * @cfg\r
50 * @hide\r
51 * @accessor\r
52 */\r
53 defaultType: 'button',\r
54\r
55 /**\r
56 * @cfg\r
57 * @ignore\r
58 * @accessor\r
59 */\r
60 layout: {\r
61 type: 'hbox'\r
62 },\r
63\r
64 /**\r
65 * @cfg {Array/Object} items The child items to add to this NavigationBar. The {@link #cfg-defaultType} of\r
66 * a NavigationBar is {@link Ext.Button}, so you do not need to specify an `xtype` if you are adding\r
67 * buttons.\r
68 *\r
69 * You can also give items a `align` configuration which will align the item to the `left` or `right` of\r
70 * the NavigationBar.\r
71 * @hide\r
72 * @accessor\r
73 */\r
74\r
75 /**\r
76 * @cfg {String} defaultBackButtonText\r
77 * The text to be displayed on the back button if:\r
78 * a) The previous view does not have a title\r
79 * b) The {@link #useTitleForBackButtonText} configuration is true.\r
80 * @private\r
81 * @accessor\r
82 */\r
83 defaultBackButtonText: 'Back',\r
84\r
85 /**\r
86 * @cfg {Object} animation\r
87 * @private\r
88 * @accessor\r
89 */\r
90 animation: {\r
91 duration: 300\r
92 },\r
93\r
94 /**\r
95 * @cfg {Boolean} useTitleForBackButtonText\r
96 * Set to false if you always want to display the {@link #defaultBackButtonText} as the text\r
97 * on the back button. True if you want to use the previous views title.\r
98 * @private\r
99 * @accessor\r
100 */\r
101 useTitleForBackButtonText: null,\r
102\r
103 /**\r
104 * @cfg {Ext.navigation.View} view A reference to the navigation view this bar is linked to.\r
105 * @private\r
106 * @accessor\r
107 */\r
108 view: null,\r
109\r
110 /**\r
111 * @cfg {Boolean} androidAnimation Optionally enable CSS transforms on Android 2\r
112 * for NavigationBar animations. Note that this may cause flickering if the\r
113 * NavigationBar is hidden.\r
114 * @accessor\r
115 */\r
116 android2Transforms: false,\r
117\r
118 /**\r
119 * @cfg {Ext.Button/Object} backButton The configuration for the back button\r
120 * @private\r
121 * @accessor\r
122 */\r
123 backButton: {\r
124 align: 'left',\r
125 ui: 'back',\r
126 hidden: true\r
127 }\r
128 },\r
129\r
130 /**\r
131 * @event back\r
132 * Fires when the back button was tapped.\r
133 * @param {Ext.navigation.Bar} this This bar\r
134 */\r
135\r
136 constructor: function (config) {\r
137 config = config || {};\r
138\r
139 if (!config.items) {\r
140 config.items = [];\r
141 }\r
142\r
143 this.backButtonStack = [];\r
144 this.activeAnimations = [];\r
145\r
146 this.callParent([config]);\r
147 },\r
148\r
149 /**\r
150 * @private\r
151 */\r
152 applyBackButton: function (config) {\r
153 return Ext.factory(config, Ext.Button, this.getBackButton());\r
154 },\r
155\r
156 /**\r
157 * @private\r
158 */\r
159 updateBackButton: function (newBackButton, oldBackButton) {\r
160 if (oldBackButton) {\r
161 this.remove(oldBackButton);\r
162 }\r
163\r
164 if (newBackButton) {\r
165 this.add(newBackButton);\r
166\r
167 newBackButton.on({\r
168 scope: this,\r
169 tap: this.onBackButtonTap\r
170 });\r
171 }\r
172 },\r
173\r
174 onBackButtonTap: function () {\r
175 this.fireEvent('back', this);\r
176 },\r
177\r
178 /**\r
179 * @private\r
180 */\r
181 updateView: function (newView) {\r
182 var me = this,\r
183 backButton, innerItems, i, backButtonText, item, title, titleText;\r
184\r
185 // Need to have items initialized before getting the backButton\r
186 me.getItems();\r
187 backButton = me.getBackButton();\r
188\r
189 if (newView) {\r
190 //update the back button stack with the current inner items of the view\r
191 innerItems = newView.getInnerItems();\r
192 for (i = 0; i < innerItems.length; i++) {\r
193 item = innerItems[i];\r
194 title = (item.getTitle) ? item.getTitle() : item.config.title;\r
195\r
196 me.backButtonStack.push(title || '&nbsp;');\r
197 }\r
198\r
199 titleText = me.getTitleText();\r
200\r
201 if (titleText === undefined) {\r
202 titleText = '';\r
203 }\r
204\r
205 me.setTitle(titleText);\r
206\r
207 backButtonText = me.getBackButtonText();\r
208 if (backButtonText) {\r
209 backButton.setText(backButtonText);\r
210 backButton.show();\r
211 }\r
212 }\r
213 },\r
214\r
215 /**\r
216 * @private\r
217 */\r
218 onViewAdd: function (view, item) {\r
219 var me = this,\r
220 backButtonStack = me.backButtonStack,\r
221 hasPrevious, title;\r
222\r
223 me.endAnimation();\r
224\r
225 title = (item.getTitle) ? item.getTitle() : item.config.title;\r
226\r
227 backButtonStack.push(title || '&nbsp;');\r
228 hasPrevious = backButtonStack.length > 1;\r
229\r
230 me.doChangeView(view, hasPrevious, false);\r
231 },\r
232\r
233 /**\r
234 * @private\r
235 */\r
236 onViewRemove: function (view) {\r
237 var me = this,\r
238 backButtonStack = me.backButtonStack,\r
239 hasPrevious;\r
240\r
241 me.endAnimation();\r
242 backButtonStack.pop();\r
243 hasPrevious = backButtonStack.length > 1;\r
244\r
245 me.doChangeView(view, hasPrevious, true);\r
246 },\r
247\r
248 /**\r
249 * @private\r
250 */\r
251 doChangeView: function (view, hasPrevious, reverse) {\r
252 var me = this,\r
253 leftBox = me.leftBox,\r
254 leftBoxElement = leftBox.element,\r
255 titleComponent = me.titleComponent,\r
256 titleElement = titleComponent.element,\r
257 backButton = me.getBackButton(),\r
258 titleText = me.getTitleText(),\r
259 backButtonText = me.getBackButtonText(),\r
260 animation = me.getAnimation() && view.getLayout().getAnimation(),\r
261 animated = animation && animation.isAnimation && view.isPainted(),\r
262 properties, leftGhost, titleGhost, leftProps, titleProps;\r
263\r
264 if (animated) {\r
265 leftGhost = me.createProxy(leftBox.element);\r
266 leftBoxElement.setStyle('opacity', '0');\r
267 backButton.setText(backButtonText);\r
268 backButton[hasPrevious ? 'show' : 'hide']();\r
269\r
270 titleGhost = me.createProxy(titleComponent.element.getParent());\r
271 titleElement.setStyle('opacity', '0');\r
272 me.setTitle(titleText);\r
273\r
274 properties = me.measureView(leftGhost, titleGhost, reverse);\r
275 leftProps = properties.left;\r
276 titleProps = properties.title;\r
277\r
278 me.isAnimating = true;\r
279 me.animate(leftBoxElement, leftProps.element);\r
280 me.animate(titleElement, titleProps.element, function () {\r
281 titleElement.setLeft(properties.titleLeft);\r
282 me.isAnimating = false;\r
283 me.refreshTitlePosition();\r
284 });\r
285\r
286 me.animate(leftGhost.ghost, leftProps.ghost);\r
287 me.animate(titleGhost.ghost, titleProps.ghost, function () {\r
288 leftGhost.ghost.destroy();\r
289 titleGhost.ghost.destroy();\r
290 });\r
291 }\r
292 else {\r
293 if (hasPrevious) {\r
294 backButton.setText(backButtonText);\r
295 backButton.show();\r
296 }\r
297 else {\r
298 backButton.hide();\r
299 }\r
300 me.setTitle(titleText);\r
301 }\r
302 },\r
303\r
304 /**\r
305 * Calculates and returns the position values needed for the back button when you are pushing a title.\r
306 * @private\r
307 */\r
308 measureView: function (oldLeft, oldTitle, reverse) {\r
309 var me = this,\r
310 barElement = me.element,\r
311 newLeftElement = me.leftBox.element,\r
312 titleElement = me.titleComponent.element,\r
313 minOffset = Math.min(barElement.getWidth() / 3, 200),\r
314 newLeftWidth = newLeftElement.getWidth(),\r
315 barX = barElement.getX(),\r
316 barWidth = barElement.getWidth(),\r
317 titleX = titleElement.getX(),\r
318 titleLeft = titleElement.getLeft(true),\r
319 titleWidth = titleElement.getWidth(),\r
320 oldLeftX = oldLeft.x,\r
321 oldLeftWidth = oldLeft.width,\r
322 oldLeftLeft = oldLeft.left,\r
323 newOffset, oldOffset, leftAnims, titleAnims, omega, theta;\r
324\r
325 theta = barX - oldLeftX - oldLeftWidth;\r
326 if (reverse) {\r
327 newOffset = theta;\r
328 oldOffset = Math.min(titleX - oldLeftWidth, minOffset);\r
329 } else {\r
330 oldOffset = theta;\r
331 newOffset = Math.min(titleX - barX, minOffset);\r
332 }\r
333\r
334 leftAnims = {\r
335 element: {\r
336 from: {\r
337 transform: {\r
338 translateX: newOffset\r
339 },\r
340 opacity: 0\r
341 },\r
342 to: {\r
343 transform: {\r
344 translateX: 0\r
345 },\r
346 opacity: 1\r
347 }\r
348 },\r
349 ghost: {\r
350 to: {\r
351 transform: {\r
352 translateX: oldOffset\r
353 },\r
354 opacity: 0\r
355 }\r
356 }\r
357 };\r
358\r
359 theta = barX - titleX + newLeftWidth;\r
360 if ((oldLeftLeft + titleWidth) > titleX) {\r
361 omega = barX - titleX - titleWidth;\r
362 }\r
363\r
364 if (reverse) {\r
365 titleElement.setLeft(0);\r
366\r
367 oldOffset = barX + barWidth - titleX - titleWidth;\r
368\r
369 if (omega !== undefined) {\r
370 newOffset = omega;\r
371 } else {\r
372 newOffset = theta;\r
373 }\r
374 } else {\r
375 newOffset = barX + barWidth - titleX - titleWidth;\r
376\r
377 if (omega !== undefined) {\r
378 oldOffset = omega;\r
379 } else {\r
380 oldOffset = theta;\r
381 }\r
382\r
383 newOffset = Math.max(titleLeft, newOffset);\r
384 }\r
385\r
386 titleAnims = {\r
387 element: {\r
388 from: {\r
389 transform: {\r
390 translateX: newOffset\r
391 },\r
392 opacity: 0\r
393 },\r
394 to: {\r
395 transform: {\r
396 translateX: titleLeft\r
397 },\r
398 opacity: 1\r
399 }\r
400 },\r
401 ghost: {\r
402 to: {\r
403 transform: {\r
404 translateX: oldOffset\r
405 },\r
406 opacity: 0\r
407 }\r
408 }\r
409 };\r
410\r
411 return {\r
412 left: leftAnims,\r
413 title: titleAnims,\r
414 titleLeft: titleLeft\r
415 };\r
416 },\r
417\r
418 /**\r
419 * Helper method used to animate elements.\r
420 * You pass it an element, objects for the from and to positions an option onEnd callback called when the animation is over.\r
421 * Normally this method is passed configurations returned from the methods such as #measureTitle(true) etc.\r
422 * It is called from the #pushLeftBoxAnimated, #pushTitleAnimated, #popBackButtonAnimated and #popTitleAnimated\r
423 * methods.\r
424 *\r
425 * If the current device is Android, it will use top/left to animate.\r
426 * If it is anything else, it will use transform.\r
427 * @private\r
428 */\r
429 animate: function (element, config, callback) {\r
430 var me = this,\r
431 animation;\r
432\r
433 //reset the left of the element\r
434 element.setLeft(0);\r
435\r
436 config = Ext.apply(config, {\r
437 element: element,\r
438 easing: 'ease-in-out',\r
439 duration: me.getAnimation().duration || 250,\r
440 preserveEndState: true\r
441 });\r
442\r
443 animation = new Ext.fx.Animation(config);\r
444 animation.on('animationend', function () {\r
445 if (callback) {\r
446 callback.call(me);\r
447 }\r
448 }, me);\r
449\r
450 Ext.Animator.run(animation);\r
451 me.activeAnimations.push(animation);\r
452 },\r
453\r
454 endAnimation: function () {\r
455 var activeAnimations = this.activeAnimations,\r
456 animation, i, ln;\r
457\r
458 if (activeAnimations) {\r
459 ln = activeAnimations.length;\r
460 for (i = 0; i < ln; i++) {\r
461 animation = activeAnimations[i];\r
462 if (animation.isAnimating) {\r
463 animation.stopAnimation();\r
464 }\r
465 else {\r
466 animation.destroy();\r
467 }\r
468 }\r
469 this.activeAnimations = [];\r
470 }\r
471 },\r
472\r
473 refreshTitlePosition: function () {\r
474 if (!this.isAnimating) {\r
475 this.callParent();\r
476 }\r
477 },\r
478\r
479 /**\r
480 * Returns the text needed for the current back button at anytime.\r
481 * @private\r
482 */\r
483 getBackButtonText: function () {\r
484 var text = this.backButtonStack[this.backButtonStack.length - 2],\r
485 useTitleForBackButtonText = this.getUseTitleForBackButtonText();\r
486\r
487 if (!useTitleForBackButtonText) {\r
488 if (text) {\r
489 text = this.getDefaultBackButtonText();\r
490 }\r
491 }\r
492\r
493 return text;\r
494 },\r
495\r
496 /**\r
497 * Returns the text needed for the current title at anytime.\r
498 * @private\r
499 */\r
500 getTitleText: function () {\r
501 return this.backButtonStack[this.backButtonStack.length - 1];\r
502 },\r
503\r
504 /**\r
505 * Handles removing back button stacks from this bar\r
506 * @private\r
507 */\r
508 beforePop: function (count) {\r
509 count--;\r
510 for (var i = 0; i < count; i++) {\r
511 this.backButtonStack.pop();\r
512 }\r
513 },\r
514\r
515 /**\r
516 * We override the hidden method because we don't want to remove it from the view using display:none. Instead we just position it off\r
517 * the screen, much like the navigation bar proxy. This means that all animations, pushing, popping etc. all still work when if you hide/show\r
518 * this bar at any time.\r
519 * @private\r
520 */\r
521 updateHidden: function (hidden) {\r
522 if (!hidden) {\r
523 this.element.setStyle({\r
524 position: 'relative',\r
525 top: 'auto',\r
526 left: 'auto',\r
527 width: 'auto'\r
528 });\r
529 } else {\r
530 this.element.setStyle({\r
531 position: 'absolute',\r
532 top: '-1000px',\r
533 left: '-1000px',\r
534 width: this.element.getWidth() + 'px'\r
535 });\r
536 }\r
537 },\r
538\r
539 /**\r
540 * Creates a proxy element of the passed element, and positions it in the same position, using absolute positioning.\r
541 * The createNavigationBarProxy method uses this to create proxies of the backButton and the title elements.\r
542 * @private\r
543 */\r
544 createProxy: function (element) {\r
545 var ghost, x, y, left, width;\r
546\r
547 ghost = element.dom.cloneNode(true);\r
548 ghost.id = element.id + '-proxy';\r
549\r
550 //insert it into the toolbar\r
551 element.getParent().dom.appendChild(ghost);\r
552\r
553 //set the x/y\r
554 ghost = Ext.get(ghost);\r
555 x = element.getX();\r
556 y = element.getY();\r
557 left = element.getLeft(true);\r
558 width = element.getWidth();\r
559 ghost.setStyle('position', 'absolute');\r
560 ghost.setX(x);\r
561 ghost.setY(y);\r
562 ghost.setHeight(element.getHeight());\r
563 ghost.setWidth(width);\r
564\r
565 return {\r
566 x: x,\r
567 y: y,\r
568 left: left,\r
569 width: width,\r
570 ghost: ghost\r
571 };\r
572 }\r
573});\r