/**
* @private - To be made a sample
*/
Ext.define('Ext.dataview.ComponentList', {
alternateClassName: 'Ext.ComponentList',
extend: 'Ext.dataview.ComponentView',
xtype : 'componentlist',
requires: [
'Ext.dataview.ListItem',
'Ext.dataview.IndexBar'
],
config: {
/**
* @cfg {Boolean/Object} indexBar
* True to render an alphabet IndexBar docked on the right.
* This can also be a config object that will be passed to {@link Ext.IndexBar}
* (defaults to false)
* @accessor
*/
indexBar: false,
disclosure: null,
icon: null,
/**
* @cfg {Boolean} clearSelectionOnDeactivate
* True to clear any selections on the list when the list is deactivated (defaults to true).
* @accessor
*/
clearSelectionOnDeactivate: true,
/**
* @cfg {Boolean} preventSelectionOnDisclose True to prevent the item selection when the user
* taps a disclose icon. Defaults to <tt>true</tt>
* @accessor
*/
preventSelectionOnDisclose: true,
// @inherit
baseCls: Ext.baseCSSPrefix + 'list',
* @cfg {Boolean} pinHeaders
* Whether or not to pin headers on top of item groups while scrolling for an iPhone native list experience.
* Defaults to <tt>false</tt> on Android and Blackberry (for performance reasons)
* Defaults to <tt>true</tt> on other devices.
* @accessor
*/
pinHeaders: true,
// @inherit
defaultType: 'listitem',
grouped: false,
innerWidth: 'block',
itemTpl: null,
/**
* @cfg {Boolean/Function/Object} onItemDisclosure
* True to display a disclosure icon on each list item.
* This won't bind a listener to the tap event. The list
* will still fire the disclose event though.
* By setting this config to a function, it will automatically
* add a tap event listeners to the disclosure buttons which
* will fire your function.
* Finally you can specify an object with a 'scope' and 'handler'
* property defined. This will also be bound to the tap event listener
* and is useful when you want to change the scope of the handler.
* @accessor
*/
onItemDisclosure: null
},
constructor: function() {
this.previousHeaderIndices = [];
this.callParent(arguments);
},
initialize: function() {
var me = this;
me.callParent(arguments);
me.on({
delegate: '> ' + me.getDefaultType() + ' > listdisclosure',
tap: 'handleItemDisclosure',
scope: me
});
},
applyIndexBar: function(indexBar) {
if (this.getGrouped()) {
return Ext.factory(indexBar, Ext.dataview.IndexBar, this.getIndexBar());
}
},
updateIndexBar: function(indexBar) {
if (indexBar && this.getScrollable()) {
this.getScrollableBehavior().getScrollView().getElement().insertFirst(indexBar.renderElement);
indexBar.on({
index: 'onIndex',
scope: this
});
}
},
updatePinHeaders: function(pinnedHeaders) {
var scrollable = this.getScrollable(),
store = this.getStore(),
scrollView = this.getScrollableBehavior().getScrollView(),
scrollViewElement = scrollView.getElement(),
header, scroller;
if (scrollable && this.getGrouped()) {
scroller = scrollable.getScroller();
if (pinnedHeaders) {
scroller.on({
refresh: 'doRefreshHeaders',
scroll: 'onScroll',
scope: this
});
store.on({
datachanged: 'doRefreshHeaders',
scope: this
});
this.header = header = Ext.create('Ext.dataview.ListItemHeader', {html: ' ', cls: 'x-list-header-swap'});
scrollViewElement.dom.insertBefore(header.element.dom, scroller.getContainer().dom.nextSibling);
} else {
scroller.un({
refresh: 'onScrollerRefresh',
scroll: 'onScroll',
scope: this
});
store.un({
datachanged: 'doRefreshHeaders',
scope: this
});
if (this.header) {
this.header.destroy();
}
}
}
},
// @private
getClosestGroups : function() {
var groups = this.pinHeaderInfo.offsets,
pos = this.getScrollable().getScroller().position,
ln = groups.length,
group, i,
current, next;
for (i = 0; i < ln; i++) {
group = groups[i];
if (group.offset > pos.y) {
next = group;
break;
}
current = group;
}
return {
current: current,
next: next
};
},
doRefreshHeaders: function() {
var headerIndicis = this.previousHeaderIndices,
ln = headerIndicis.length,
items = this.getViewItems(),
headerInfo = this.pinHeaderInfo = {offsets: []},
headerOffsets = headerInfo.offsets,
i, headerItem, header;
if (ln) {
for (i = 0; i < ln; i++) {
headerItem = items[headerIndicis[i].index];
header = this.getItemHeader(headerItem);
headerOffsets.push({
header: header,
offset: headerItem.element.dom.offsetTop
});
header.element.setVisibilityMode(Ext.Element.VISIBILITY);
}
headerInfo.headerHeight = header.element.getHeight();
headerInfo.closest = this.getClosestGroups();
this.setActiveGroup(headerInfo.closest.current);
}
},
getItemHeader: function(item) {
return item.getHeader();
},
onScroll: function(scroller, x, y) {
var me = this,
headerInfo = me.pinHeaderInfo,
closest = headerInfo.closest,
activeGroup = me.activeGroup,
headerHeight = headerInfo.headerHeight,
next = closest.next,
current = closest.current,
header = this.header;
if (y <= 0) {
if (activeGroup) {
me.setActiveGroup(false);
closest.next = current;
}
return;
}
else if (
(next && y > next.offset) ||
(y < current.offset)
) {
closest = headerInfo.closest = this.getClosestGroups();
next = closest.next;
current = closest.current;
this.setActiveGroup(current);
}
if (next && y > 0 && next.offset - y <= headerHeight) {
var transform = headerHeight - (next.offset - y)
// @TODO: use top/left on Android
header.element.dom.style.webkitTransform = 'translate3d(0px, -' + transform + 'px, 0px)';
this.transformed = true;
}
else if (this.transformed) {
header.element.dom.style.webkitTransform = null;
this.transformed = false;
}
},
/**
* Set the current active group
* @param {Object} group The group to set active
* @private
*/
setActiveGroup : function(group) {
var me = this;
if (group) {
if (!me.activeGroup || me.activeGroup.header != group.header) {
me.header.setHtml(group.header.getHtml());
me.header.show();
}
} else {
me.header.hide();
}
this.activeGroup = group;
},
onIndex: function(index) {
var key = index.toLowerCase(),
store = this.getStore(),
groups = store.getGroups(),
ln = groups.length,
group, i, closest, id, item;
for (i = 0; i < ln; i++) {
group = groups[i];
id = group.name.toLowerCase();
if (id == key || id > key) {
closest = group;
break;
}
else {
closest = group;
}
}
item = this.getViewItems()[store.indexOf(closest.children[0])];
this.getScrollable().getScroller().scrollTo(0, item.element.dom.offsetTop);
},
applyOnItemDisclosure: function(config) {
if (Ext.isFunction(config)) {
return {
scope: this,
handler: config
};
}
if (Ext.isObject(config)) {
return config;
}
return null;
},
getDisclosure: function() {
var value = this._disclosure,
onItemDisclosure = this.getOnItemDisclosure();
if (onItemDisclosure && onItemDisclosure != value) {
value = true;
this.setDisclosure(value);
}
return value;
},
updateOnItemDisclosure: function(newOnItemDisclosure) {
// If we have an onItemDisclosure configuration, force disclose config to true
if (newOnItemDisclosure) {
this.setDisclosure(true);
}
},
handleItemDisclosure: function(disclosure, e) {
var me = this,
listItem = disclosure.ownerCt,
index = me.getViewItems().indexOf(listItem),
record = me.getStore().getAt(index),
onItemDisclosure = me.getOnItemDisclosure();
if (me.getPreventSelectionOnDisclose()) {
e.stopEvent();
}
me.fireAction('disclose', [record, listItem, index, e], 'doDisclose');
if (onItemDisclosure && onItemDisclosure.handler) {
onItemDisclosure.handler.call(me, record, listItem, index);
}
},
doDisclose: Ext.emptyFn,
updateItemTpl: function(newTpl) {
this.getItemConfig().tpl = newTpl;
},
getItemConfig: function() {
var me = this,
config, tpl;
if (!me._isItemConfigInitialized) {
this_isItemConfigInitialized = true;
me.setItemConfig(me.config.itemConfig);
}
config = me._itemConfig;
tpl = me.getItemTpl();
if (tpl) {
config.tpl = tpl;
}
return config;
},
getDataItemConfig: function(xtype, record, itemConfig) {
return {
xtype: xtype,
record: record,
defaults: itemConfig,
disclosure: this.getDisclosure(),
icon: this.getIcon()
};
},
findGroupHeaderIndices: function() {
if (!this.getGrouped()) {
return;
}
var me = this,
store = me.getStore(),
groups = store.getGroups(),
groupLn = groups.length,
items = me.getViewItems(),
i = 0,
previousHeaderIndices = me.previousHeaderIndices,
previousIndexLn = previousHeaderIndices.length,
newHeaderIndices = [],
firstGroupedRecord, index, oldItemWithHeader;
// Add header to an item if needed
for (; i < groupLn; i++) {
firstGroupedRecord = groups[i].children[0];
index = store.indexOf(firstGroupedRecord);
if (previousHeaderIndices.indexOf(firstGroupedRecord) == -1) {
me.doAddHeader(items[index], store.getGroupString(firstGroupedRecord));
}
newHeaderIndices.push(firstGroupedRecord);
}
// Remove header from an item if needed
for (i = 0; i < previousIndexLn; i++) {
oldItemWithHeader = previousHeaderIndices[i];
if (newHeaderIndices.indexOf(oldItemWithHeader) == -1) {
oldItemWithHeader = items[store.indexOf(oldItemWithHeader)];
if (oldItemWithHeader) {
me.doRemoveHeader(oldItemWithHeader);
}
}
}
me.previousHeaderIndices = newHeaderIndices;
},
doAddHeader: function(item, html) {
item.setHeader({
html: html
});
},
doRemoveHeader: function(item) {
item.setHeader(null);
},
doRefresh: function() {
this.callParent(arguments);
this.findGroupHeaderIndices();
},
onStoreAdd: function() {
this.callParent(arguments);
this.findGroupHeaderIndices();
},
onStoreRemove: function() {
this.callParent(arguments);
this.findGroupHeaderIndices();
},
onStoreUpdate: function() {
this.callParent(arguments);
this.findGroupHeaderIndices();
}
});