|
|
|
@ -19,30 +19,12 @@
|
|
|
|
|
})(function (/** Sortable */Sortable) { |
|
|
|
|
'use strict'; |
|
|
|
|
|
|
|
|
|
var _nextSibling; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Simple and easy mixin-wrapper for rubaxa/Sortable library, in order to |
|
|
|
|
* make reorderable drag-and-drop lists on modern browsers and touch devices. |
|
|
|
|
* |
|
|
|
|
* @mixin |
|
|
|
|
*/ |
|
|
|
|
var SortableMixin = { |
|
|
|
|
sortableMixinVersion: '0.0.0', |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @type {Sortable} |
|
|
|
|
* @private |
|
|
|
|
*/ |
|
|
|
|
_sortableInstance: null, |
|
|
|
|
var _activeComponent; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Sortable options |
|
|
|
|
* @returns {object} |
|
|
|
|
*/ |
|
|
|
|
getDefaultProps: function () { |
|
|
|
|
return { |
|
|
|
|
sortable: { |
|
|
|
|
var _defaultOptions = { |
|
|
|
|
ref: 'list', |
|
|
|
|
model: 'items', |
|
|
|
|
|
|
|
|
@ -54,65 +36,100 @@
|
|
|
|
|
onRemove: 'handleRemove', |
|
|
|
|
onSort: 'handleSort', |
|
|
|
|
onFilter: 'handleFilter' |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
componentDidMount: function () { |
|
|
|
|
var nextSibling, |
|
|
|
|
sortableProps = this.props.sortable, |
|
|
|
|
sortableOptions = {}, |
|
|
|
|
|
|
|
|
|
callMethod = function (/** string */type, /** Event */evt) { |
|
|
|
|
var method = this[sortableProps[type]]; |
|
|
|
|
method && method.call(this, evt, this._sortableInstance); |
|
|
|
|
}.bind(this); |
|
|
|
|
function _getModelName(component) { |
|
|
|
|
return component.sortableOptions && component.sortableOptions.model || _defaultOptions.model; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Pass through unrecognized options
|
|
|
|
|
for (var key in sortableProps) { |
|
|
|
|
sortableOptions[key] = sortableProps[key]; |
|
|
|
|
function _getModelItems(component) { |
|
|
|
|
return component.state[_getModelName(component)].slice(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Bind callbacks so that "this" refers to the component
|
|
|
|
|
'onEnd onAdd onUpdate onRemove onFilter'.split(' ').forEach(function (/** string */name) { |
|
|
|
|
if (sortableProps[name]) { |
|
|
|
|
sortableOptions[name] = callMethod.bind(this, name); |
|
|
|
|
function _extend(dst, src) { |
|
|
|
|
for (var key in src) { |
|
|
|
|
if (src.hasOwnProperty(key)) { |
|
|
|
|
dst[key] = src[key]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}.bind(this)); |
|
|
|
|
|
|
|
|
|
return dst; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sortableOptions.onStart = function (/** Event */evt) { |
|
|
|
|
nextSibling = evt.item.nextSibling; |
|
|
|
|
callMethod('onStart', evt); |
|
|
|
|
}.bind(this); |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Simple and easy mixin-wrapper for rubaxa/Sortable library, in order to |
|
|
|
|
* make reorderable drag-and-drop lists on modern browsers and touch devices. |
|
|
|
|
* |
|
|
|
|
* @mixin |
|
|
|
|
*/ |
|
|
|
|
var SortableMixin = { |
|
|
|
|
sortableMixinVersion: '0.1.0', |
|
|
|
|
|
|
|
|
|
sortableOptions.onSort = function (/** Event */evt) { |
|
|
|
|
evt.from.insertBefore(evt.item, nextSibling || null); |
|
|
|
|
|
|
|
|
|
var modelName = sortableProps.model, |
|
|
|
|
newState = {}, |
|
|
|
|
items = this.state[modelName]; |
|
|
|
|
/** |
|
|
|
|
* @type {Sortable} |
|
|
|
|
* @private |
|
|
|
|
*/ |
|
|
|
|
_sortableInstance: null, |
|
|
|
|
|
|
|
|
|
if (items) { |
|
|
|
|
items = items.slice(); // clone
|
|
|
|
|
items.splice(evt.newIndex, 0, items.splice(evt.oldIndex, 1)[0]); |
|
|
|
|
|
|
|
|
|
newState[modelName] = items; |
|
|
|
|
componentDidMount: function () { |
|
|
|
|
var options = _extend(_extend({}, _defaultOptions), this.sortableOptions || {}), |
|
|
|
|
copyOptions = _extend({}, options), |
|
|
|
|
|
|
|
|
|
emitEvent = function (/** string */type, /** Event */evt) { |
|
|
|
|
var method = this[options[type]]; |
|
|
|
|
method && method.call(this, evt, this._sortableInstance); |
|
|
|
|
}.bind(this); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Bind callbacks so that "this" refers to the component
|
|
|
|
|
'onStart onEnd onAdd onSort onUpdate onRemove onFilter'.split(' ').forEach(function (/** string */name) { |
|
|
|
|
copyOptions[name] = function (evt) { |
|
|
|
|
if (name === 'onStart') { |
|
|
|
|
_nextSibling = evt.item.nextElementSibling; |
|
|
|
|
_activeComponent = this; |
|
|
|
|
} |
|
|
|
|
else if (name === 'onAdd' || name === 'onUpdate') { |
|
|
|
|
evt.from.insertBefore(evt.item, _nextSibling); |
|
|
|
|
|
|
|
|
|
var newState = {}, |
|
|
|
|
remoteState = {}, |
|
|
|
|
oldIndex = evt.oldIndex, |
|
|
|
|
newIndex = evt.newIndex, |
|
|
|
|
items = _getModelItems(this), |
|
|
|
|
remoteItems, |
|
|
|
|
item; |
|
|
|
|
|
|
|
|
|
if (name === 'onAdd') { |
|
|
|
|
remoteItems = _getModelItems(_activeComponent); |
|
|
|
|
item = remoteItems.splice(oldIndex, 1)[0]; |
|
|
|
|
items.splice(newIndex, 0, item); |
|
|
|
|
|
|
|
|
|
remoteState[_getModelName(_activeComponent)] = remoteItems; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
items.splice(newIndex, 0, items.splice(oldIndex, 1)[0]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
newState[_getModelName(this)] = items; |
|
|
|
|
this.setState(newState); |
|
|
|
|
(this !== _activeComponent) && _activeComponent.setState(remoteState); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
callMethod('onSort', evt); |
|
|
|
|
setTimeout(function () { |
|
|
|
|
emitEvent(name, evt); |
|
|
|
|
}, 0); |
|
|
|
|
}.bind(this); |
|
|
|
|
}, this); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @namespace this.refs — http://facebook.github.io/react/docs/more-about-refs.html */ |
|
|
|
|
if (!sortableProps.ref || this.refs[sortableProps.ref]) { |
|
|
|
|
this._sortableInstance = Sortable.create((this.refs[sortableProps.ref] || this).getDOMNode(), sortableOptions); |
|
|
|
|
} |
|
|
|
|
this._sortableInstance = Sortable.create((this.refs[options.ref] || this).getDOMNode(), copyOptions); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|