]> git.proxmox.com Git - extjs.git/blob - extjs/modern/modern/src/Sortable.js
add extjs 6.0.1 sources
[extjs.git] / extjs / modern / modern / src / Sortable.js
1 /**
2 * A mixin which allows a data component to be sorted
3 * @ignore
4 */
5 Ext.define('Ext.Sortable', {
6 mixins: {
7 observable: 'Ext.mixin.Observable'
8 },
9
10 requires: ['Ext.util.Draggable'],
11
12 config: {
13 /**
14 * @cfg
15 * @inheritdoc
16 */
17 baseCls: Ext.baseCSSPrefix + 'sortable',
18
19 /**
20 * @cfg {Number} delay
21 * How many milliseconds a user must hold the draggable before starting a
22 * drag operation.
23 * @private
24 * @accessor
25 */
26 delay: 0
27
28 },
29
30 /**
31 * @cfg {String} direction
32 * Possible values: 'vertical', 'horizontal'.
33 */
34 direction: 'vertical',
35
36 /**
37 * @cfg {String} cancelSelector
38 * A simple CSS selector that represents elements within the draggable
39 * that should NOT initiate a drag.
40 */
41 cancelSelector: null,
42
43 // not yet implemented
44 //indicator: true,
45 //proxy: true,
46 //tolerance: null,
47
48 /**
49 * @cfg {HTMLElement/Boolean} constrain
50 * An Element to constrain the Sortable dragging to.
51 * If `true` is specified, the dragging will be constrained to the element
52 * of the sortable.
53 */
54 constrain: window,
55 /**
56 * @cfg {String} group
57 * Draggable and Droppable objects can participate in a group which are
58 * capable of interacting.
59 */
60 group: 'base',
61
62 /**
63 * @cfg {Boolean} revert
64 * This should NOT be changed.
65 * @private
66 */
67 revert: true,
68
69 /**
70 * @cfg {String} itemSelector
71 * A simple CSS selector that represents individual items within the Sortable.
72 */
73 itemSelector: null,
74
75 /**
76 * @cfg {String} handleSelector
77 * A simple CSS selector to indicate what is the handle to drag the Sortable.
78 */
79 handleSelector: null,
80
81 /**
82 * @cfg {Boolean} disabled
83 * Passing in `true` will disable this Sortable.
84 */
85 disabled: false,
86
87 // Properties
88
89 /**
90 * Read-only property that indicates whether a Sortable is currently sorting.
91 * @type Boolean
92 * @private
93 * @readonly
94 */
95 sorting: false,
96
97 /**
98 * Read-only value representing whether the Draggable can be moved vertically.
99 * This is automatically calculated by Draggable by the direction configuration.
100 * @type Boolean
101 * @private
102 * @readonly
103 */
104 vertical: false,
105
106 /**
107 * Creates new Sortable.
108 * @param {Mixed} el
109 * @param {Object} config
110 */
111 constructor : function(el, config) {
112 config = config || {};
113 Ext.apply(this, config);
114
115 this.addEvents(
116 /**
117 * @event sortstart
118 * @param {Ext.Sortable} this
119 * @param {Ext.event.Event} e
120 */
121 'sortstart',
122 /**
123 * @event sortend
124 * @param {Ext.Sortable} this
125 * @param {Ext.event.Event} e
126 */
127 'sortend',
128 /**
129 * @event sortchange
130 * @param {Ext.Sortable} this
131 * @param {Ext.Element} el The Element being dragged.
132 * @param {Number} index The index of the element after the sort change.
133 */
134 'sortchange'
135
136 // not yet implemented.
137 // 'sortupdate',
138 // 'sortreceive',
139 // 'sortremove',
140 // 'sortenter',
141 // 'sortleave',
142 // 'sortactivate',
143 // 'sortdeactivate'
144 );
145
146 this.el = Ext.get(el);
147 this.callParent();
148
149 this.mixins.observable.constructor.call(this);
150
151 if (this.direction == 'horizontal') {
152 this.horizontal = true;
153 }
154 else if (this.direction == 'vertical') {
155 this.vertical = true;
156 }
157 else {
158 this.horizontal = this.vertical = true;
159 }
160
161 this.el.addCls(this.baseCls);
162 this.startEventName = (this.getDelay() > 0) ? 'taphold' : 'tapstart';
163 if (!this.disabled) {
164 this.enable();
165 }
166 },
167
168 /**
169 * @private
170 */
171 onStart : function(e, t) {
172 if (this.cancelSelector && e.getTarget(this.cancelSelector)) {
173 return;
174 }
175 if (this.handleSelector && !e.getTarget(this.handleSelector)) {
176 return;
177 }
178
179 if (!this.sorting) {
180 this.onSortStart(e, t);
181 }
182 },
183
184 /**
185 * @private
186 */
187 onSortStart : function(e, t) {
188 this.sorting = true;
189 var draggable = Ext.create('Ext.util.Draggable', t, {
190 threshold: 0,
191 revert: this.revert,
192 direction: this.direction,
193 constrain: this.constrain === true ? this.el : this.constrain,
194 animationDuration: 100
195 });
196 draggable.on({
197 drag: this.onDrag,
198 dragend: this.onDragEnd,
199 scope: this
200 });
201
202 this.dragEl = t;
203 this.calculateBoxes();
204
205 if (!draggable.dragging) {
206 draggable.onStart(e);
207 }
208
209 this.fireEvent('sortstart', this, e);
210 },
211
212 /**
213 * @private
214 */
215 calculateBoxes : function() {
216 this.items = [];
217 var els = this.el.select(this.itemSelector, false),
218 ln = els.length, i, item, el, box;
219
220 for (i = 0; i < ln; i++) {
221 el = els[i];
222 if (el != this.dragEl) {
223 item = Ext.fly(el).getRegion();
224 item.el = el;
225 this.items.push(item);
226 }
227 }
228 },
229
230 /**
231 * @private
232 */
233 onDrag : function(draggable, e) {
234 var items = this.items,
235 ln = items.length,
236 region = draggable.region,
237 sortChange = false,
238 i, intersect, overlap, item;
239
240 for (i = 0; i < ln; i++) {
241 item = items[i];
242 intersect = region.intersect(item);
243 if (intersect) {
244 if (this.vertical && Math.abs(intersect.top - intersect.bottom) > (region.bottom - region.top) / 2) {
245 if (region.bottom > item.top && item.top > region.top) {
246 draggable.el.insertAfter(item.el);
247 }
248 else {
249 draggable.el.insertBefore(item.el);
250 }
251 sortChange = true;
252 }
253 else if (this.horizontal && Math.abs(intersect.left - intersect.right) > (region.right - region.left) / 2) {
254 if (region.right > item.left && item.left > region.left) {
255 draggable.el.insertAfter(item.el);
256 }
257 else {
258 draggable.el.insertBefore(item.el);
259 }
260 sortChange = true;
261 }
262
263 if (sortChange) {
264 // We reset the draggable (initializes all the new start values)
265 draggable.reset();
266
267 // Move the draggable to its current location (since the transform is now
268 // different)
269 draggable.moveTo(region.left, region.top);
270
271 // Finally lets recalculate all the items boxes
272 this.calculateBoxes();
273 this.fireEvent('sortchange', this, draggable.el, this.el.select(this.itemSelector, false).indexOf(draggable.el.dom));
274 return;
275 }
276 }
277 }
278 },
279
280 /**
281 * @private
282 */
283 onDragEnd : function(draggable, e) {
284 draggable.destroy();
285 this.sorting = false;
286 this.fireEvent('sortend', this, draggable, e);
287 },
288
289 /**
290 * Enables sorting for this Sortable.
291 * This method is invoked immediately after construction of a Sortable unless
292 * the disabled configuration is set to `true`.
293 */
294 enable : function() {
295 this.el.on(this.startEventName, this.onStart, this, {delegate: this.itemSelector, holdThreshold: this.getDelay()});
296 this.disabled = false;
297 },
298
299 /**
300 * Disables sorting for this Sortable.
301 */
302 disable : function() {
303 this.el.un(this.startEventName, this.onStart, this);
304 this.disabled = true;
305 },
306
307 /**
308 * Method to determine whether this Sortable is currently disabled.
309 * @return {Boolean} The disabled state of this Sortable.
310 */
311 isDisabled: function() {
312 return this.disabled;
313 },
314
315 /**
316 * Method to determine whether this Sortable is currently sorting.
317 * @return {Boolean} The sorting state of this Sortable.
318 */
319 isSorting : function() {
320 return this.sorting;
321 },
322
323 /**
324 * Method to determine whether this Sortable is currently disabled.
325 * @return {Boolean} The disabled state of this Sortable.
326 */
327 isVertical : function() {
328 return this.vertical;
329 },
330
331 /**
332 * Method to determine whether this Sortable is currently sorting.
333 * @return {Boolean} The sorting state of this Sortable.
334 */
335 isHorizontal : function() {
336 return this.horizontal;
337 }
338 });