|
1 <!DOCTYPE html> |
|
2 <html lang="en"> |
|
3 <head> |
|
4 <meta charset="utf-8"> |
|
5 <title>Example: Creating a Node Plugin that chains transitions</title> |
|
6 <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic"> |
|
7 <link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css"> |
|
8 <link rel="stylesheet" href="../assets/css/main.css"> |
|
9 <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css"> |
|
10 <link rel="shortcut icon" type="image/png" href="../assets/favicon.png"> |
|
11 <script src="../../build/yui/yui-min.js"></script> |
|
12 |
|
13 </head> |
|
14 <body> |
|
15 <!-- |
|
16 <a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a> |
|
17 --> |
|
18 <div id="doc"> |
|
19 <div id="hd"> |
|
20 <h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1> |
|
21 </div> |
|
22 |
|
23 <a href="#toc" class="jump">Jump to Table of Contents</a> |
|
24 |
|
25 |
|
26 <h1>Example: Creating a Node Plugin that chains transitions</h1> |
|
27 <div class="yui3-g"> |
|
28 <div class="yui3-u-3-4"> |
|
29 <div id="main"> |
|
30 <div class="content"><style scoped> |
|
31 #square { |
|
32 width: 100px; |
|
33 height: 100px; |
|
34 background: gray; |
|
35 position: relative; |
|
36 margin: 20px; |
|
37 } |
|
38 </style> |
|
39 |
|
40 |
|
41 <div class="intro"> |
|
42 <p> |
|
43 In order to run transitions sequentially, you would normally have to use the callback provided by <code>node.transition()</code>. This example shows how to create your own Node plugin based on promises that lets you chain CSS transitions. |
|
44 </p> |
|
45 </div> |
|
46 |
|
47 <div class="example yui3-skin-sam"> |
|
48 <button id="without-plugin">Without Plugin</button> |
|
49 <button id="with-plugin">With Plugin</button> |
|
50 <div id="square"></div> |
|
51 |
|
52 <script> |
|
53 YUI().use('promise', 'transition', 'node-pluginhost', function (Y) { |
|
54 |
|
55 // NodePromise will represent a YUI Node |
|
56 function NodePromise() { |
|
57 NodePromise.superclass.constructor.apply(this, arguments); |
|
58 } |
|
59 Y.extend(NodePromise, Y.Promise); |
|
60 |
|
61 // This method takes the same "config" parameter as Node's transition method |
|
62 // but returns a NodePromise instead |
|
63 NodePromise.prototype.transition = function (config) { |
|
64 // We call this.then to ensure the promise is fulfilled. |
|
65 // Since we will be creating a chain of transitions this means we will be |
|
66 // waiting for the previous transition to end |
|
67 return this.then(function (node) { |
|
68 // As noted in the user guide, returning a promise inside the then() |
|
69 // callback causes the promise returned by then() to be synced with this |
|
70 // new promise. This is a way to control when the returned promise is |
|
71 // fulfilled |
|
72 return new Y.Promise(function (fulfill, reject) { |
|
73 node.transition(config, function () { |
|
74 // The transition is done, signal the promise that all is ready |
|
75 // by fulfilling it with the same node |
|
76 fulfill(node); |
|
77 }); |
|
78 }); |
|
79 }); |
|
80 }; |
|
81 |
|
82 function PromisePlugin(config) { |
|
83 // Create a private NodePromise instance that points to the plugin host |
|
84 this._promise = new NodePromise(function (fulfill) { |
|
85 // Since this is a Node plugin, config.host will be an instance of Node |
|
86 fulfill(config.host); |
|
87 }); |
|
88 } |
|
89 |
|
90 // Set up the plugin's namespace |
|
91 PromisePlugin.NS = 'promise'; |
|
92 |
|
93 PromisePlugin.prototype.transition = function (config) { |
|
94 // Simply point to the private promise's transition method |
|
95 return this._promise.transition(config); |
|
96 }; |
|
97 |
|
98 |
|
99 var square = Y.one('#square'); |
|
100 square.plug(PromisePlugin); |
|
101 |
|
102 function resetStyles() { |
|
103 square.setStyles({ |
|
104 width: '100px', |
|
105 height: '100px', |
|
106 left: '0' |
|
107 }); |
|
108 } |
|
109 |
|
110 Y.one('#without-plugin').on('click', function () { |
|
111 resetStyles(); |
|
112 square |
|
113 .transition({width: '300px'}) |
|
114 .transition({height: '300px'}) |
|
115 .transition({left: '200px'}); |
|
116 }); |
|
117 Y.one('#with-plugin').on('click', function () { |
|
118 resetStyles(); |
|
119 square.promise |
|
120 .transition({width: '300px'}) |
|
121 .transition({height: '300px'}) |
|
122 .transition({left: '200px'}); |
|
123 }); |
|
124 |
|
125 }); |
|
126 </script> |
|
127 |
|
128 </div> |
|
129 |
|
130 <h2 id="using-promises-to-chain-css-transitions">Using Promises to Chain CSS Transitions</h2> |
|
131 |
|
132 <h3 id="the-plan">The plan</h3> |
|
133 |
|
134 <p>Plugins are a way to add functionality to Node without modifying its existing methods. They also are usually subclasses of Plugin.Base that contain various methods to interact with in a different way with a node. In our case we will skip the use of Plugin.Base to focus on returning promises from a plugin method.</p> |
|
135 |
|
136 <p>The plan is to create a Promise subclass that represents a Node and store one of these promises in the plugin instance. Then the plugin's <code>transition</code> method will return a new promise based on the one already stored.</p> |
|
137 |
|
138 <h3 id="creating-a-promise-subclass">Creating a Promise Subclass</h3> |
|
139 |
|
140 <p>Promises represent a value. Since we want to chain transitions on a Node we need to create a Promise sublcass that represents a Node. Promises can be extended the same way as any other YUI class by using <a href="http://yuilibrary.com/yui/docs/api//classes/YUI.html#method_extend"><code>Y.extend</code></a>.</p> |
|
141 |
|
142 <pre class="code prettyprint">// NodePromise will represent a YUI Node |
|
143 function NodePromise() { |
|
144 NodePromise.superclass.constructor.apply(this, arguments); |
|
145 } |
|
146 Y.extend(NodePromise, Y.Promise);</pre> |
|
147 |
|
148 |
|
149 <p>The next step is to add the <code>transition()</code> method to this promise and have it return a promise that is fulfilled when the transition is completed.</p> |
|
150 |
|
151 <pre class="code prettyprint">// This method takes the same "config" parameter as Node's transition method |
|
152 // but returns a NodePromise instead |
|
153 NodePromise.prototype.transition = function (config) { |
|
154 // We call this.then to ensure the promise is fulfilled. |
|
155 // Since we will be creating a chain of transitions this means we will be |
|
156 // waiting for the previous transition to end |
|
157 return this.then(function (node) { |
|
158 // As noted in the user guide, returning a promise inside the then() |
|
159 // callback causes the promise returned by then() to be synced with this |
|
160 // new promise. This is a way to control when the returned promise is |
|
161 // fulfilled |
|
162 return new Y.Promise(function (fulfill, reject) { |
|
163 node.transition(config, function () { |
|
164 // The transition is done, signal the promise that all is ready |
|
165 // by fulfilling it with the same node |
|
166 fulfill(node); |
|
167 }); |
|
168 }); |
|
169 }); |
|
170 };</pre> |
|
171 |
|
172 |
|
173 <h3 id="creating-the-plugin">Creating the Plugin</h3> |
|
174 |
|
175 <p>Our plugin is a very simple class that contains a NodePromise. In order for it to let us write chains of transitions like <code>node.promise.transition(config1).transition(config2)</code> we will add a <code>transition</code> method to it that simply points to the NodePromise's same method.</p> |
|
176 |
|
177 <pre class="code prettyprint">function PromisePlugin(config) { |
|
178 // Create a private NodePromise instance that points to the plugin host |
|
179 this._promise = new NodePromise(function (fulfill) { |
|
180 // Since this is a Node plugin, config.host will be an instance of Node |
|
181 fulfill(config.host); |
|
182 }); |
|
183 } |
|
184 |
|
185 // Set up the plugin's namespace |
|
186 PromisePlugin.NS = 'promise'; |
|
187 |
|
188 PromisePlugin.prototype.transition = function (config) { |
|
189 // Simply point to the private promise's transition method |
|
190 return this._promise.transition(config); |
|
191 };</pre> |
|
192 |
|
193 |
|
194 <h3 id="using-the-plugin">Using the Plugin</h3> |
|
195 |
|
196 <p>Now that we have the plugin ready, we can easily chain transitions from the plugin instance:</p> |
|
197 |
|
198 <pre class="code prettyprint">var square = Y.one('#square'); |
|
199 square.plug(PromisePlugin); |
|
200 |
|
201 // run a sequence of transitions |
|
202 square.promise |
|
203 .transition({width: '300px'}) |
|
204 .transition({height: '300px'}) |
|
205 .transition({left: '200px'});</pre> |
|
206 |
|
207 |
|
208 <h3 id="fullcode">Full Code Listing</h3> |
|
209 <h4 id="html">HTML</h4> |
|
210 <pre class="code prettyprint"><button id="without-plugin">Without Plugin</button> |
|
211 <button id="with-plugin">With Plugin</button> |
|
212 <div id="square"></div></pre> |
|
213 |
|
214 |
|
215 <h4 id="css">CSS</h4> |
|
216 <pre class="code prettyprint"><style scoped> |
|
217 #square { |
|
218 width: 100px; |
|
219 height: 100px; |
|
220 background: gray; |
|
221 position: relative; |
|
222 margin: 20px; |
|
223 } |
|
224 </style></pre> |
|
225 |
|
226 |
|
227 <h4 id="javascript">JavaScript</h4> |
|
228 <pre class="code prettyprint"><script> |
|
229 YUI().use('promise', 'transition', 'node-pluginhost', function (Y) { |
|
230 |
|
231 // NodePromise will represent a YUI Node |
|
232 function NodePromise() { |
|
233 NodePromise.superclass.constructor.apply(this, arguments); |
|
234 } |
|
235 Y.extend(NodePromise, Y.Promise); |
|
236 |
|
237 // This method takes the same "config" parameter as Node's transition method |
|
238 // but returns a NodePromise instead |
|
239 NodePromise.prototype.transition = function (config) { |
|
240 // We call this.then to ensure the promise is fulfilled. |
|
241 // Since we will be creating a chain of transitions this means we will be |
|
242 // waiting for the previous transition to end |
|
243 return this.then(function (node) { |
|
244 // As noted in the user guide, returning a promise inside the then() |
|
245 // callback causes the promise returned by then() to be synced with this |
|
246 // new promise. This is a way to control when the returned promise is |
|
247 // fulfilled |
|
248 return new Y.Promise(function (fulfill, reject) { |
|
249 node.transition(config, function () { |
|
250 // The transition is done, signal the promise that all is ready |
|
251 // by fulfilling it with the same node |
|
252 fulfill(node); |
|
253 }); |
|
254 }); |
|
255 }); |
|
256 }; |
|
257 |
|
258 function PromisePlugin(config) { |
|
259 // Create a private NodePromise instance that points to the plugin host |
|
260 this._promise = new NodePromise(function (fulfill) { |
|
261 // Since this is a Node plugin, config.host will be an instance of Node |
|
262 fulfill(config.host); |
|
263 }); |
|
264 } |
|
265 |
|
266 // Set up the plugin's namespace |
|
267 PromisePlugin.NS = 'promise'; |
|
268 |
|
269 PromisePlugin.prototype.transition = function (config) { |
|
270 // Simply point to the private promise's transition method |
|
271 return this._promise.transition(config); |
|
272 }; |
|
273 |
|
274 |
|
275 var square = Y.one('#square'); |
|
276 square.plug(PromisePlugin); |
|
277 |
|
278 function resetStyles() { |
|
279 square.setStyles({ |
|
280 width: '100px', |
|
281 height: '100px', |
|
282 left: '0' |
|
283 }); |
|
284 } |
|
285 |
|
286 Y.one('#without-plugin').on('click', function () { |
|
287 resetStyles(); |
|
288 square |
|
289 .transition({width: '300px'}) |
|
290 .transition({height: '300px'}) |
|
291 .transition({left: '200px'}); |
|
292 }); |
|
293 Y.one('#with-plugin').on('click', function () { |
|
294 resetStyles(); |
|
295 square.promise |
|
296 .transition({width: '300px'}) |
|
297 .transition({height: '300px'}) |
|
298 .transition({left: '200px'}); |
|
299 }); |
|
300 |
|
301 }); |
|
302 </script></pre> |
|
303 |
|
304 </div> |
|
305 </div> |
|
306 </div> |
|
307 |
|
308 <div class="yui3-u-1-4"> |
|
309 <div class="sidebar"> |
|
310 |
|
311 <div id="toc" class="sidebox"> |
|
312 <div class="hd"> |
|
313 <h2 class="no-toc">Table of Contents</h2> |
|
314 </div> |
|
315 |
|
316 <div class="bd"> |
|
317 <ul class="toc"> |
|
318 <li> |
|
319 <a href="#using-promises-to-chain-css-transitions">Using Promises to Chain CSS Transitions</a> |
|
320 <ul class="toc"> |
|
321 <li> |
|
322 <a href="#the-plan">The plan</a> |
|
323 </li> |
|
324 <li> |
|
325 <a href="#creating-a-promise-subclass">Creating a Promise Subclass</a> |
|
326 </li> |
|
327 <li> |
|
328 <a href="#creating-the-plugin">Creating the Plugin</a> |
|
329 </li> |
|
330 <li> |
|
331 <a href="#using-the-plugin">Using the Plugin</a> |
|
332 </li> |
|
333 <li> |
|
334 <a href="#fullcode">Full Code Listing</a> |
|
335 <ul class="toc"> |
|
336 <li> |
|
337 <a href="#html">HTML</a> |
|
338 </li> |
|
339 <li> |
|
340 <a href="#css">CSS</a> |
|
341 </li> |
|
342 <li> |
|
343 <a href="#javascript">JavaScript</a> |
|
344 </li> |
|
345 </ul> |
|
346 </li> |
|
347 </ul> |
|
348 </li> |
|
349 </ul> |
|
350 </div> |
|
351 </div> |
|
352 |
|
353 |
|
354 |
|
355 <div class="sidebox"> |
|
356 <div class="hd"> |
|
357 <h2 class="no-toc">Examples</h2> |
|
358 </div> |
|
359 |
|
360 <div class="bd"> |
|
361 <ul class="examples"> |
|
362 |
|
363 |
|
364 <li data-description="Wrapping async transactions with promises"> |
|
365 <a href="basic-example.html">Wrapping async transactions with promises</a> |
|
366 </li> |
|
367 |
|
368 |
|
369 |
|
370 <li data-description="Extend Y.Promise to create classes that encapsulate standard transaction logic in descriptive method names"> |
|
371 <a href="subclass-example.html">Subclassing Y.Promise</a> |
|
372 </li> |
|
373 |
|
374 |
|
375 |
|
376 <li data-description="Extend the Promise class to create your own Node plugin that chains transitions"> |
|
377 <a href="plugin-example.html">Creating a Node Plugin that chains transitions</a> |
|
378 </li> |
|
379 |
|
380 |
|
381 </ul> |
|
382 </div> |
|
383 </div> |
|
384 |
|
385 |
|
386 |
|
387 </div> |
|
388 </div> |
|
389 </div> |
|
390 </div> |
|
391 |
|
392 <script src="../assets/vendor/prettify/prettify-min.js"></script> |
|
393 <script>prettyPrint();</script> |
|
394 |
|
395 <script> |
|
396 YUI.Env.Tests = { |
|
397 examples: [], |
|
398 project: '../assets', |
|
399 assets: '../assets/promise', |
|
400 name: 'plugin-example', |
|
401 title: 'Creating a Node Plugin that chains transitions', |
|
402 newWindow: '', |
|
403 auto: false |
|
404 }; |
|
405 YUI.Env.Tests.examples.push('basic-example'); |
|
406 YUI.Env.Tests.examples.push('subclass-example'); |
|
407 YUI.Env.Tests.examples.push('plugin-example'); |
|
408 |
|
409 </script> |
|
410 <script src="../assets/yui/test-runner.js"></script> |
|
411 |
|
412 |
|
413 |
|
414 </body> |
|
415 </html> |