]> git.proxmox.com Git - extjs.git/blame - extjs/packages/ux/classic/src/DataView/Animated.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / ux / classic / src / DataView / Animated.js
CommitLineData
6527f429
DM
1/**\r
2 * Transition plugin for DataViews\r
3 */\r
4Ext.define('Ext.ux.DataView.Animated', {\r
5\r
6 /**\r
7 * @property defaults\r
8 * @type Object\r
9 * Default configuration options for all DataViewTransition instances\r
10 */\r
11 defaults: {\r
12 duration : 750,\r
13 idProperty: 'id'\r
14 },\r
15 \r
16 /**\r
17 * Creates the plugin instance, applies defaults\r
18 * @constructor\r
19 * @param {Object} config Optional config object\r
20 */\r
21 constructor: function(config) {\r
22 Ext.apply(this, config || {}, this.defaults);\r
23 },\r
24\r
25 /**\r
26 * Initializes the transition plugin. Overrides the dataview's default refresh function\r
27 * @param {Ext.view.View} dataview The dataview\r
28 */\r
29 init: function(dataview) {\r
30 var me = this,\r
31 store = dataview.store,\r
32 items = dataview.all,\r
33 task = {\r
34 interval: 20\r
35 },\r
36 duration = me.duration;\r
37\r
38 /**\r
39 * @property dataview\r
40 * @type Ext.view.View\r
41 * Reference to the DataView this instance is bound to\r
42 */\r
43 me.dataview = dataview;\r
44 \r
45 dataview.blockRefresh = true;\r
46 dataview.updateIndexes = Ext.Function.createSequence(dataview.updateIndexes, function() {\r
47 this.getTargetEl().select(this.itemSelector).each(function(element, composite, index) {\r
48 element.dom.id = Ext.util.Format.format("{0}-{1}", dataview.id, store.getAt(index).internalId);\r
49 }, this);\r
50 }, dataview);\r
51 \r
52 /**\r
53 * @property dataviewID\r
54 * @type String\r
55 * The string ID of the DataView component. This is used internally when animating child objects\r
56 */\r
57 me.dataviewID = dataview.id;\r
58 \r
59 /**\r
60 * @property cachedStoreData\r
61 * @type Object\r
62 * A cache of existing store data, keyed by id. This is used to determine\r
63 * whether any items were added or removed from the store on data change\r
64 */\r
65 me.cachedStoreData = {};\r
66 \r
67 //catch the store data with the snapshot immediately\r
68 me.cacheStoreData(store.data || store.snapshot);\r
69\r
70 dataview.on('resize', function() {\r
71 var store = dataview.store;\r
72 if (store.getCount() > 0) {\r
73 // reDraw.call(this, store);\r
74 }\r
75 }, this);\r
76\r
77 // Buffer listenher so that rapid calls, for example a filter followed by a sort\r
78 // Only produce one redraw.\r
79 dataview.store.on({\r
80 datachanged: reDraw,\r
81 scope: this,\r
82 buffer: 50\r
83 });\r
84 \r
85 function reDraw() {\r
86 var parentEl = dataview.getTargetEl(),\r
87 parentElY = parentEl.getY(),\r
88 parentElPaddingTop = parentEl.getPadding('t'),\r
89 added = me.getAdded(store),\r
90 removed = me.getRemoved(store),\r
91 remaining = me.getRemaining(store),\r
92 itemArray,\r
93 i, id,\r
94 itemFly = new Ext.dom.Fly(),\r
95 rtl = me.dataview.getInherited().rtl,\r
96 oldPos, newPos,\r
97 styleSide = rtl ? 'right' : 'left',\r
98 newStyle = {};\r
99\r
100 // Not yet rendered\r
101 if (!parentEl) {\r
102 return;\r
103 }\r
104\r
105 // Collect nodes that will be removed in the forthcoming refresh so\r
106 // that we can put them back in order to fade them out\r
107 Ext.iterate(removed, function(recId, item) {\r
108 id = me.dataviewID + '-' + recId;\r
109\r
110 // Stop any animations for removed items and ensure th.\r
111 Ext.fx.Manager.stopAnimation(id);\r
112\r
113 item.dom = Ext.getDom(id);\r
114 if (!item.dom) {\r
115 delete removed[recId];\r
116 }\r
117 });\r
118\r
119 me.cacheStoreData(store);\r
120\r
121 // stores the current top and left values for each element (discovered below)\r
122 var oldPositions = {},\r
123 newPositions = {};\r
124\r
125 // Find current positions of elements which are to remain after the refresh.\r
126 Ext.iterate(remaining, function(id, item) {\r
127 if (itemFly.attach(Ext.getDom(me.dataviewID + '-' + id))) {\r
128 oldPos = oldPositions[id] = {\r
129 top : itemFly.getY() - parentElY - itemFly.getMargin('t') - parentElPaddingTop\r
130 };\r
131 oldPos[styleSide] = me.getItemX(itemFly);\r
132 } else {\r
133 delete remaining[id];\r
134 }\r
135 });\r
136\r
137 // The view MUST refresh, creating items in the natural flow, and collecting the items\r
138 // so that its item collection is consistent.\r
139 dataview.refresh();\r
140\r
141 // Replace removed nodes so that they can be faded out, THEN removed\r
142 Ext.iterate(removed, function(id, item) {\r
143 parentEl.dom.appendChild(item.dom);\r
144 itemFly.attach(item.dom).animate({\r
145 duration: duration,\r
146 opacity : 0,\r
147 callback: function(anim) {\r
148 var el = Ext.get(anim.target.id);\r
149 if (el) {\r
150 el.destroy();\r
151 }\r
152 }\r
153 });\r
154 delete item.dom;\r
155 });\r
156\r
157 // We have taken care of any removals.\r
158 // If the store is empty, we are done.\r
159 if (!store.getCount()) {\r
160 return;\r
161 }\r
162\r
163 // Collect the correct new positions after the refresh\r
164 itemArray = items.slice();\r
165\r
166 // Reverse order so that moving to absolute position does not affect the position of\r
167 // the next one we're looking at.\r
168 for (i = itemArray.length - 1; i >= 0; i--) {\r
169 id = store.getAt(i).internalId;\r
170 itemFly.attach(itemArray[i]);\r
171\r
172 newPositions[id] = {\r
173 dom: itemFly.dom,\r
174 top : itemFly.getY() - parentElY - itemFly.getMargin('t') - parentElPaddingTop\r
175 };\r
176 newPositions[id][styleSide] = me.getItemX(itemFly);\r
177\r
178 // We're going to absolutely position each item.\r
179 // If it is a "remaining" one from last refesh, shunt it back to\r
180 // its old position from where it will be animated.\r
181 newPos = oldPositions[id] || newPositions[id];\r
182\r
183 // set absolute positioning on all DataView items. We need to set position, left and \r
184 // top at the same time to avoid any flickering\r
185 newStyle.position = 'absolute';\r
186 newStyle.top = newPos.top + "px";\r
187 newStyle[styleSide] = newPos.left + "px";\r
188 itemFly.applyStyles(newStyle);\r
189 }\r
190\r
191 // This is the function which moves remaining items to their new position\r
192 var doAnimate = function() {\r
193 var elapsed = new Date() - task.taskStartTime,\r
194 fraction = elapsed / duration;\r
195\r
196 if (fraction >= 1) {\r
197 // At end, return all items to natural flow.\r
198 newStyle.position = newStyle.top = newStyle[styleSide] = '';\r
199 for (id in newPositions) {\r
200 itemFly.attach(newPositions[id].dom).applyStyles(newStyle);\r
201 }\r
202 Ext.TaskManager.stop(task);\r
203 } else {\r
204 // In frame, move each "remaining" item according to time elapsed\r
205 for (id in remaining) {\r
206 var oldPos = oldPositions[id],\r
207 newPos = newPositions[id],\r
208 oldTop = oldPos.top,\r
209 newTop = newPos.top,\r
210 oldLeft = oldPos[styleSide],\r
211 newLeft = newPos[styleSide],\r
212 diffTop = fraction * Math.abs(oldTop - newTop),\r
213 diffLeft= fraction * Math.abs(oldLeft - newLeft),\r
214 midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop,\r
215 midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;\r
216\r
217 newStyle.top = midTop + "px";\r
218 newStyle[styleSide] = midLeft + "px";\r
219 itemFly.attach(newPos.dom).applyStyles(newStyle);\r
220 }\r
221 }\r
222 };\r
223\r
224 // Fade in new items\r
225 Ext.iterate(added, function(id, item) {\r
226 if (itemFly.attach(Ext.getDom(me.dataviewID + '-' + id))) {\r
227 itemFly.setOpacity(0);\r
228 itemFly.animate({\r
229 duration: duration,\r
230 opacity : 1\r
231 });\r
232 }\r
233 });\r
234\r
235 // Stop any previous animations\r
236 Ext.TaskManager.stop(task);\r
237 task.run = doAnimate;\r
238 Ext.TaskManager.start(task);\r
239\r
240 me.cacheStoreData(store);\r
241 }\r
242 },\r
243\r
244 getItemX: function(el) {\r
245 var rtl = this.dataview.getInherited().rtl,\r
246 parentEl = el.up('');\r
247\r
248 if (rtl) {\r
249 return parentEl.getViewRegion().right - el.getRegion().right + el.getMargin('r');\r
250 } else {\r
251 return el.getX() - parentEl.getX() - el.getMargin('l') - parentEl.getPadding('l');\r
252 }\r
253 },\r
254\r
255 /**\r
256 * Caches the records from a store locally for comparison later\r
257 * @param {Ext.data.Store} store The store to cache data from\r
258 */\r
259 cacheStoreData: function(store) {\r
260 var cachedStoreData = this.cachedStoreData = {};\r
261 \r
262 store.each(function(record) {\r
263 cachedStoreData[record.internalId] = record;\r
264 });\r
265 },\r
266 \r
267 /**\r
268 * Returns all records that were already in the DataView\r
269 * @return {Object} All existing records\r
270 */\r
271 getExisting: function() {\r
272 return this.cachedStoreData;\r
273 },\r
274 \r
275 /**\r
276 * Returns the total number of items that are currently visible in the DataView\r
277 * @return {Number} The number of existing items\r
278 */\r
279 getExistingCount: function() {\r
280 var count = 0,\r
281 items = this.getExisting();\r
282 \r
283 for (var k in items) {\r
284 count++;\r
285 }\r
286 \r
287 return count;\r
288 },\r
289 \r
290 /**\r
291 * Returns all records in the given store that were not already present\r
292 * @param {Ext.data.Store} store The updated store instance\r
293 * @return {Object} Object of records not already present in the dataview in format {id: record}\r
294 */\r
295 getAdded: function(store) {\r
296 var cachedStoreData = this.cachedStoreData,\r
297 added = {};\r
298 \r
299 store.each(function(record) {\r
300 if (cachedStoreData[record.internalId] == null) {\r
301 added[record.internalId] = record;\r
302 }\r
303 });\r
304 \r
305 return added;\r
306 },\r
307 \r
308 /**\r
309 * Returns all records that are present in the DataView but not the new store\r
310 * @param {Ext.data.Store} store The updated store instance\r
311 * @return {Array} Array of records that used to be present\r
312 */\r
313 getRemoved: function(store) {\r
314 var cachedStoreData = this.cachedStoreData,\r
315 removed = {},\r
316 id;\r
317 \r
318 for (id in cachedStoreData) {\r
319 if (store.findBy(function(record) {return record.internalId == id;}) == -1) {\r
320 removed[id] = cachedStoreData[id];\r
321 }\r
322 }\r
323 \r
324 return removed;\r
325 },\r
326 \r
327 /**\r
328 * Returns all records that are already present and are still present in the new store\r
329 * @param {Ext.data.Store} store The updated store instance\r
330 * @return {Object} Object of records that are still present from last time in format {id: record}\r
331 */\r
332 getRemaining: function(store) {\r
333 var cachedStoreData = this.cachedStoreData,\r
334 remaining = {};\r
335\r
336 store.each(function(record) {\r
337 if (cachedStoreData[record.internalId] != null) {\r
338 remaining[record.internalId] = record;\r
339 }\r
340 });\r
341 \r
342 return remaining;\r
343 }\r
344});\r