2 * @class Ext.ux.DataViewTransition
4 * @author Ed Spencer (http://sencha.com)
5 * Transition plugin for DataViews
7 Ext.ux.DataViewTransition = Ext.extend(Object, {
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 dataview.blockRefresh = true;
42 dataview.updateIndexes = Ext.Function.createSequence(dataview.updateIndexes, function() {
43 this.getTargetEl().select(this.itemSelector).each(function(element, composite, index) {
44 element.id = element.dom.id = Ext.util.Format.format("{0}-{1}", dataview.id, dataview.store.getAt(index).get(idProperty));
49 * @property dataviewID
51 * The string ID of the DataView component. This is used internally when animating child objects
53 this.dataviewID = dataview.id;
56 * @property cachedStoreData
58 * A cache of existing store data, keyed by id. This is used to determine
59 * whether any items were added or removed from the store on data change
61 this.cachedStoreData = {};
63 //var store = dataview.store;
65 //catch the store data with the snapshot immediately
66 this.cacheStoreData(dataview.store.snapshot);
68 dataview.store.on('datachanged', function(store) {
69 var parentEl = dataview.getTargetEl(),
70 calcItem = store.getAt(0),
71 added = this.getAdded(store),
72 removed = this.getRemoved(store),
73 previous = this.getRemaining(store),
74 existing = Ext.apply({}, previous, added);
77 Ext.each(removed, function(item) {
78 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).animate({
87 if (calcItem == undefined) {
88 this.cacheStoreData(store);
92 var el = Ext.get(this.dataviewID + "-" + calcItem.get(this.idProperty));
94 //calculate the number of rows and columns we have
95 var itemCount = store.getCount(),
96 itemWidth = el.getMargin('lr') + el.getWidth(),
97 itemHeight = el.getMargin('bt') + el.getHeight(),
98 dvWidth = parentEl.getWidth(),
99 columns = Math.floor(dvWidth / itemWidth),
100 rows = Math.ceil(itemCount / columns),
101 currentRows = Math.ceil(this.getExistingCount() / columns);
103 //make sure the correct styles are applied to the parent element
104 parentEl.applyStyles({
109 //stores the current top and left values for each element (discovered below)
110 var oldPositions = {},
114 //find current positions of each element and save a reference in the elCache
115 Ext.iterate(previous, function(id, item) {
116 var id = item.get(this.idProperty),
117 el = elCache[id] = Ext.get(this.dataviewID + '-' + id);
120 top : el.getTop() - parentEl.getTop() - el.getMargin('t') - parentEl.getPadding('t'),
121 left: el.getLeft() - parentEl.getLeft() - el.getMargin('l') - parentEl.getPadding('l')
125 //set absolute positioning on all DataView items. We need to set position, left and
126 //top at the same time to avoid any flickering
127 Ext.iterate(previous, function(id, item) {
128 var oldPos = oldPositions[id],
131 if (el.getStyle('position') != 'absolute') {
132 elCache[id].applyStyles({
133 position: 'absolute',
134 left : oldPos.left + "px",
135 top : oldPos.top + "px",
137 //we set the width here to make ListViews work correctly. This is not needed for DataViews
138 width : el.getWidth(!Ext.isIE || Ext.isStrict),
139 height : el.getHeight(!Ext.isIE || Ext.isStrict)
146 Ext.iterate(store.data.items, function(item) {
147 var id = item.get(idProperty),
150 var column = index % columns,
151 row = Math.floor(index / columns),
152 top = row * itemHeight,
153 left = column * itemWidth;
164 var startTime = new Date(),
165 duration = this.duration,
166 dataviewID = this.dataviewID;
168 var doAnimate = function() {
169 var elapsed = new Date() - startTime,
170 fraction = elapsed / duration;
173 for (var id in newPositions) {
174 Ext.fly(dataviewID + '-' + id).applyStyles({
175 top : newPositions[id].top + "px",
176 left: newPositions[id].left + "px"
180 Ext.TaskManager.stop(task);
183 for (var id in newPositions) {
184 if (!previous[id]) continue;
186 var oldPos = oldPositions[id],
187 newPos = newPositions[id],
190 oldLeft = oldPos.left,
191 newLeft = newPos.left,
192 diffTop = fraction * Math.abs(oldTop - newTop),
193 diffLeft= fraction * Math.abs(oldLeft - newLeft),
194 midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop,
195 midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;
197 Ext.fly(dataviewID + '-' + id).applyStyles({
211 Ext.TaskManager.start(task);
215 for (var k in added) {
218 if (Ext.global.console) {
219 Ext.global.console.log('added:', count);
224 Ext.iterate(added, function(id, item) {
225 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).applyStyles({
226 top : newPositions[item.get(this.idProperty)].top + "px",
227 left : newPositions[item.get(this.idProperty)].left + "px"
230 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).animate({
237 this.cacheStoreData(store);
242 * Caches the records from a store locally for comparison later
243 * @param {Ext.data.Store} store The store to cache data from
245 cacheStoreData: function(store) {
246 this.cachedStoreData = {};
248 store.each(function(record) {
249 this.cachedStoreData[record.get(this.idProperty)] = record;
254 * Returns all records that were already in the DataView
255 * @return {Object} All existing records
257 getExisting: function() {
258 return this.cachedStoreData;
262 * Returns the total number of items that are currently visible in the DataView
263 * @return {Number} The number of existing items
265 getExistingCount: function() {
267 items = this.getExisting();
269 for (var k in items) count++;
275 * Returns all records in the given store that were not already present
276 * @param {Ext.data.Store} store The updated store instance
277 * @return {Object} Object of records not already present in the dataview in format {id: record}
279 getAdded: function(store) {
282 store.each(function(record) {
283 if (this.cachedStoreData[record.get(this.idProperty)] == undefined) {
284 added[record.get(this.idProperty)] = record;
292 * Returns all records that are present in the DataView but not the new store
293 * @param {Ext.data.Store} store The updated store instance
294 * @return {Array} Array of records that used to be present
296 getRemoved: function(store) {
299 for (var id in this.cachedStoreData) {
300 if (store.findExact(this.idProperty, Number(id)) == -1) {
301 removed.push(this.cachedStoreData[id]);
309 * Returns all records that are already present and are still present in the new store
310 * @param {Ext.data.Store} store The updated store instance
311 * @return {Object} Object of records that are still present from last time in format {id: record}
313 getRemaining: function(store) {
316 store.each(function(record) {
317 if (this.cachedStoreData[record.get(this.idProperty)] != undefined) {
318 remaining[record.get(this.idProperty)] = record;