328 lines
8.9 KiB
JavaScript
328 lines
8.9 KiB
JavaScript
/**
|
|
* A mixin which allows a data component to be sorted
|
|
* @ignore
|
|
*/
|
|
Ext.define('Ext.Sortable', {
|
|
mixins: {
|
|
observable: 'Ext.mixin.Observable'
|
|
},
|
|
|
|
requires: ['Ext.util.Draggable'],
|
|
|
|
config: {
|
|
/**
|
|
* @cfg
|
|
* @inheritdoc
|
|
*/
|
|
baseCls: Ext.baseCSSPrefix + 'sortable',
|
|
|
|
/**
|
|
* @cfg {Number} delay
|
|
* How many milliseconds a user must hold the draggable before starting a
|
|
* drag operation.
|
|
* @private
|
|
* @accessor
|
|
*/
|
|
delay: 0
|
|
|
|
},
|
|
|
|
/**
|
|
* @cfg {String} direction
|
|
* Possible values: 'vertical', 'horizontal'.
|
|
*/
|
|
direction: 'vertical',
|
|
|
|
/**
|
|
* @cfg {String} cancelSelector
|
|
* A simple CSS selector that represents elements within the draggable
|
|
* that should NOT initiate a drag.
|
|
*/
|
|
cancelSelector: null,
|
|
|
|
// not yet implemented
|
|
//indicator: true,
|
|
//proxy: true,
|
|
//tolerance: null,
|
|
|
|
/**
|
|
* @cfg {HTMLElement/Boolean} constrain
|
|
* An Element to constrain the Sortable dragging to.
|
|
* If `true` is specified, the dragging will be constrained to the element
|
|
* of the sortable.
|
|
*/
|
|
constrain: window,
|
|
/**
|
|
* @cfg {String} group
|
|
* Draggable and Droppable objects can participate in a group which are
|
|
* capable of interacting.
|
|
*/
|
|
group: 'base',
|
|
|
|
/**
|
|
* @cfg {Boolean} revert
|
|
* This should NOT be changed.
|
|
* @private
|
|
*/
|
|
revert: true,
|
|
|
|
/**
|
|
* @cfg {String} itemSelector
|
|
* A simple CSS selector that represents individual items within the Sortable.
|
|
*/
|
|
itemSelector: null,
|
|
|
|
/**
|
|
* @cfg {String} handleSelector
|
|
* A simple CSS selector to indicate what is the handle to drag the Sortable.
|
|
*/
|
|
handleSelector: null,
|
|
|
|
/**
|
|
* @cfg {Boolean} disabled
|
|
* Passing in `true` will disable this Sortable.
|
|
*/
|
|
disabled: false,
|
|
|
|
// Properties
|
|
|
|
/**
|
|
* Read-only property that indicates whether a Sortable is currently sorting.
|
|
* @type Boolean
|
|
* @private
|
|
* @readonly
|
|
*/
|
|
sorting: false,
|
|
|
|
/**
|
|
* Read-only value representing whether the Draggable can be moved vertically.
|
|
* This is automatically calculated by Draggable by the direction configuration.
|
|
* @type Boolean
|
|
* @private
|
|
* @readonly
|
|
*/
|
|
vertical: false,
|
|
|
|
/**
|
|
* Creates new Sortable.
|
|
* @param {Mixed} el
|
|
* @param {Object} config
|
|
*/
|
|
constructor : function(el, config) {
|
|
config = config || {};
|
|
Ext.apply(this, config);
|
|
|
|
this.addEvents(
|
|
/**
|
|
* @event sortstart
|
|
* @param {Ext.Sortable} this
|
|
* @param {Ext.event.Event} e
|
|
*/
|
|
'sortstart',
|
|
/**
|
|
* @event sortend
|
|
* @param {Ext.Sortable} this
|
|
* @param {Ext.event.Event} e
|
|
*/
|
|
'sortend',
|
|
/**
|
|
* @event sortchange
|
|
* @param {Ext.Sortable} this
|
|
* @param {Ext.Element} el The Element being dragged.
|
|
* @param {Number} index The index of the element after the sort change.
|
|
*/
|
|
'sortchange'
|
|
|
|
// not yet implemented.
|
|
// 'sortupdate',
|
|
// 'sortreceive',
|
|
// 'sortremove',
|
|
// 'sortenter',
|
|
// 'sortleave',
|
|
// 'sortactivate',
|
|
// 'sortdeactivate'
|
|
);
|
|
|
|
this.el = Ext.get(el);
|
|
this.callParent();
|
|
|
|
this.mixins.observable.constructor.call(this);
|
|
|
|
if (this.direction == 'horizontal') {
|
|
this.horizontal = true;
|
|
}
|
|
else if (this.direction == 'vertical') {
|
|
this.vertical = true;
|
|
}
|
|
else {
|
|
this.horizontal = this.vertical = true;
|
|
}
|
|
|
|
this.el.addCls(this.baseCls);
|
|
this.startEventName = (this.getDelay() > 0) ? 'taphold' : 'tapstart';
|
|
if (!this.disabled) {
|
|
this.enable();
|
|
}
|
|
},
|
|
|
|
// @private
|
|
onStart : function(e, t) {
|
|
if (this.cancelSelector && e.getTarget(this.cancelSelector)) {
|
|
return;
|
|
}
|
|
if (this.handleSelector && !e.getTarget(this.handleSelector)) {
|
|
return;
|
|
}
|
|
|
|
if (!this.sorting) {
|
|
this.onSortStart(e, t);
|
|
}
|
|
},
|
|
|
|
// @private
|
|
onSortStart : function(e, t) {
|
|
this.sorting = true;
|
|
var draggable = Ext.create('Ext.util.Draggable', t, {
|
|
threshold: 0,
|
|
revert: this.revert,
|
|
direction: this.direction,
|
|
constrain: this.constrain === true ? this.el : this.constrain,
|
|
animationDuration: 100
|
|
});
|
|
draggable.on({
|
|
drag: this.onDrag,
|
|
dragend: this.onDragEnd,
|
|
scope: this
|
|
});
|
|
|
|
this.dragEl = t;
|
|
this.calculateBoxes();
|
|
|
|
if (!draggable.dragging) {
|
|
draggable.onStart(e);
|
|
}
|
|
|
|
this.fireEvent('sortstart', this, e);
|
|
},
|
|
|
|
// @private
|
|
calculateBoxes : function() {
|
|
this.items = [];
|
|
var els = this.el.select(this.itemSelector, false),
|
|
ln = els.length, i, item, el, box;
|
|
|
|
for (i = 0; i < ln; i++) {
|
|
el = els[i];
|
|
if (el != this.dragEl) {
|
|
item = Ext.fly(el).getPageBox(true);
|
|
item.el = el;
|
|
this.items.push(item);
|
|
}
|
|
}
|
|
},
|
|
|
|
// @private
|
|
onDrag : function(draggable, e) {
|
|
var items = this.items,
|
|
ln = items.length,
|
|
region = draggable.region,
|
|
sortChange = false,
|
|
i, intersect, overlap, item;
|
|
|
|
for (i = 0; i < ln; i++) {
|
|
item = items[i];
|
|
intersect = region.intersect(item);
|
|
if (intersect) {
|
|
if (this.vertical && Math.abs(intersect.top - intersect.bottom) > (region.bottom - region.top) / 2) {
|
|
if (region.bottom > item.top && item.top > region.top) {
|
|
draggable.el.insertAfter(item.el);
|
|
}
|
|
else {
|
|
draggable.el.insertBefore(item.el);
|
|
}
|
|
sortChange = true;
|
|
}
|
|
else if (this.horizontal && Math.abs(intersect.left - intersect.right) > (region.right - region.left) / 2) {
|
|
if (region.right > item.left && item.left > region.left) {
|
|
draggable.el.insertAfter(item.el);
|
|
}
|
|
else {
|
|
draggable.el.insertBefore(item.el);
|
|
}
|
|
sortChange = true;
|
|
}
|
|
|
|
if (sortChange) {
|
|
// We reset the draggable (initializes all the new start values)
|
|
draggable.reset();
|
|
|
|
// Move the draggable to its current location (since the transform is now
|
|
// different)
|
|
draggable.moveTo(region.left, region.top);
|
|
|
|
// Finally lets recalculate all the items boxes
|
|
this.calculateBoxes();
|
|
this.fireEvent('sortchange', this, draggable.el, this.el.select(this.itemSelector, false).indexOf(draggable.el.dom));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// @private
|
|
onDragEnd : function(draggable, e) {
|
|
draggable.destroy();
|
|
this.sorting = false;
|
|
this.fireEvent('sortend', this, draggable, e);
|
|
},
|
|
|
|
/**
|
|
* Enables sorting for this Sortable.
|
|
* This method is invoked immediately after construction of a Sortable unless
|
|
* the disabled configuration is set to `true`.
|
|
*/
|
|
enable : function() {
|
|
this.el.on(this.startEventName, this.onStart, this, {delegate: this.itemSelector, holdThreshold: this.getDelay()});
|
|
this.disabled = false;
|
|
},
|
|
|
|
/**
|
|
* Disables sorting for this Sortable.
|
|
*/
|
|
disable : function() {
|
|
this.el.un(this.startEventName, this.onStart, this);
|
|
this.disabled = true;
|
|
},
|
|
|
|
/**
|
|
* Method to determine whether this Sortable is currently disabled.
|
|
* @return {Boolean} The disabled state of this Sortable.
|
|
*/
|
|
isDisabled: function() {
|
|
return this.disabled;
|
|
},
|
|
|
|
/**
|
|
* Method to determine whether this Sortable is currently sorting.
|
|
* @return {Boolean} The sorting state of this Sortable.
|
|
*/
|
|
isSorting : function() {
|
|
return this.sorting;
|
|
},
|
|
|
|
/**
|
|
* Method to determine whether this Sortable is currently disabled.
|
|
* @return {Boolean} The disabled state of this Sortable.
|
|
*/
|
|
isVertical : function() {
|
|
return this.vertical;
|
|
},
|
|
|
|
/**
|
|
* Method to determine whether this Sortable is currently sorting.
|
|
* @return {Boolean} The sorting state of this Sortable.
|
|
*/
|
|
isHorizontal : function() {
|
|
return this.horizontal;
|
|
}
|
|
}); |