// vim: sw=4:ts=4:nu:nospell:fdc=4
/*global Ext:true, GD:true */
/*jslint browser: true, devel:true, sloppy: true, white: true, plusplus: true */
/**
* # Grid MultiSort Feature by Jozok
*
* @author Jozef Kejst
* @date 24.2.2015
* @copyright (c) 2014, Jozef Kejst
* @license This file is proprietary and it is only
* meant to be run as a part of Sencha Examples application.
* All other uses (reading, copying, reverse engineering
* to name a few) are prohibited.
*/
Ext.define('Ext.jozok.grid.MultiSort',{
extend:'Ext.grid.feature.Feature'
,alternateClassName:'Ext.ux.grid.MultiSort'
,alias:['feature.ux-gmsrt','feature.jozok-gmsrt']
/**
* @cfg removeSortText
*/
,removeSortText:"Remove from Sort"
/**
* @cfg displaySortOrder
* flag to display counters in column header for sorting order
*/
,displaySortOrder:false
,config:{
/**
* sortersCount
* total count of sortered columns
*/
sortersCount:0
}
/**
* function init
* Attach events to view and view header container
*/
,init:function(grid) {
var me = this
,view = me.view
,headerCt = view.headerCt
;
me.callParent(arguments);
view.on({
afterrender:{
fn:me.afterViewRender
,scope:me
}
});
headerCt.on({
headerclick:{
fn:me.onHeaderClick
,scope:me
}
});
headerCt.onHeaderCtEvent = function(e,t) {
var oldSortClickVal = headerCt.sortOnClick;
if(e.altKey) {
headerCt.sortOnClick = false;
}
Ext.grid.header.Container.prototype.onHeaderCtEvent.apply(headerCt,[e,t]);
headerCt.sortOnClick = oldSortClickVal;
}
}
/**
* function afterViewRender
* change sorting grid panel submenu and add new for remove column from sorting
* update sortered columns in grid (depend on stateful grid property)
*
*/
,afterViewRender:function() {
var me = this;
me.injectMultiSortMenu();
if(!me.grid.viewModel) {
me.updateSorteredColumns();
} else {
me.setSortersCount(0);
}
}
* function onHeaderClick
* listener for grid header click event
*/
,onHeaderClick:function(ct,column,e) {
var me = this
,store = me.view.getStore()
,sorter
,direction = 'ASC'
,version = Ext.versions.extjs.major
;
// first remove old labels from column header
me.removeOldSorterLabels();
// shift pressed has special functionality only in extjs 5
if(e.altKey && version > 4) {
// check if column is in sorters
sorter = store.sorters.getByKey(column.dataIndex);
if (sorter) {
// change direction only
if (sorter.direction !== 'DESC') {
sorter.setDirection('DESC');
} else {
sorter.setDirection('ASC');
}
column.setSortState(sorter);
store.sort();
} else {
// add to sorting with ASC direction
column.setSortState(sorter);
column.enableRemSortItem = true;
me.setSortersCount(me.getSortersCount()+1);
store.sort(column.dataIndex,direction,'append');
}
}
me.updateSorteredColumns();
}
* function injectMultiSortMenu
*/
,injectMultiSortMenu:function() {
var me = this
,headerCt = me.view.headerCt
;
if (headerCt.sortable) {
headerCt.getMenuItems = me.getMenuItems();
headerCt.getMenu().on({
beforeshow:function(menu) {
menu.down('#remSortItem').setDisabled(!menu.activeHeader.enableRemSortItem);
}
});
}
}// eo function injectMultiSortMenu
/**
* function removeOldSorterlabels
*/
,removeOldSorterLabels:function() {
var me = this
,headerCt = me.view.headerCt
;
Ext.each(headerCt.getGridColumns(),function(column){
column.enableRemSortItem = false;
if (me.displaySortOrder) {
column.setText(column.text.split(' <small>(')[0]);
}
});
}
/**
* function updateSorteredColumns
* function to update counter labels and enable/disable remove sort submenu for sortered columns
*/
,updateSorteredColumns:function() {
var me = this
,store = me.view.getStore()
,activeHeader
,sorters = store.sorters
,sorter
,text
,dataIndex
,i
;
me.setSortersCount(sorters.getCount());
for (i=0;i<me.getSortersCount();i++) {
sorter = sorters.getAt(i);
dataIndex = sorter.property||sorter.getProperty();
activeHeader = me.view.headerCt.down('[dataIndex=' + dataIndex + ']');
if (activeHeader) {
activeHeader.enableRemSortItem = true;
//activeHeader.setSortState(sorter.direction,true,true);
me.setSortState(activeHeader,sorter);
if (me.displaySortOrder) {
text = activeHeader.text.split(' <small>');
activeHeader.setText(text[0] + ' <small>(' + (i+1) + ')</small>');
}
}
}
}// eo function updateSorteredColumns
* function getMenuItems
* update exisiting grid column menu with new functionality
*/
,getMenuItems:function() {
var me = this
,headerCt = me.view.headerCt
,getMenuItems = headerCt.getMenuItems
;
// runs in the scope of headerCt
return function() {
// We cannot use the method from HeaderContainer's prototype here
// because other plugins or features may already have injected an implementation
var o = getMenuItems.call(this)
,index = o.length
;
Ext.each(o,function(item,idx){
if(item.itemId === 'ascItem') {
item.text = 'Add ' + item.text;
item.handler = me.onSortAscClick;
item.scope = me;
}
if(item.itemId === 'descItem') {
item.text = 'Add ' + item.text;
item.handler = me.onSortDescClick;
item.scope = me;
index = idx+1;
}
});
Ext.Array.insert(o,index,[{
itemId:'remSortItem'
,text:me.removeSortText
,iconCls:me.menuSortDescCls
,handler:me.onRemoveSortClick
,disabled:true
,scope:me
}]);
return o;
};
}// eo function getMenuItems
/**
* function onRemoveSortClick
* remove one column form sorting
*/
,onRemoveSortClick:function() {
var me = this
,headerCt = me.view.headerCt
,menu = headerCt.getMenu()
,activeHeader = menu.activeHeader
,dataIndex = activeHeader.dataIndex
,store = me.view.getStore()
,sorters = store.sorters
,text = activeHeader.text.split(' <small>(')
,ascCls = activeHeader.ascSortCls
,descCls = activeHeader.descSortCls
,version = Ext.versions.extjs.major
,sorter
;
//activeHeader.setSortState(null);
activeHeader.removeCls([ascCls, descCls]);
sorter = sorters.getByKey(dataIndex);
if (sorter) {
// sencha ext 5.0.1 bug
if(!(version<5)) {
sorter.isFilter = true;
}
sorters.removeAtKey(dataIndex);
}
headerCt.fireEvent('sortchange', headerCt, activeHeader);
if (version<5) {
store.sort();
}
// for counters displayed we must set new order to column
// or only remove enableRemSort submenu for this column
if (me.displaySortOrder) {
activeHeader.setText(text[0]);
me.updateSorteredColumns();
}
activeHeader.enableRemSortItem = false;
// if sorters is empty we load remote sorting store without sorting
if (sorters.length === 0 && store.remoteSort) {
store.load();
}
}// eo function onRemoveSortClick
/**
* function onSortClick
* add new column to sorting
* @param bool direction
*/
,onSortClick:function(direction) {
var me = this
,menu = me.view.headerCt.getMenu()
,activeHeader = menu.activeHeader
,dataIndex = activeHeader.dataIndex
,text = activeHeader.text.split(' <small>(')
,store = me.view.getStore()
,sorters = store.sorters
,sorter = sorters.getByKey(dataIndex)
,version = Ext.versions.extjs.major
;
if (version<5) {
activeHeader.setSortState(direction,true,true);
} else if (sorter) {
activeHeader.setSortState(sorter);
}
activeHeader.enableRemSortItem = true;
// special case is when we change sort direction
// selected column will be move to last position and we must update counter labels
if (sorter) {
sorters.removeAtKey(dataIndex);
if (me.displaySortOrder) {
activeHeader.setText(text[0]);
me.updateSorteredColumns();
}
}
me.setSortersCount(me.getSortersCount()+1);
if (me.displaySortOrder) {
activeHeader.setText(activeHeader.text + ' <small>(' + me.getSortersCount() + ')</small>');
}
if (version<5) {
sorters.add(Ext.create('Ext.util.Sorter',{property:dataIndex,direction:direction,id:dataIndex}));
if (store.remoteSort) {
store.sort();
} else {
me.sortLocalSortingStore();
}
} else {
store.sort(dataIndex,direction,'append');
}
}// eo function onSortClick
/**
* function onSortAscClick
* listeners for sort submenu
*/
,onSortAscClick:function() {
this.onSortClick('ASC');
}// eo function onSortAscClick
/**
* function onSortDescClick
* listener for sort submenu
*/
,onSortDescClick:function() {
this.onSortClick('DESC');
}// eo function onSortDescClick
,setSortState:function(column,sorter) {
var version = Ext.versions.extjs.major;
if (version<5) {
column.setSortState(sorter.direction,true,true);
} else if (sorter) {
column.setSortState(sorter);
}
}
/**
* function sortLocalSortingStore
*/
,sortLocalSortingStore:function() {
var me = this
,store = me.view.getStore()
,sorters = store.sorters
,sorter
,sortItems = []
,i
,property
,direction
;
for (i=0;i<sorters.getCount();i++) {
sorter = sorters.getAt(i);
property = sorter.property||sorter.getProperty();
direction = sorter.direction||sorter.getDirection();
sortItems.push({property:property,direction:direction});
}
sortItems.push({property:property,direction:direction});
store.sort(sortItems);
}
});
// eof