]>
git.proxmox.com Git - extjs.git/blob - extjs/packages/core/src/util/Sortable.js
2 * A mixin which allows a data component to be sorted. This is used by e.g. {@link Ext.data.Store}
3 * and {@link Ext.data.TreeStore}.
5 * **NOTE**: This mixin is mainly for internal use and most users should not need to use it
6 * directly. It is more likely you will want to use one of the component classes that import
7 * this mixin, such as {@link Ext.data.Store} or {@link Ext.data.TreeStore}.
9 Ext
.define("Ext.util.Sortable", {
11 * @property {Boolean} isSortable
12 * `true` in this class to identify an object as an instantiated Sortable, or subclass thereof.
16 $configPrefixed
: false,
21 * @cfg {Ext.util.Sorter[]/Object[]} sorters
22 * The initial set of {@link Ext.util.Sorter Sorters}.
28 * property: 'firstName',
36 * @cfg {String} defaultSortDirection
37 * The default sort direction to use if one is not specified.
39 defaultSortDirection
: "ASC",
47 * Fires before a sort occurs.
48 * @param {Ext.util.Sortable} me This object.
49 * @param {Ext.util.Sorter[]} sorters The collection of Sorters being used to generate
50 * the comparator function.
54 * @cfg {Number} [multiSortLimit=3]
55 * The maximum number of sorters which may be applied to this Sortable when using the "multi"
56 * insertion position when adding sorters.
58 * New sorters added using the "multi" insertion position are inserted at the top of the
59 * sorters list becoming the new primary sort key.
61 * If the sorters collection has grown to longer then **`multiSortLimit`**, then it is trimmed.
68 * Creates a single comparator function which encapsulates the passed Sorter array.
69 * @param {Ext.util.Sorter[]} sorters The sorter set for which to create a comparator
71 * @return {Function} a function, which when passed two comparable objects returns
72 * the result of the whole sorter comparator functions.
74 createComparator: function(sorters
) {
75 return sorters
&& sorters
.length
77 var result
= sorters
[0].sort(r1
, r2
),
78 length
= sorters
.length
,
81 // While we have not established a comparison value,
82 // loop through subsequent sorters asking for a comparison value
83 for (; !result
&& i
< length
; i
++) {
84 result
= sorters
[i
].sort
.call(sorters
[i
], r1
, r2
);
96 * @cfg {String} sortRoot
97 * The property in each item that contains the data to sort.
100 applySorters: function(sorters
) {
104 sortersCollection
= me
.getSorters() || new Ext
.util
.MixedCollection(false, Ext
.returnId
);
106 // We have been configured with a non-default value.
108 sortersCollection
.addAll(me
.decodeSorters(sorters
));
111 return sortersCollection
;
115 * Updates the sorters collection and triggers sorting of this Sortable. Example usage:
117 * //sort by a single field
118 * myStore.sort('myField', 'DESC');
120 * //sorting by multiple fields
129 * Classes which use this mixin must implement a **`soSort`** method which accepts a comparator
130 * function computed from the full sorter set which performs the sort
131 * in an implementation-specific way.
133 * When passing a single string argument to sort, Store maintains a ASC/DESC toggler per field,
136 * store.sort('myField');
137 * store.sort('myField');
139 * Is equivalent to this code, because Store handles the toggling automatically:
141 * store.sort('myField', 'ASC');
142 * store.sort('myField', 'DESC');
144 * @param {String/Ext.util.Sorter[]} [sorters] Either a string name of one of the fields
145 * in this Store's configured {@link Ext.data.Model Model}, or an array of sorter
147 * @param {String} [direction="ASC"] The overall direction to sort the data by.
148 * @param {String} [insertionPosition="replace"] Where to put the new sorter in the collection
149 * of sorters. This may take the following values:
151 * * `replace`: This means that the new sorter(s) becomes the sole sorter set for this Sortable.
152 * This is the most useful call mode to programatically sort by multiple fields.
154 * * `prepend`: This means that the new sorters are inserted as the primary sorters, unchanged,
155 * and the sorter list length must be controlled by the developer.
157 * * `multi`: This is mainly useful for implementing intuitive "Sort by this" user interfaces
158 * such as the {@link Ext.grid.Panel GridPanel}'s column sorting UI. This mode is only
159 * supported when passing a property name and a direction. This means that the new sorter
160 * becomes the primary sorter. If the sorter was **already** the primary sorter, the direction
161 * of sort is toggled if no direction parameter is specified. The number of sorters maintained
162 * is limited by the {@link #multiSortLimit} configuration.
164 * * `append` : This means that the new sorter becomes the last sorter.
165 * @param {Boolean} doSort True to sort using a generated sorter function that combines all
166 * of the Sorters passed
167 * @return {Ext.util.Sorter[]} The new sorters.
169 sort: function(sorters
, direction
, insertionPosition
, doSort
) {
173 currentSorters
= me
.getSorters();
175 if (!currentSorters
) {
177 currentSorters
= me
.getSorters();
180 if (Ext
.isArray(sorters
)) {
181 doSort
= insertionPosition
;
182 insertionPosition
= direction
;
184 else if (Ext
.isObject(sorters
)) {
186 doSort
= insertionPosition
;
187 insertionPosition
= direction
;
189 else if (Ext
.isString(sorters
)) {
190 sorter
= currentSorters
.get(sorters
);
198 else if (direction
== null) {
202 sorter
.setDirection(direction
);
208 if (sorters
&& sorters
.length
) {
209 sorters
= me
.decodeSorters(sorters
);
211 switch (insertionPosition
) {
212 // multi sorting means always inserting the specified sorters
214 // If we are asked to sort by what is already the primary sorter
215 // then toggle its direction.
217 // Insert the new sorter at the beginning.
218 currentSorters
.insert(0, sorters
[0]);
220 // If we now are oversize, trim our sorters collection
221 overFlow
= currentSorters
.getCount() - me
.multiSortLimit
;
224 currentSorters
.removeRange(me
.multiSortLimit
, overFlow
);
230 currentSorters
.insert(0, sorters
);
234 currentSorters
.addAll(sorters
);
240 currentSorters
.clear();
241 currentSorters
.addAll(sorters
);
246 Ext
.raise('Sorter insertion point must be "multi", "prepend", ' +
247 '"append" or "replace"');
252 if (doSort
!== false) {
253 me
.fireEvent('beforesort', me
, sorters
);
254 me
.onBeforeSort(sorters
);
256 if (me
.getSorterCount()) {
257 // Sort using a generated sorter function which combines all of the Sorters passed
258 me
.doSort(me
.generateComparator());
267 * Returns the number of Sorters which apply to this Sortable.
269 * May be overridden in subclasses. {@link Ext.data.Store Store} in particlar overrides
270 * this because its groupers must contribute to the sorter count so that the sort method above
273 getSorterCount: function() {
274 return this.getSorters().items
.length
;
278 * Returns a comparator function which compares two items and returns -1, 0, or 1 depending
279 * on the currently defined set of {@link #cfg-sorters}.
281 * If there are no {@link #cfg-sorters} defined, it returns a function which returns `0` meaning
282 * that no sorting will occur.
284 generateComparator: function() {
285 var sorters
= this.getSorters().getRange();
287 return sorters
.length
? this.createComparator(sorters
) : this.emptyComparator
;
290 emptyComparator: function() {
294 onBeforeSort
: Ext
.emptyFn
,
298 * Normalizes an array of sorter objects, ensuring that they are all Ext.util.Sorter instances
299 * @param {Object[]} sorters The sorters array
300 * @return {Ext.util.Sorter[]} Array of Ext.util.Sorter objects
302 decodeSorters: function(sorters
) {
303 if (!Ext
.isArray(sorters
)) {
304 if (sorters
=== undefined) {
312 // eslint-disable-next-line vars-on-top
313 var length
= sorters
.length
,
314 Sorter
= Ext
.util
.Sorter
,
315 model
= this.getModel
? this.getModel() : this.model
,
319 for (i
= 0; i
< length
; i
++) {
322 if (!(config
instanceof Sorter
)) {
323 if (Ext
.isString(config
)) {
329 Ext
.applyIf(config
, {
334 // support for 3.x style sorters where a function can be defined as 'fn'
336 config
.sorterFn
= config
.fn
;
339 // support a function to be passed as a sorter definition
340 if (typeof config
=== 'function') {
346 // ensure sortType gets pushed on if necessary
347 if (model
&& !config
.transform
) {
348 field
= model
.getField(config
.property
);
349 config
.transform
= field
&& field
.sortType
!== Ext
.identityFn
354 sorters
[i
] = new Ext
.util
.Sorter(config
);
362 * Gets the first sorter from the sorters collection, excluding
363 * any groupers that may be in place
365 * @return {Ext.util.Sorter} The sorter, null if none exist
367 getFirstSorter: function() {
368 var sorters
= this.getSorters().items
,
369 len
= sorters
.length
,
373 for (; i
< len
; ++i
) {
376 if (!sorter
.isGrouper
) {
384 // Reference the static implementation in prototype
385 this.prototype.createComparator
= this.createComparator
;