Skip to content

Instantly share code, notes, and snippets.

@pphetra
Created August 10, 2012 06:01
Show Gist options
  • Select an option

  • Save pphetra/3311548 to your computer and use it in GitHub Desktop.

Select an option

Save pphetra/3311548 to your computer and use it in GitHub Desktop.
catalog
<html>
<head>
<link rel="stylesheet" href="www/extjs/resources/css/ext-all.css">
<script src="www/extjs/ext-all-debug.js"></script>
<style>
div.product {
float: left;
padding: 8px 17px;
margin: 5px;
/* margin: 10px 0 0 25px;*/
text-align: center;
line-height: 14px;
color: #333;
font-size: 14px;
font-family: "Helvetica Neue",sans-serif;
height: 130px;
width: 140px;
overflow: hidden;
border-top: 1px solid transparent;
cursor: pointer;
}
</style>
<script>
/**
* @author Ed Spencer (http://sencha.com)
* Transition plugin for DataViews
*/
Ext.define('Ext.ux.DataView.Animated', {
/**
* @property defaults
* @type Object
* Default configuration options for all DataViewTransition instances
*/
defaults: {
duration : 750,
idProperty: 'id'
},
/**
* Creates the plugin instance, applies defaults
* @constructor
* @param {Object} config Optional config object
*/
constructor: function(config) {
Ext.apply(this, config || {}, this.defaults);
},
/**
* Initializes the transition plugin. Overrides the dataview's default refresh function
* @param {Ext.view.View} dataview The dataview
*/
init: function(dataview) {
/**
* @property dataview
* @type Ext.view.View
* Reference to the DataView this instance is bound to
*/
this.dataview = dataview;
var idProperty = this.idProperty,
store = dataview.store;
dataview.blockRefresh = true;
dataview.updateIndexes = Ext.Function.createSequence(dataview.updateIndexes, function() {
this.getTargetEl().select(this.itemSelector).each(function(element, composite, index) {
element.id = element.dom.id = Ext.util.Format.format("{0}-{1}", dataview.id, store.getAt(index).internalId);
}, this);
}, dataview);
/**
* @property dataviewID
* @type String
* The string ID of the DataView component. This is used internally when animating child objects
*/
this.dataviewID = dataview.id;
/**
* @property cachedStoreData
* @type Object
* A cache of existing store data, keyed by id. This is used to determine
* whether any items were added or removed from the store on data change
*/
this.cachedStoreData = {};
//catch the store data with the snapshot immediately
this.cacheStoreData(store.data || store.snapshot);
dataview.on('resize', function() {
var store = dataview.store;
if (store.getCount() > 0) {
// reDraw.call(this, store);
}
}, this);
dataview.store.on('datachanged', reDraw, this);
function reDraw(store) {
var parentEl = dataview.getTargetEl(),
calcItem = store.getAt(0),
added = this.getAdded(store),
removed = this.getRemoved(store),
previous = this.getRemaining(store),
existing = Ext.apply({}, previous, added);
//hide old items
Ext.each(removed, function(item) {
var id = this.dataviewID + '-' + item.internalId;
Ext.fly(id).animate({
remove : false,
duration: duration,
opacity : 0,
useDisplay: true,
callback: function() {
Ext.fly(id).setDisplayed(false);
}
});
}, this);
//store is empty
if (calcItem == undefined) {
this.cacheStoreData(store);
return;
}
this.cacheStoreData(store);
var el = Ext.get(this.dataviewID + "-" + calcItem.internalId);
//if there is nothing rendered, force a refresh and return. This happens when loading asynchronously (was not
//covered correctly in previous versions, which only accepted local data)
if (!el) {
dataview.refresh();
return true;
}
//calculate the number of rows and columns we have
var itemCount = store.getCount(),
itemWidth = el.getMargin('lr') + el.getWidth(),
itemHeight = el.getMargin('bt') + el.getHeight(),
dvWidth = parentEl.getWidth(),
columns = Math.floor(dvWidth / itemWidth),
rows = Math.ceil(itemCount / columns),
currentRows = Math.ceil(this.getExistingCount() / columns);
//stores the current top and left values for each element (discovered below)
var oldPositions = {},
newPositions = {},
elCache = {};
//find current positions of each element and save a reference in the elCache
Ext.iterate(previous, function(id, item) {
var id = item.internalId,
el = elCache[id] = Ext.get(this.dataviewID + '-' + id);
oldPositions[id] = {
top : el.getTop() - parentEl.getTop() - el.getMargin('t') - parentEl.getPadding('t'),
left: el.getLeft() - parentEl.getLeft() - el.getMargin('l') - parentEl.getPadding('l')
};
}, this);
//make sure the correct styles are applied to the parent element
parentEl.applyStyles({
display : 'block',
position: 'relative'
});
//set absolute positioning on all DataView items. We need to set position, left and
//top at the same time to avoid any flickering
Ext.iterate(previous, function(id, item) {
var oldPos = oldPositions[id],
el = elCache[id];
if (el.getStyle('position') != 'absolute') {
elCache[id].applyStyles({
position: 'absolute',
left : oldPos.left + "px",
top : oldPos.top + "px"
});
}
});
//get new positions
var index = 0;
Ext.iterate(store.data.items, function(item) {
var id = item.internalId,
el = elCache[id];
var column = index % columns,
row = Math.floor(index / columns),
top = row * itemHeight,
left = column * itemWidth;
newPositions[id] = {
top : top,
left: left
};
index ++;
}, this);
//do the movements
var startTime = new Date(),
duration = this.duration,
dataviewID = this.dataviewID;
var doAnimate = function() {
var elapsed = new Date() - startTime,
fraction = elapsed / duration,
id;
if (fraction >= 1) {
for (id in newPositions) {
Ext.fly(dataviewID + '-' + id).applyStyles({
top : newPositions[id].top + "px",
left: newPositions[id].left + "px"
});
}
Ext.TaskManager.stop(task);
} else {
//move each item
for (id in newPositions) {
if (!previous[id]) {
continue;
}
var oldPos = oldPositions[id],
newPos = newPositions[id],
oldTop = oldPos.top,
newTop = newPos.top,
oldLeft = oldPos.left,
newLeft = newPos.left,
diffTop = fraction * Math.abs(oldTop - newTop),
diffLeft= fraction * Math.abs(oldLeft - newLeft),
midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop,
midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;
Ext.fly(dataviewID + '-' + id).applyStyles({
top : midTop + "px",
left: midLeft + "px"
}).setDisplayed(true);
}
}
};
var task = {
run : doAnimate,
interval: 20,
scope : this
};
Ext.TaskManager.start(task);
//show new items
Ext.iterate(added, function(id, item) {
Ext.fly(this.dataviewID + '-' + item.internalId).applyStyles({
top : newPositions[item.internalId].top + "px",
left : newPositions[item.internalId].left + "px"
}).setDisplayed(true);
Ext.fly(this.dataviewID + '-' + item.internalId).animate({
remove : false,
duration: duration,
opacity : 1
});
}, this);
this.cacheStoreData(store);
}
},
/**
* Caches the records from a store locally for comparison later
* @param {Ext.data.Store} store The store to cache data from
*/
cacheStoreData: function(store) {
this.cachedStoreData = {};
store.each(function(record) {
this.cachedStoreData[record.internalId] = record;
}, this);
},
/**
* Returns all records that were already in the DataView
* @return {Object} All existing records
*/
getExisting: function() {
return this.cachedStoreData;
},
/**
* Returns the total number of items that are currently visible in the DataView
* @return {Number} The number of existing items
*/
getExistingCount: function() {
var count = 0,
items = this.getExisting();
for (var k in items) {
count++;
}
return count;
},
/**
* Returns all records in the given store that were not already present
* @param {Ext.data.Store} store The updated store instance
* @return {Object} Object of records not already present in the dataview in format {id: record}
*/
getAdded: function(store) {
var added = {};
store.each(function(record) {
if (this.cachedStoreData[record.internalId] == undefined) {
added[record.internalId] = record;
}
}, this);
return added;
},
/**
* Returns all records that are present in the DataView but not the new store
* @param {Ext.data.Store} store The updated store instance
* @return {Array} Array of records that used to be present
*/
getRemoved: function(store) {
var removed = [],
id;
for (id in this.cachedStoreData) {
if (store.findBy(function(record) {return record.internalId == id;}) == -1) {
removed.push(this.cachedStoreData[id]);
}
}
return removed;
},
/**
* Returns all records that are already present and are still present in the new store
* @param {Ext.data.Store} store The updated store instance
* @return {Object} Object of records that are still present from last time in format {id: record}
*/
getRemaining: function(store) {
var remaining = {};
store.each(function(record) {
if (this.cachedStoreData[record.internalId] != undefined) {
remaining[record.internalId] = record;
}
}, this);
return remaining;
}
});
Ext.onReady(function() {
Ext.define('Brand', {
extend: 'Ext.data.Model',
fields: [
'name'
]
});
// สร้าง model Product
Ext.define('Product', {
extend: 'Ext.data.Model',
fields: [
{name: 'name', type: 'string'},
{name: 'price', type: 'double'},
{name: 'brand', type: 'string'}
]
});
Ext.define('CartItem', {
extend: 'Ext.data.Model',
fields: [
{name: 'name', type: 'string'},
{name: 'price', type: 'double'},
{name: 'brand', type: 'string'},
{name: 'qty', type: 'int'}
]
});
// กำหนด store ของ Product
var productStore = Ext.create('Ext.data.Store', {
model: 'Product',
data: [
{name: 'cx-0001', price: 200, brand: 'samsung'},
{name: 'zx-3300', price: 1700, brand: 'sony'},
{name: 'cx-0071', price: 200, brand: 'samsung'},
{name: 'zx-3270', price: 1700, brand: 'samsung'},
{name: 'xx-d27f', price: 8899, brand: 'apple'},
{name: 'cx-0271', price: 200, brand: 'sony'},
{name: 'zx-3370', price: 1700, brand: 'sony'},
{name: 'xx-d17f', price: 8899, brand: 'apple'},
{name: 'xx-dfdf', price: 8899, brand: 'apple'}
]
});
pok = productStore;
var cartStore = Ext.create('Ext.data.Store', {
model: 'CartItem',
data: [
{name: 'cx-0001', price: 200, brand: 'samsung', qty: 1},
{name: 'zx-3300', price: 1700, brand: 'sony', qty: 1},
{name: 'xx-dfdf', price: 8899, brand: 'apple', qty: 1}
]
});
var brandStore = Ext.create('Ext.data.Store', {
model: 'Brand',
data: [
{name: 'apple'},
{name: 'samsung'},
{name: 'sony'}
]
});
// กำหนด template การ render Product
var catalogTpl = new Ext.XTemplate(
'<tpl for=".">',
'<div class="product">',
'<div><img width="64" height="64" src="{name}.png"/></div>',
'{name}',
'<div>{price}</div>',
'<button>Buy</button>',
'</div>',
'</tpl>'
);
var cartTpl = new Ext.XTemplate(
'<table>',
'<tpl for=".">',
'<tr class="cartItem">',
'<td width="150">{name}</td>',
'<td width="50">{price}</td>',
'<td width="50">{qty}</td>',
'<td><button>Remove</button></td>',
'</tr>',
'</tpl>',
'</table>'
);
// ร้อย template และ store เข้ากับ DataView
var catalogView = Ext.create('Ext.view.View', {
// กำหนด region ที่จะแสดงใน border ด้วย
region: 'center',
store: productStore,
tpl: catalogTpl,
emptyText: 'No Product Found',
itemSelector: 'div.product',
plugins : [
Ext.create('Ext.ux.DataView.Animated', {
duration : 550,
idProperty: 'name'
})
],
listeners: {
// ดัก event click ที่เกิดบนปุ่ม Buy
// ถ้าพบให้ console.log(model.get('name'));
itemclick: function(comp, model, el, index, evt) {
console.log(arguments);
if (evt.target.tagName === 'BUTTON') { //กรองเฉพาะ click ที่เกิดจาก <button>
console.log(model.get('name'));
// หา cartItem ที่มี name ตรงกัน
var idx = cartStore.findExact('name', model.get('name'));
if (idx >= 0) {
// ถ้าเจอให้ทำการ set qty=qty+1
var cartItem = cartStore.getAt(idx);
cartItem.set('qty', cartItem.get('qty') + 1);
} else {
// ถ้าไม่เจอให้ create CartItem แล้ว add เข้า cartStore
// create CartItem
var item = new CartItem({
name: model.get('name'),
price: model.get('price'),
brand: model.get('brand'),
qty: 1
});
cartStore.add(item);
}
}
}
}
});
var cartView = Ext.create('Ext.view.View', {
region: 'east',
width: 200, //กำหนด size ด้วย
store: cartStore,
tpl: cartTpl,
emptyText: 'no items',
itemSelector: 'tr.cartItem',
listeners: {
'itemclick': function(comp, model, el, index, evt) {
if (evt.target.tagName === 'BUTTON') {
cartStore.remove(model);
}
}
}
});
var mainPanel = Ext.create('Ext.Panel', {
layout: 'border',
width: 700,
height: 600,
items: [
catalogView // dataview
,cartView // dataview
,{ // ไม่ระบุ xtype, default เป็น Ext.Panel
region: 'north',
height: 50,
items: [
{
xtype: 'combo', // ระบุ xtype
store: brandStore,
displayField: 'name',
queryMode: 'local', // ถ้าเป็น remote มันจะใช้ proxy ไปดึงค่ามา
listeners: {
// change: function(combo, newValue, oldValue) {
// // set filter ให้กับ productStore
// productStore.filter('brand', newValue);
// },
select: function(combo, records) {
productStore.clearFilter();
var r = records[0];
var brand = r.get('name');
productStore.filter('brand', brand);
}
}
}
]
}
],
renderTo: document.body
});
});
</script>
</head>
<body>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment