1
+ /**
2
+ * @class Ext.ux.grid.plugin.MultipleSort
3
+ * @extends Ext.toolbar.Toolbar
4
+ *
5
+ * @author Harald Hanek (c) 2012
6
+ * @license http://harrydeluxe.mit-license.org
7
+ *
8
+ * This plugin is based on Senchas multiple grid sorting example.
9
+ * @see http://dev.sencha.com/deploy/ext-4.1.0-gpl/examples/grid/multiple-sorting.html
10
+ */
11
+ Ext . define ( 'Ext.ux.grid.plugin.MultipleSort' , {
12
+ extend : 'Ext.toolbar.Toolbar' ,
13
+ alias : 'plugin.ux.multiplesort' ,
14
+ require : [ 'Ext.ux.BoxReorderer' ,
15
+ 'Ext.ux.ToolbarDroppable' ,
16
+ 'Ext.menu.Menu' ] ,
17
+
18
+ clone : function ( )
19
+ {
20
+ return {
21
+ init : Ext . emptyFn
22
+ } ;
23
+ } ,
24
+
25
+ layout : {
26
+ overflowHandler : 'Menu'
27
+ } ,
28
+
29
+ minHeight : 29 ,
30
+
31
+ autoHide : false ,
32
+
33
+ removeText : 'remove' ,
34
+
35
+ removeAllText : 'remove all' ,
36
+
37
+ initComponent : function ( )
38
+ {
39
+ var me = this ;
40
+
41
+ me . reorderer = new Ext . ux . BoxReorderer ( {
42
+ listeners : {
43
+ scope : me ,
44
+ Drop : function ( r , c , button )
45
+ { // update sort direction when button is dropped
46
+ me . changeSortDirection ( button , false ) ;
47
+ }
48
+ }
49
+ } ) ;
50
+
51
+ me . droppable = new Ext . ux . ToolbarDroppable ( {
52
+ /**
53
+ * Creates the new toolbar item from the drop event
54
+ */
55
+ createItem : function ( data )
56
+ {
57
+ var header = data . header ,
58
+ headerCt = header . ownerCt ,
59
+ reorderer = headerCt . reorderer ;
60
+
61
+ // Hide the drop indicators of the standard
62
+ // HeaderDropZone
63
+ // in case user had a pending valid drop in
64
+ if ( reorderer )
65
+ {
66
+ reorderer . dropZone . invalidateDrop ( ) ;
67
+ }
68
+
69
+ return me . createSorterButtonConfig ( {
70
+ text : header . text ,
71
+ sortData : {
72
+ property : header . dataIndex ,
73
+ direction : "ASC"
74
+ }
75
+ } ) ;
76
+ } ,
77
+
78
+ /**
79
+ * Custom canDrop implementation which returns true
80
+ * if a column can be added to the toolbar
81
+ *
82
+ * @param {Object } data Arbitrary data from the drag
83
+ * source. For a HeaderContainer, it will
84
+ * contain a header property which is the
85
+ * Header being dragged.
86
+ * @return {Boolean } True if the drop is allowed
87
+ */
88
+ canDrop : function ( dragSource , event , data )
89
+ {
90
+ var sorters = me . getSorters ( ) ,
91
+ header = data . header ,
92
+ length = sorters . length ,
93
+ entryIndex = this . calculateEntryIndex ( event ) ,
94
+ targetItem = this . toolbar . getComponent ( entryIndex ) ,
95
+ i ;
96
+
97
+ // Group columns have no dataIndex and therefore
98
+ // cannot be sorted
99
+ // If target isn't reorderable it could not be
100
+ // replaced
101
+ if ( ! header . dataIndex || ( targetItem && targetItem . reorderable === false ) )
102
+ return false ;
103
+
104
+ for ( i = 0 ; i < length ; i ++ )
105
+ {
106
+ if ( sorters [ i ] . property == header . dataIndex )
107
+ return false ;
108
+ }
109
+
110
+ return true ;
111
+ } ,
112
+
113
+ afterLayout : me . doSort
114
+ } ) ;
115
+
116
+ me . plugins = [ me . reorderer ,
117
+ me . droppable ] ;
118
+
119
+ Ext . each ( me . items , function ( item )
120
+ {
121
+ if ( item . sortData )
122
+ Ext . applyIf ( item , me . createSorterButtonConfig ( item ) ) ;
123
+ } , me ) ;
124
+
125
+ me . callParent ( arguments ) ;
126
+ } ,
127
+
128
+ init : function ( grid )
129
+ {
130
+ var me = this ;
131
+
132
+ if ( ! me . grid )
133
+ me . grid = grid ;
134
+
135
+ me . grid . on ( 'render' , me . onGridRender , me , {
136
+ single : true
137
+ } ) ;
138
+ } ,
139
+
140
+ onGridRender : function ( grid )
141
+ {
142
+ grid . on ( 'afterlayout' , this . onGridAfterLayout , this , {
143
+ single : true
144
+ } ) ;
145
+ } ,
146
+
147
+ onGridAfterLayout : function ( grid )
148
+ {
149
+ var me = this ,
150
+ headerCt = ( grid . ownerCt . lockedGrid ) ? grid . ownerCt . lockedGrid . headerCt : grid . headerCt ,
151
+ dragZone = headerCt . reorderer . dragZone ;
152
+
153
+ /**
154
+ * stops here if the toolbar is never rendered!
155
+ * @todo addDocked add toolbar to grid
156
+ */
157
+ if ( ! me . rendered )
158
+ return false ;
159
+
160
+ me . droppable . addDDGroup ( dragZone . ddGroup ) ;
161
+
162
+ dragZone . self . override (
163
+ {
164
+ onStartDrag : function ( )
165
+ {
166
+ if ( me . autoHide && me . isHidden ( ) )
167
+ {
168
+ me . show ( null , function ( ) {
169
+ Ext . dd . DragDropManager . refreshCache ( dragZone . groups ) ;
170
+ } , me ) ;
171
+ }
172
+ } ,
173
+ onEndDrag : function ( data , e )
174
+ {
175
+ if ( me . autoHide && me . getSorters ( ) . length == 0 )
176
+ {
177
+ me . hide ( ) ;
178
+ }
179
+ }
180
+
181
+ } ) ;
182
+
183
+ me . doSort ( ) ;
184
+ } ,
185
+
186
+ /**
187
+ * Tells the store to sort itself according to our sort data
188
+ */
189
+ doSort : function ( )
190
+ {
191
+ var me = this . toolbar ? this . toolbar : this ,
192
+ sorters = me . getSorters ( ) ;
193
+
194
+ if ( sorters . length >= 1 )
195
+ {
196
+ me . show ( ) ;
197
+ me . grid . store . sort ( sorters ) ;
198
+ }
199
+ else
200
+ {
201
+ if ( me . autoHide )
202
+ me . hide ( ) ;
203
+ }
204
+ } ,
205
+
206
+ /**
207
+ * Callback handler used when a sorter button is clicked or
208
+ * reordered
209
+ *
210
+ * @param {Ext.Button } button The button that was clicked
211
+ * @param {Boolean } changeDirection True to change direction
212
+ * (default). Set to false for reorder operations as we
213
+ * wish to preserve ordering there
214
+ */
215
+ changeSortDirection : function ( button , changeDirection )
216
+ {
217
+ var sortData = button . sortData ,
218
+ iconCls = button . iconCls ;
219
+
220
+ if ( sortData )
221
+ {
222
+ if ( changeDirection !== false )
223
+ {
224
+ button . sortData . direction = Ext . String . toggle ( button . sortData . direction , "ASC" , "DESC" ) ;
225
+ button . setIconCls ( Ext . String . toggle ( iconCls , "sort-asc" , "sort-desc" ) ) ;
226
+ }
227
+
228
+ this . grid . store . clearFilter ( ) ;
229
+ this . doSort ( ) ;
230
+ }
231
+ } ,
232
+
233
+ /**
234
+ * Returns an array of sortData from the sorter buttons
235
+ *
236
+ * @return {Array } Ordered sort data from each of the sorter buttons
237
+ */
238
+ getSorters : function ( )
239
+ {
240
+ var sorters = [ ] ;
241
+
242
+ Ext . each ( this . query ( 'button' ) , function ( button )
243
+ {
244
+ if ( button . sortData )
245
+ sorters . push ( button . sortData ) ;
246
+ } , this ) ;
247
+
248
+ return sorters ;
249
+ } ,
250
+
251
+ /**
252
+ * Convenience function for creating Toolbar Buttons that are tied
253
+ * to sorters
254
+ *
255
+ * @param {Object } config Optional config object
256
+ * @return {Object } The new Button configuration
257
+ */
258
+ createSorterButtonConfig : function ( config )
259
+ {
260
+ var me = this ;
261
+
262
+ if ( ! config . sortData . direction )
263
+ config . sortData . direction = 'ASC' ;
264
+
265
+ Ext . applyIf ( config , {
266
+ listeners : {
267
+ click : function ( button , event )
268
+ {
269
+ me . changeSortDirection ( button , true ) ;
270
+ } ,
271
+
272
+ render : function ( button )
273
+ {
274
+ var removeAllItem = new Ext . menu . Item ( {
275
+ text : me . removeAllText ,
276
+ handler : function ( )
277
+ {
278
+ Ext . each ( me . query ( 'button' ) , function ( button )
279
+ {
280
+ if ( button . sortData )
281
+ me . remove ( button ) ;
282
+ } , me ) ;
283
+
284
+ me . doSort ( ) ;
285
+ }
286
+ } ) ,
287
+ contextMenu = new Ext . menu . Menu ( {
288
+ showSeparator : false ,
289
+ items : [ {
290
+ text : me . removeText ,
291
+ handler : function ( )
292
+ {
293
+ me . remove ( button ) ;
294
+ me . doSort ( ) ;
295
+ }
296
+ } , removeAllItem ]
297
+ } ) ,
298
+ el = button . getEl ( ) ;
299
+
300
+ el . on ( 'contextmenu' , function ( event ) {
301
+ var sorters = me . getSorters ( ) ;
302
+
303
+ removeAllItem . setVisible ( sorters . length > 1 ) ;
304
+ event . stopEvent ( ) ;
305
+ contextMenu . showAt ( event . getXY ( ) ) ;
306
+ } ) ;
307
+ } ,
308
+
309
+ scope : me
310
+ } ,
311
+ iconCls : 'sort-' + config . sortData . direction . toLowerCase ( ) ,
312
+ reorderable : true ,
313
+ xtype : 'button'
314
+ } ) ;
315
+
316
+ return config ;
317
+ }
318
+ } ) ;
0 commit comments