2 * @class Ext.ux.DataViewTransition
4 * @author Ed Spencer (http://sencha.com)
5 * Transition plugin for DataViews
7 Ext.define('Ext.ux.DataView.Animated', {
12 * Default configuration options for all DataViewTransition instances
20 * Creates the plugin instance, applies defaults
22 * @param {Object} config Optional config object
24 constructor: function(config) {
25 Ext.apply(this, config || {}, this.defaults);
29 * Initializes the transition plugin. Overrides the dataview's default refresh function
30 * @param {Ext.view.View} dataview The dataview
32 init: function(dataview) {
36 * Reference to the DataView this instance is bound to
38 this.dataview = dataview;
40 var idProperty = this.idProperty,
41 store = dataview.store;
43 dataview.blockRefresh = true;
44 dataview.updateIndexes = Ext.Function.createSequence(dataview.updateIndexes, function() {
45 this.getTargetEl().select(this.itemSelector).each(function(element, composite, index) {
46 element.id = element.dom.id = Ext.util.Format.format("{0}-{1}", dataview.id, store.getAt(index).internalId);
51 * @property dataviewID
53 * The string ID of the DataView component. This is used internally when animating child objects
55 this.dataviewID = dataview.id;
58 * @property cachedStoreData
60 * A cache of existing store data, keyed by id. This is used to determine
61 * whether any items were added or removed from the store on data change
63 this.cachedStoreData = {};
65 //catch the store data with the snapshot immediately
66 this.cacheStoreData(store.data || store.snapshot);
68 dataview.on('resize', function() {
69 var store = dataview.store;
70 if (store.getCount() > 0) {
71 // reDraw.call(this, store);
75 dataview.store.on('datachanged', reDraw, this);
77 function reDraw(store) {
78 var parentEl = dataview.getTargetEl(),
79 calcItem = store.getAt(0),
80 added = this.getAdded(store),
81 removed = this.getRemoved(store),
82 previous = this.getRemaining(store),
83 existing = Ext.apply({}, previous, added);
86 Ext.each(removed, function(item) {
87 var id = this.dataviewID + '-' + item.internalId;
93 callback: function() {
94 Ext.fly(id).setDisplayed(false);
100 if (calcItem == undefined) {
101 this.cacheStoreData(store);
105 this.cacheStoreData(store);
107 var el = Ext.get(this.dataviewID + "-" + calcItem.internalId);
109 //if there is nothing rendered, force a refresh and return. This happens when loading asynchronously (was not
110 //covered correctly in previous versions, which only accepted local data)
116 //calculate the number of rows and columns we have
117 var itemCount = store.getCount(),
118 itemWidth = el.getMargin('lr') + el.getWidth(),
119 itemHeight = el.getMargin('bt') + el.getHeight(),
120 dvWidth = parentEl.getWidth(),
121 columns = Math.floor(dvWidth / itemWidth),
122 rows = Math.ceil(itemCount / columns),
123 currentRows = Math.ceil(this.getExistingCount() / columns);
125 //stores the current top and left values for each element (discovered below)
126 var oldPositions = {},
130 //find current positions of each element and save a reference in the elCache
131 Ext.iterate(previous, function(id, item) {
132 var id = item.internalId,
133 el = elCache[id] = Ext.get(this.dataviewID + '-' + id);
136 top : el.getTop() - parentEl.getTop() - el.getMargin('t') - parentEl.getPadding('t'),
137 left: el.getLeft() - parentEl.getLeft() - el.getMargin('l') - parentEl.getPadding('l')
141 //make sure the correct styles are applied to the parent element
142 parentEl.applyStyles({
147 //set absolute positioning on all DataView items. We need to set position, left and
148 //top at the same time to avoid any flickering
149 Ext.iterate(previous, function(id, item) {
150 var oldPos = oldPositions[id],
153 if (el.getStyle('position') != 'absolute') {
154 elCache[id].applyStyles({
155 position: 'absolute',
156 left : oldPos.left + "px",
157 top : oldPos.top + "px"
164 Ext.iterate(store.data.items, function(item) {
165 var id = item.internalId,
168 var column = index % columns,
169 row = Math.floor(index / columns),
170 top = row * itemHeight,
171 left = column * itemWidth;
182 var startTime = new Date(),
183 duration = this.duration,
184 dataviewID = this.dataviewID;
186 var doAnimate = function() {
187 var elapsed = new Date() - startTime,
188 fraction = elapsed / duration,
192 for (id in newPositions) {
193 Ext.fly(dataviewID + '-' + id).applyStyles({
194 top : newPositions[id].top + "px",
195 left: newPositions[id].left + "px"
199 Ext.TaskManager.stop(task);
202 for (id in newPositions) {
207 var oldPos = oldPositions[id],
208 newPos = newPositions[id],
211 oldLeft = oldPos.left,
212 newLeft = newPos.left,
213 diffTop = fraction * Math.abs(oldTop - newTop),
214 diffLeft= fraction * Math.abs(oldLeft - newLeft),
215 midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop,
216 midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;
218 Ext.fly(dataviewID + '-' + id).applyStyles({
221 }).setDisplayed(true);
232 Ext.TaskManager.start(task);
235 Ext.iterate(added, function(id, item) {
236 Ext.fly(this.dataviewID + '-' + item.internalId).applyStyles({
237 top : newPositions[item.internalId].top + "px",
238 left : newPositions[item.internalId].left + "px"
239 }).setDisplayed(true);
241 Ext.fly(this.dataviewID + '-' + item.internalId).animate({
248 this.cacheStoreData(store);
253 * Caches the records from a store locally for comparison later
254 * @param {Ext.data.Store} store The store to cache data from
256 cacheStoreData: function(store) {
257 this.cachedStoreData = {};
259 store.each(function(record) {
260 this.cachedStoreData[record.internalId] = record;
265 * Returns all records that were already in the DataView
266 * @return {Object} All existing records
268 getExisting: function() {
269 return this.cachedStoreData;
273 * Returns the total number of items that are currently visible in the DataView
274 * @return {Number} The number of existing items
276 getExistingCount: function() {
278 items = this.getExisting();
280 for (var k in items) {
288 * Returns all records in the given store that were not already present
289 * @param {Ext.data.Store} store The updated store instance
290 * @return {Object} Object of records not already present in the dataview in format {id: record}
292 getAdded: function(store) {
295 store.each(function(record) {
296 if (this.cachedStoreData[record.internalId] == undefined) {
297 added[record.internalId] = record;
305 * Returns all records that are present in the DataView but not the new store
306 * @param {Ext.data.Store} store The updated store instance
307 * @return {Array} Array of records that used to be present
309 getRemoved: function(store) {
313 for (id in this.cachedStoreData) {
314 if (store.findBy(function(record) {return record.internalId == id;}) == -1) {
315 removed.push(this.cachedStoreData[id]);
323 * Returns all records that are already present and are still present in the new store
324 * @param {Ext.data.Store} store The updated store instance
325 * @return {Object} Object of records that are still present from last time in format {id: record}
327 getRemaining: function(store) {
330 store.each(function(record) {
331 if (this.cachedStoreData[record.internalId] != undefined) {
332 remaining[record.internalId] = record;