Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / examples / grid / multiple-sorting.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 Ext.Loader.setConfig({enabled: true});
16
17 Ext.Loader.setPath('Ext.ux', '../ux/');
18
19 Ext.require([
20     'Ext.data.*',
21     'Ext.grid.*',
22     'Ext.util.*',
23     'Ext.toolbar.*',
24     'Ext.ux.ToolbarDroppable',
25     'Ext.ux.BoxReorderer'
26 ]);
27
28 Ext.onReady(function() {
29    //The following functions are used to get the sorting data from the toolbar and apply it to the store
30     /**
31      * Tells the store to sort itself according to our sort data
32      */
33     function doSort() {
34         store.sort(getSorters());
35     }
36
37     /**
38      * Callback handler used when a sorter button is clicked or reordered
39      * @param {Ext.Button} button The button that was clicked
40      * @param {Boolean} changeDirection True to change direction (default). Set to false for reorder
41      * operations as we wish to preserve ordering there
42      */
43     function changeSortDirection(button, changeDirection) {
44         var sortData = button.sortData,
45             iconCls  = button.iconCls;
46         
47         if (sortData) {
48             if (changeDirection !== false) {
49                 button.sortData.direction = Ext.String.toggle(button.sortData.direction, "ASC", "DESC");
50                 button.setIconCls(Ext.String.toggle(iconCls, "sort-asc", "sort-desc"));
51             }
52             store.clearFilter();
53             doSort();
54         }
55     }
56
57     /**
58      * Returns an array of sortData from the sorter buttons
59      * @return {Array} Ordered sort data from each of the sorter buttons
60      */
61     function getSorters() {
62         var sorters = [];
63  
64         Ext.each(tbar.query('button'), function(button) {
65             sorters.push(button.sortData);
66         }, this);
67
68         return sorters;
69     }
70
71     /**
72      * Convenience function for creating Toolbar Buttons that are tied to sorters
73      * @param {Object} config Optional config object
74      * @return {Object} The new Button configuration
75      */
76     function createSorterButtonConfig(config) {
77         config = config || {};
78         Ext.applyIf(config, {
79             listeners: {
80                 click: function(button, e) {
81                     changeSortDirection(button, true);
82                 }
83             },
84             iconCls: 'sort-' + config.sortData.direction.toLowerCase(),
85             reorderable: true,
86             xtype: 'button'
87         });
88         return config;
89     }
90
91     /**
92      * Returns an array of fake data
93      * @param {Number} count The number of fake rows to create data for
94      * @return {Array} The fake record data, suitable for usage with an ArrayReader
95      */
96     function createFakeData(count) {
97         var firstNames   = ['Ed', 'Tommy', 'Aaron', 'Abe', 'Jamie', 'Adam', 'Dave', 'David', 'Jay', 'Nicolas', 'Nige'],
98             lastNames    = ['Spencer', 'Maintz', 'Conran', 'Elias', 'Avins', 'Mishcon', 'Kaneda', 'Davis', 'Robinson', 'Ferrero', 'White'],
99             ratings      = [1, 2, 3, 4, 5],
100             salaries     = [100, 400, 900, 1500, 1000000];
101
102         var data = [];
103         for (var i = 0; i < (count || 25); i++) {
104             var ratingId    = Math.floor(Math.random() * ratings.length),
105                 salaryId    = Math.floor(Math.random() * salaries.length),
106                 firstNameId = Math.floor(Math.random() * firstNames.length),
107                 lastNameId  = Math.floor(Math.random() * lastNames.length),
108
109                 rating      = ratings[ratingId],
110                 salary      = salaries[salaryId],
111                 name        = Ext.String.format("{0} {1}", firstNames[firstNameId], lastNames[lastNameId]);
112
113             data.push([rating, salary, name]);
114         }
115         return data;
116     }
117
118     // create the data store
119     Ext.define('Employee', {
120         extend: 'Ext.data.Model',
121         fields: [
122            {name: 'rating', type: 'int'},
123            {name: 'salary', type: 'float'},
124            {name: 'name'}
125         ]
126     });
127
128     var store = Ext.create('Ext.data.Store', {
129         model: 'Employee',
130         proxy: {
131             type: 'memory',
132             data: createFakeData(25),
133             reader: {
134                 type: 'array'
135             }
136         },
137         autoLoad: true
138     });
139
140     var reorderer = Ext.create('Ext.ux.BoxReorderer', {
141         listeners: {
142             scope: this,
143             Drop: function(r, c, button) { //update sort direction when button is dropped
144                 changeSortDirection(button, false);
145             }
146         }
147     });
148
149     var droppable = Ext.create('Ext.ux.ToolbarDroppable', {
150         /**
151          * Creates the new toolbar item from the drop event
152          */
153         createItem: function(data) {
154             var header = data.header,
155                 headerCt = header.ownerCt,
156                 reorderer = headerCt.reorderer;
157             
158             // Hide the drop indicators of the standard HeaderDropZone
159             // in case user had a pending valid drop in 
160             if (reorderer) {
161                 reorderer.dropZone.invalidateDrop();
162             }
163
164             return createSorterButtonConfig({
165                 text: header.text,
166                 sortData: {
167                     property: header.dataIndex,
168                     direction: "ASC"
169                 }
170             });
171         },
172
173         /**
174          * Custom canDrop implementation which returns true if a column can be added to the toolbar
175          * @param {Object} data Arbitrary data from the drag source. For a HeaderContainer, it will
176          * contain a header property which is the Header being dragged.
177          * @return {Boolean} True if the drop is allowed
178          */
179         canDrop: function(dragSource, event, data) {
180             var sorters = getSorters(),
181                 header  = data.header,
182                 length = sorters.length,
183                 entryIndex = this.calculateEntryIndex(event),
184                 targetItem = this.toolbar.getComponent(entryIndex),
185                 i;
186
187             // Group columns have no dataIndex and therefore cannot be sorted
188             // If target isn't reorderable it could not be replaced
189             if (!header.dataIndex || (targetItem && targetItem.reorderable === false)) {
190                 return false;
191             }
192
193             for (i = 0; i < length; i++) {
194                 if (sorters[i].property == header.dataIndex) {
195                     return false;
196                 }
197             }
198             return true;
199         },
200
201         afterLayout: doSort
202     });
203
204     //create the toolbar with the 2 plugins
205     var tbar = Ext.create('Ext.toolbar.Toolbar', {
206         items  : [{
207             xtype: 'tbtext',
208             text: 'Sorting order:',
209             reorderable: false
210         }, '-'],
211         plugins: [reorderer, droppable]
212     });
213
214     tbar.add(createSorterButtonConfig({
215         text: 'Rating',
216         sortData: {
217             property: 'rating',
218             direction: 'DESC'
219         }
220     }));
221
222     tbar.add(createSorterButtonConfig({
223         text: 'Salary',
224         sortData: {
225             property: 'salary',
226             direction: 'ASC'
227         }
228     }));
229
230     // create the Grid
231     var grid = Ext.create('Ext.grid.Panel', {
232         tbar : tbar,
233         store: store,
234         columns: [
235             {
236                 text: 'Name',
237                 flex:1 ,
238                 sortable: false,
239                 dataIndex: 'name'
240             },{
241                 text: 'Rating',
242                 width: 125,
243                 sortable: false,
244                 dataIndex: 'rating'
245             },{
246                 text: 'Salary',
247                 width: 125,
248                 sortable: false,
249                 dataIndex: 'salary',
250                 align: 'right',
251                 renderer: Ext.util.Format.usMoney
252             }
253         ],
254         stripeRows: true,
255         height: 350,
256         width : 600,
257         title : 'Array Grid',
258         renderTo: 'grid-example',
259         listeners: {
260             scope: this,
261             // wait for the first layout to access the headerCt (we only want this once):
262             single: true,
263             // tell the toolbar's droppable plugin that it accepts items from the columns' dragdrop group
264             afterlayout: function(grid) {
265                 var headerCt = grid.child("headercontainer");
266                 droppable.addDDGroup(headerCt.reorderer.dragZone.ddGroup);
267                 doSort();
268             }
269         }
270     });
271 });