Polymer elements added to third_party/polymer.
[chromium-blink-merge.git] / third_party / polymer / components / core-animated-pages / core-animated-pages.html
blobbef086d58a753c1da031779f7e81f8e93a7651f7
1 <link href="../core-selector/core-selector.html" rel="import">
3 <link href="transitions/hero-transition.html" rel="import">
4 <link href="transitions/cross-fade.html" rel="import">
6 <!--
8 `core-animated-pages` selects one of its children "pages" to show and runs a transition
9 when switching between them. The transitions are designed to be pluggable, and can
10 accept any object that is an instance of a `core-transition-pages`. Transitions to run
11 are specified in the `transitions` attribute as a space-delimited string of `id`s of
12 transition elements. Several transitions are available with `core-animated-pages` by
13 default, including `hero-transition`, `cross-fade`, and `tile-cascade`.
15 Example:
17 <style>
18 #hero1 {
19 position: absolute;
20 top: 0;
21 left: 0;
22 width: 300px;
23 height: 300px;
24 background-color: orange;
26 #hero2 {
27 position: absolute;
28 top: 200px;
29 left: 300px;
30 width: 300px;
31 height: 300px;
32 background-color: orange;
34 #bottom1, #bottom2 {
35 position: absolute;
36 bottom: 0;
37 top: 0;
38 left: 0;
39 height: 50px;
41 #bottom1 {
42 background-color: blue;
44 #bottom2 {
45 background-color: green;
47 </style>
48 // hero-transition and cross-fade are declared elsewhere
49 <core-animated-pages transitions="hero-transition cross-fade">
50 <section id="page1">
51 <div id="hero1" hero-id="hero" hero></div>
52 <div id="bottom1" cross-fade></div>
53 </section>
54 <section id="page2">
55 <div id="hero2" hero-id="hero" hero></div>
56 <div id="bottom2" cross-fade></div>
57 </section>
58 </core-animated-pages>
60 In the above example, two transitions (`hero-transition` and `cross-fade`) are run when switching
61 between `page1` and `page2`. `hero-transition` transforms elements with the same `hero-id` such
62 that they appear to be shared across different pages. `cross-fade` fades out the elements marked
63 `cross-fade` in the outgoing page, and fades in those in the incoming page. See the individual
64 transition's documentation for specific details.
66 Finding elements to transition
67 ------------------------------
69 In general, a transition is applied to elements marked with a certain attribute. For example,
70 `hero-transition` applies the transition on elements with the `hero` and `hero-id` attribute.
71 Among the transitions included with `core-animated-pages`, script-based transitions such as
72 `hero-transition` generally look for elements up to one level of shadowRoot from the
73 `core-animated-pages` element, and CSS-based transitions such as `cross-fade` look for elements
74 within any shadowRoot within the `core-animated-pages` element. This means you can use
75 custom elements as pages and mark elements in their shadowRoots as heroes, or mark
76 elements in deeper shadowRoots with other transitions.
78 Example:
80 <polymer-element name="x-el" noscript>
81 <template>
82 <style>
83 #hero {
84 position: absolute;
85 top: 0;
86 right: 0;
87 width: 50px;
88 height: 300px;
89 background-color: blue;
91 </style>
92 <div id="hero" hero-id="bar" hero></div>
93 </template>
94 </polymer-element>
96 <polymer-element name="x-page-1" noscript>
97 <template>
98 <style>
99 #hero1 {
100 position: absolute;
101 top: 0;
102 left: 0;
103 width: 300px;
104 height: 300px;
105 background-color: orange;
107 </style>
108 <div id="hero1" hero-id="foo" hero></div>
109 <div id="hero2" hero-id="bar" hero></div>
110 </template>
111 </polymer-element>
113 <polymer-element name="x-page-2" noscript>
114 <template>
115 <style>
116 #hero1 {
117 position: absolute;
118 top: 200px;
119 left: 300px;
120 width: 300px;
121 height: 300px;
122 background-color: orange;
124 #hero2 {
125 background-color: blue;
126 height: 150px;
127 width: 400px;
129 </style>
130 // The below element is one level of shadow from the core-animated-pages and will
131 // be transitioned.
132 <div id="hero1" hero-id="foo" hero></div>
133 // The below element contains a hero inside its shadowRoot making it two levels away
134 // from the core-animated-pages, and will not be transitioned.
135 <x-el></x-el>
136 </template>
137 </polymer-element>
139 <core-animated-pages transitions="hero-transition">
140 <x-page-1></x-page-1>
141 <x-page-2></x-page-2>
142 </core-animated-pages>
144 Note that the container element of the page does not participate in the transition.
146 // This does not work
147 <core-animated-pages transitions="cross-fade">
148 <section cross-fade></section>
149 <section cross-fade></section>
150 </core-animated-pages>
152 // This works
153 <core-animated-pages transitions="cross-fade">
154 <section>
155 <div cross-fade></div>
156 </section>
157 <section>
158 <div cross-fade></div>
159 </section>
160 </core-animated-pages>
162 Dynamically setting up transitions
163 ----------------------------------
165 An easy way to dynamically set up transitions dynamically is to use property binding on
166 the transition attributes.
168 Example:
170 <core-animated-pages selected="{{selected}}">
171 <section id="page1">
172 <div hero-id="hero" hero></div>
173 </section>
174 <section id="page2">
175 <div id="foo" hero-id="hero" hero?="{{selected === 1 || selected === 0}}" cross-fade="{{selected === 2}}"></div>
176 </section>
177 <section id="page3">
178 </section>
179 </core-animated-pages>
181 In the above example, the "foo" element only behaves as a hero element if transitioning between
182 `#page1` and `#page2`. It gets cross-faded when transition to or from `#page3`.
184 Nesting pages
185 -------------
187 It is possible to nest core-animated-pages elements for organization. Excessive nesting is
188 not encouraged, however, since it makes setting up the transition more complex.
190 To nest core-animated-pages, the page containing the nested core-animated-pages element should
191 have a `selectedItem` property bound to the `selectedItem` property of the nested element. This
192 will allow the outer core-animated-pages to know which nested page it is actually transitioning
195 Example:
197 <polymer-element name="nested-page" attributes="selectedItem">
198 <template>
199 <core-animated-pages selectedItem="{{selectedItem}}">
201 </core-animated-pages>
202 </template>
203 </polymer-element>
205 <core-animated-pages>
206 <section id="page1"></section>
207 <nested-page id="page2"></section>
208 </core-animated-pages>
210 @element core-animated-pages
211 @extends core-selector
212 @status beta
213 @homepage github.io
215 <!--
216 Fired before a page transition occurs. Both pages involved in the transition are visible when
217 this event fires. This is useful if there is something the client needs to do when a page becomes
218 visible.
220 @event core-animated-pages-transition-prepare
222 <!--
223 Fired when a page transition completes.
225 @event core-animated-pages-transition-end
227 <polymer-element name="core-animated-pages" extends="core-selector" notap attributes="transitions">
229 <template>
231 <link href="core-animated-pages.css" rel="stylesheet">
233 <shadow></shadow>
235 </template>
237 <script>
239 Polymer({
241 eventDelegates: {
242 'core-transitionend': 'transitionEnd'
246 * A space-delimited string of transitions to use when switching between pages in this element.
247 * The strings are `id`s of `core-transition-pages` elements included elsewhere. See the
248 * individual transition's document for specific details.
250 * @attribute transitions
251 * @type string
252 * @default ''
254 transitions: '',
256 selected: 0,
259 * The last page selected. This property is useful to dynamically set transitions based
260 * on incoming and outgoing pages.
262 * @attribute lastSelected
263 * @type Object
264 * @default null
266 lastSelected: null,
268 registerCallback: function() {
269 this.tmeta = document.createElement('core-transition');
272 created: function() {
273 this._transitions = [];
274 this.transitioning = [];
277 transitionsChanged: function() {
278 this._transitions = this.transitions.split(' ');
281 _transitionsChanged: function(old) {
282 if (this._transitionElements) {
283 this._transitionElements.forEach(function(t) {
284 t.teardown(this);
285 }, this);
287 this._transitionElements = [];
288 this._transitions.forEach(function(transitionId) {
289 var t = this.getTransition(transitionId);
290 if (t) {
291 this._transitionElements.push(t);
292 t.setup(this);
294 }, this);
297 getTransition: function(transitionId) {
298 return this.tmeta.byId(transitionId);
301 selectionSelect: function(e, detail) {
302 this.updateSelectedItem();
303 // Wait to call applySelection when we run the transition
306 applyTransition: function(src, dst) {
307 if (this.animating) {
308 this.cancelAsync(this.animating);
309 this.animating = null;
312 Platform.flush();
314 if (this.transitioning.indexOf(src) === -1) {
315 this.transitioning.push(src);
317 if (this.transitioning.indexOf(dst) === -1) {
318 this.transitioning.push(dst);
320 // force src, dst to display
321 src.setAttribute('animate', '');
322 dst.setAttribute('animate', '');
324 var options = {
325 src: src,
326 dst: dst,
327 easing: 'cubic-bezier(0.4, 0, 0.2, 1)'
330 // fire an event so clients have a chance to do something when the
331 // new page becomes visible but before it draws.
332 this.fire('core-animated-pages-transition-prepare');
335 // prepare transition
336 this._transitionElements.forEach(function(transition) {
337 transition.prepare(this, options);
338 }, this);
340 // force layout!
341 src.offsetTop;
344 // apply selection
345 this.applySelection(dst, true);
346 this.applySelection(src, false);
348 // start transition
349 this._transitionElements.forEach(function(transition) {
350 transition.go(this, options);
351 }, this);
353 if (!this._transitionElements.length) {
354 this.complete();
355 } else {
356 this.animating = this.async(this.complete.bind(this), null, 5000);
360 complete: function() {
361 if (this.animating) {
362 this.cancelAsync(this.animating);
363 this.animating = null;
366 this.transitioning.forEach(function(t) {
367 t.removeAttribute('animate');
369 this.transitioning = [];
371 this._transitionElements.forEach(function(transition) {
372 transition.ensureComplete(this);
373 }, this);
375 this.fire('core-animated-pages-transition-end');
378 transitionEnd: function(e) {
379 if (this.transitioning.length) {
380 var completed = true;
381 this._transitionElements.forEach(function(transition) {
382 if (!transition.completed) {
383 completed = false;
386 if (completed) {
387 this.job('transitionWatch', function() {
388 this.complete();
389 }, 100);
394 selectedChanged: function(old) {
395 this.lastSelected = old;
396 this.super(arguments);
399 selectedItemChanged: function(oldItem) {
400 this.super(arguments);
402 if (!oldItem) {
403 this.applySelection(this.selectedItem, true);
404 return;
407 if (this.hasAttribute('no-transition') || !this._transitionElements || !this._transitionElements.length) {
408 this.applySelection(oldItem, false);
409 this.applySelection(this.selectedItem, true);
410 return;
413 if (oldItem && this.selectedItem) {
414 // TODO(sorvell): allow bindings to update first?
415 var self = this;
416 Platform.flush();
417 Platform.endOfMicrotask(function() {
418 self.applyTransition(oldItem, self.selectedItem);
425 </script>
427 </polymer-element>