Ext.Loader.setConfig({enabled: true}); Ext.Loader.setPath('Ext.ux', '../ux/'); Ext.require([ 'Ext.data.*', 'Ext.grid.*', 'Ext.util.*', 'Ext.toolbar.*', 'Ext.ux.ToolbarDroppable', 'Ext.ux.BoxReorderer' ]); Ext.onReady(function() { //The following functions are used to get the sorting data from the toolbar and apply it to the store /** * Tells the store to sort itself according to our sort data */ function doSort() { store.sort(getSorters()); } /** * Callback handler used when a sorter button is clicked or reordered * @param {Ext.Button} button The button that was clicked * @param {Boolean} changeDirection True to change direction (default). Set to false for reorder * operations as we wish to preserve ordering there */ function changeSortDirection(button, changeDirection) { var sortData = button.sortData, iconCls = button.iconCls; if (sortData) { if (changeDirection !== false) { button.sortData.direction = Ext.String.toggle(button.sortData.direction, "ASC", "DESC"); button.setIconCls(Ext.String.toggle(iconCls, "sort-asc", "sort-desc")); } store.clearFilter(); doSort(); } } /** * Returns an array of sortData from the sorter buttons * @return {Array} Ordered sort data from each of the sorter buttons */ function getSorters() { var sorters = []; Ext.each(tbar.query('button'), function(button) { sorters.push(button.sortData); }, this); return sorters; } /** * Convenience function for creating Toolbar Buttons that are tied to sorters * @param {Object} config Optional config object * @return {Object} The new Button configuration */ function createSorterButtonConfig(config) { config = config || {}; Ext.applyIf(config, { listeners: { click: function(button, e) { changeSortDirection(button, true); } }, iconCls: 'sort-' + config.sortData.direction.toLowerCase(), reorderable: true, xtype: 'button' }); return config; } /** * Returns an array of fake data * @param {Number} count The number of fake rows to create data for * @return {Array} The fake record data, suitable for usage with an ArrayReader */ function createFakeData(count) { var firstNames = ['Ed', 'Tommy', 'Aaron', 'Abe', 'Jamie', 'Adam', 'Dave', 'David', 'Jay', 'Nicolas', 'Nige'], lastNames = ['Spencer', 'Maintz', 'Conran', 'Elias', 'Avins', 'Mishcon', 'Kaneda', 'Davis', 'Robinson', 'Ferrero', 'White'], ratings = [1, 2, 3, 4, 5], salaries = [100, 400, 900, 1500, 1000000]; var data = []; for (var i = 0; i < (count || 25); i++) { var ratingId = Math.floor(Math.random() * ratings.length), salaryId = Math.floor(Math.random() * salaries.length), firstNameId = Math.floor(Math.random() * firstNames.length), lastNameId = Math.floor(Math.random() * lastNames.length), rating = ratings[ratingId], salary = salaries[salaryId], name = Ext.String.format("{0} {1}", firstNames[firstNameId], lastNames[lastNameId]); data.push([rating, salary, name]); } return data; } // create the data store Ext.define('Employee', { extend: 'Ext.data.Model', fields: [ {name: 'rating', type: 'int'}, {name: 'salary', type: 'float'}, {name: 'name'} ] }); var store = Ext.create('Ext.data.Store', { model: 'Employee', proxy: { type: 'memory', data: createFakeData(25), reader: { type: 'array' } }, autoLoad: true }); var reorderer = Ext.create('Ext.ux.BoxReorderer', { listeners: { scope: this, Drop: function(r, c, button) { //update sort direction when button is dropped changeSortDirection(button, false); } } }); var droppable = Ext.create('Ext.ux.ToolbarDroppable', { /** * Creates the new toolbar item from the drop event */ createItem: function(data) { var header = data.header, headerCt = header.ownerCt, reorderer = headerCt.reorderer; // Hide the drop indicators of the standard HeaderDropZone // in case user had a pending valid drop in if (reorderer) { reorderer.dropZone.invalidateDrop(); } return createSorterButtonConfig({ text: header.text, sortData: { property: header.dataIndex, direction: "ASC" } }); }, /** * Custom canDrop implementation which returns true if a column can be added to the toolbar * @param {Object} data Arbitrary data from the drag source. For a HeaderContainer, it will * contain a header property which is the Header being dragged. * @return {Boolean} True if the drop is allowed */ canDrop: function(dragSource, event, data) { var sorters = getSorters(), header = data.header, length = sorters.length, entryIndex = this.calculateEntryIndex(event), targetItem = this.toolbar.getComponent(entryIndex), i; // Group columns have no dataIndex and therefore cannot be sorted // If target isn't reorderable it could not be replaced if (!header.dataIndex || (targetItem && targetItem.reorderable === false)) { return false; } for (i = 0; i < length; i++) { if (sorters[i].property == header.dataIndex) { return false; } } return true; }, afterLayout: doSort }); //create the toolbar with the 2 plugins var tbar = Ext.create('Ext.toolbar.Toolbar', { items : [{ xtype: 'tbtext', text: 'Sorting order:', reorderable: false }, createSorterButtonConfig({ text: 'Rating', sortData: { property: 'rating', direction: 'DESC' } }), createSorterButtonConfig({ text: 'Salary', sortData: { property: 'salary', direction: 'ASC' } })], plugins: [reorderer, droppable] }); // create the Grid var grid = Ext.create('Ext.grid.Panel', { tbar : tbar, store: store, columns: [ { text: 'Name', flex:1 , sortable: false, dataIndex: 'name' },{ text: 'Rating', width: 125, sortable: false, dataIndex: 'rating' },{ text: 'Salary', width: 125, sortable: false, dataIndex: 'salary', align: 'right', renderer: Ext.util.Format.usMoney } ], stripeRows: true, height: 350, width : 600, title : 'Array Grid', renderTo: 'grid-example', listeners: { scope: this, // wait for the first layout to access the headerCt (we only want this once): single: true, // tell the toolbar's droppable plugin that it accepts items from the columns' dragdrop group afterlayout: function(grid) { var headerCt = grid.child("headercontainer"); droppable.addDDGroup(headerCt.reorderer.dragZone.ddGroup); doSort(); } } }); });