]> git.proxmox.com Git - extjs.git/blame - extjs/packages/ux/classic/src/DataViewTransition.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / ux / classic / src / DataViewTransition.js
CommitLineData
6527f429
DM
1/**\r
2 * @class Ext.ux.DataViewTransition\r
3 * Transition plugin for DataViews\r
4 */\r
5Ext.ux.DataViewTransition = Ext.extend(Object, {\r
6\r
7 /**\r
8 * @property defaults\r
9 * @type Object\r
10 * Default configuration options for all DataViewTransition instances\r
11 */\r
12 defaults: {\r
13 duration : 750,\r
14 idProperty: 'id'\r
15 },\r
16 \r
17 /**\r
18 * Creates the plugin instance, applies defaults\r
19 * @constructor\r
20 * @param {Object} config Optional config object\r
21 */\r
22 constructor: function(config) {\r
23 Ext.apply(this, config || {}, this.defaults);\r
24 },\r
25\r
26 /**\r
27 * Initializes the transition plugin. Overrides the dataview's default refresh function\r
28 * @param {Ext.view.View} dataview The dataview\r
29 */\r
30 init: function(dataview) {\r
31 /**\r
32 * @property dataview\r
33 * @type Ext.view.View\r
34 * Reference to the DataView this instance is bound to\r
35 */\r
36 this.dataview = dataview;\r
37 \r
38 var idProperty = this.idProperty;\r
39 dataview.blockRefresh = true;\r
40 dataview.updateIndexes = Ext.Function.createSequence(dataview.updateIndexes, function() {\r
41 this.getTargetEl().select(this.itemSelector).each(function(element, composite, index) {\r
42 element.id = element.dom.id = Ext.util.Format.format("{0}-{1}", dataview.id, dataview.store.getAt(index).get(idProperty));\r
43 }, this);\r
44 }, dataview);\r
45 \r
46 /**\r
47 * @property dataviewID\r
48 * @type String\r
49 * The string ID of the DataView component. This is used internally when animating child objects\r
50 */\r
51 this.dataviewID = dataview.id;\r
52 \r
53 /**\r
54 * @property cachedStoreData\r
55 * @type Object\r
56 * A cache of existing store data, keyed by id. This is used to determine\r
57 * whether any items were added or removed from the store on data change\r
58 */\r
59 this.cachedStoreData = {};\r
60 \r
61 //var store = dataview.store;\r
62 \r
63 //catch the store data with the snapshot immediately\r
64 this.cacheStoreData(dataview.store.snapshot);\r
65 \r
66 dataview.store.on('datachanged', function(store) {\r
67 var parentEl = dataview.getTargetEl(),\r
68 calcItem = store.getAt(0),\r
69 added = this.getAdded(store),\r
70 removed = this.getRemoved(store),\r
71 previous = this.getRemaining(store),\r
72 existing = Ext.apply({}, previous, added);\r
73 \r
74 //hide old items\r
75 Ext.each(removed, function(item) {\r
76 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).animate({\r
77 remove : false,\r
78 duration: duration,\r
79 opacity : 0,\r
80 useDisplay: true\r
81 });\r
82 }, this);\r
83 \r
84 //store is empty\r
85 if (calcItem == undefined) {\r
86 this.cacheStoreData(store);\r
87 return;\r
88 }\r
89 \r
90 var el = Ext.get(this.dataviewID + "-" + calcItem.get(this.idProperty));\r
91 \r
92 //calculate the number of rows and columns we have\r
93 var itemCount = store.getCount(),\r
94 itemWidth = el.getMargin('lr') + el.getWidth(),\r
95 itemHeight = el.getMargin('bt') + el.getHeight(),\r
96 dvWidth = parentEl.getWidth(),\r
97 columns = Math.floor(dvWidth / itemWidth),\r
98 rows = Math.ceil(itemCount / columns),\r
99 currentRows = Math.ceil(this.getExistingCount() / columns);\r
100 \r
101 //make sure the correct styles are applied to the parent element\r
102 parentEl.applyStyles({\r
103 display : 'block',\r
104 position: 'relative'\r
105 });\r
106 \r
107 //stores the current top and left values for each element (discovered below)\r
108 var oldPositions = {},\r
109 newPositions = {},\r
110 elCache = {};\r
111 \r
112 //find current positions of each element and save a reference in the elCache\r
113 Ext.iterate(previous, function(id, item) {\r
114 var id = item.get(this.idProperty),\r
115 el = elCache[id] = Ext.get(this.dataviewID + '-' + id);\r
116 \r
117 oldPositions[id] = {\r
118 top : el.getY() - parentEl.getY() - el.getMargin('t') - parentEl.getPadding('t'),\r
119 left: el.getX() - parentEl.getX() - el.getMargin('l') - parentEl.getPadding('l')\r
120 };\r
121 }, this);\r
122 \r
123 //set absolute positioning on all DataView items. We need to set position, left and \r
124 //top at the same time to avoid any flickering\r
125 Ext.iterate(previous, function(id, item) {\r
126 var oldPos = oldPositions[id],\r
127 el = elCache[id];\r
128 \r
129 if (el.getStyle('position') != 'absolute') {\r
130 elCache[id].applyStyles({\r
131 position: 'absolute',\r
132 left : oldPos.left + "px",\r
133 top : oldPos.top + "px",\r
134\r
135 //we set the width here to make ListViews work correctly. This is not needed for DataViews\r
136 width : el.getWidth(!Ext.isIE || Ext.isStrict),\r
137 height : el.getHeight(!Ext.isIE || Ext.isStrict)\r
138 });\r
139 }\r
140 });\r
141 \r
142 //get new positions\r
143 var index = 0;\r
144 Ext.iterate(store.data.items, function(item) {\r
145 var id = item.get(idProperty),\r
146 el = elCache[id];\r
147 \r
148 var column = index % columns,\r
149 row = Math.floor(index / columns),\r
150 top = row * itemHeight,\r
151 left = column * itemWidth;\r
152 \r
153 newPositions[id] = {\r
154 top : top,\r
155 left: left\r
156 };\r
157 \r
158 index ++;\r
159 }, this);\r
160 \r
161 //do the movements\r
162 var startTime = new Date(),\r
163 duration = this.duration,\r
164 dataviewID = this.dataviewID;\r
165 \r
166 var doAnimate = function() {\r
167 var elapsed = new Date() - startTime,\r
168 fraction = elapsed / duration;\r
169 \r
170 if (fraction >= 1) {\r
171 for (var id in newPositions) {\r
172 Ext.fly(dataviewID + '-' + id).applyStyles({\r
173 top : newPositions[id].top + "px",\r
174 left: newPositions[id].left + "px"\r
175 });\r
176 }\r
177 \r
178 Ext.TaskManager.stop(task);\r
179 } else {\r
180 //move each item\r
181 for (var id in newPositions) {\r
182 if (!previous[id]) continue;\r
183 \r
184 var oldPos = oldPositions[id],\r
185 newPos = newPositions[id],\r
186 oldTop = oldPos.top,\r
187 newTop = newPos.top,\r
188 oldLeft = oldPos.left,\r
189 newLeft = newPos.left,\r
190 diffTop = fraction * Math.abs(oldTop - newTop),\r
191 diffLeft= fraction * Math.abs(oldLeft - newLeft),\r
192 midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop,\r
193 midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;\r
194 \r
195 Ext.fly(dataviewID + '-' + id).applyStyles({\r
196 top : midTop + "px",\r
197 left: midLeft + "px"\r
198 });\r
199 }\r
200 }\r
201 };\r
202 \r
203 var task = {\r
204 run : doAnimate,\r
205 interval: 20,\r
206 scope : this\r
207 };\r
208 \r
209 Ext.TaskManager.start(task);\r
210 \r
211 //<debug>\r
212 var count = 0;\r
213 for (var k in added) {\r
214 count++;\r
215 }\r
216 if (Ext.global.console && Ext.global.console.log) {\r
217 Ext.global.console.log('added:', count);\r
218 }\r
219 //</debug>\r
220 \r
221 //show new items\r
222 Ext.iterate(added, function(id, item) {\r
223 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).applyStyles({\r
224 top : newPositions[item.get(this.idProperty)].top + "px",\r
225 left : newPositions[item.get(this.idProperty)].left + "px"\r
226 });\r
227 \r
228 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).animate({\r
229 remove : false,\r
230 duration: duration,\r
231 opacity : 1\r
232 });\r
233 }, this);\r
234 \r
235 this.cacheStoreData(store);\r
236 }, this);\r
237 },\r
238 \r
239 /**\r
240 * Caches the records from a store locally for comparison later\r
241 * @param {Ext.data.Store} store The store to cache data from\r
242 */\r
243 cacheStoreData: function(store) {\r
244 this.cachedStoreData = {};\r
245 \r
246 store.each(function(record) {\r
247 this.cachedStoreData[record.get(this.idProperty)] = record;\r
248 }, this);\r
249 },\r
250 \r
251 /**\r
252 * Returns all records that were already in the DataView\r
253 * @return {Object} All existing records\r
254 */\r
255 getExisting: function() {\r
256 return this.cachedStoreData;\r
257 },\r
258 \r
259 /**\r
260 * Returns the total number of items that are currently visible in the DataView\r
261 * @return {Number} The number of existing items\r
262 */\r
263 getExistingCount: function() {\r
264 var count = 0,\r
265 items = this.getExisting();\r
266 \r
267 for (var k in items) count++;\r
268 \r
269 return count;\r
270 },\r
271 \r
272 /**\r
273 * Returns all records in the given store that were not already present\r
274 * @param {Ext.data.Store} store The updated store instance\r
275 * @return {Object} Object of records not already present in the dataview in format {id: record}\r
276 */\r
277 getAdded: function(store) {\r
278 var added = {};\r
279 \r
280 store.each(function(record) {\r
281 if (this.cachedStoreData[record.get(this.idProperty)] == undefined) {\r
282 added[record.get(this.idProperty)] = record;\r
283 }\r
284 }, this);\r
285 \r
286 return added;\r
287 },\r
288 \r
289 /**\r
290 * Returns all records that are present in the DataView but not the new store\r
291 * @param {Ext.data.Store} store The updated store instance\r
292 * @return {Array} Array of records that used to be present\r
293 */\r
294 getRemoved: function(store) {\r
295 var removed = [];\r
296 \r
297 for (var id in this.cachedStoreData) {\r
298 if (store.findExact(this.idProperty, Number(id)) == -1) {\r
299 removed.push(this.cachedStoreData[id]);\r
300 }\r
301 }\r
302 \r
303 return removed;\r
304 },\r
305 \r
306 /**\r
307 * Returns all records that are already present and are still present in the new store\r
308 * @param {Ext.data.Store} store The updated store instance\r
309 * @return {Object} Object of records that are still present from last time in format {id: record}\r
310 */\r
311 getRemaining: function(store) {\r
312 var remaining = {};\r
313\r
314 store.each(function(record) {\r
315 if (this.cachedStoreData[record.get(this.idProperty)] != undefined) {\r
316 remaining[record.get(this.idProperty)] = record;\r
317 }\r
318 }, this);\r
319\r
320 return remaining;\r
321 }\r
322});\r