diff --git a/index.html b/index.html
index 6b7a46d..3e83346 100644
--- a/index.html
+++ b/index.html
@@ -210,14 +210,6 @@
-
-
-
-
-
diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js
index 5415a13..72a215d 100644
--- a/react-sortable-mixin.js
+++ b/react-sortable-mixin.js
@@ -19,6 +19,46 @@
})(function (/** Sortable */Sortable) {
'use strict';
+ var _nextSibling;
+
+ var _activeComponent;
+
+
+ var _defaultOptions = {
+ ref: 'list',
+ model: 'items',
+
+ animation: 100,
+ onStart: 'handleStart',
+ onEnd: 'handleEnd',
+ onAdd: 'handleAdd',
+ onUpdate: 'handleUpdate',
+ onRemove: 'handleRemove',
+ onSort: 'handleSort',
+ onFilter: 'handleFilter'
+ };
+
+
+ function _getModelName(component) {
+ return component.sortableOptions && component.sortableOptions.model || _defaultOptions.model;
+ }
+
+
+ function _getModelItems(component) {
+ return component.state[_getModelName(component)].slice();
+ }
+
+
+ function _extend(dst, src) {
+ for (var key in src) {
+ if (src.hasOwnProperty(key)) {
+ dst[key] = src[key];
+ }
+ }
+
+ return dst;
+ }
+
/**
* Simple and easy mixin-wrapper for rubaxa/Sortable library, in order to
@@ -27,7 +67,8 @@
* @mixin
*/
var SortableMixin = {
- sortableMixinVersion: '0.0.0',
+ sortableMixinVersion: '0.1.0',
+
/**
* @type {Sortable}
@@ -36,83 +77,59 @@
_sortableInstance: null,
- /**
- * Sortable options
- * @returns {object}
- */
- getDefaultProps: function () {
- return {
- sortable: {
- ref: 'list',
- model: 'items',
-
- animation: 100,
- onStart: 'handleStart',
- onEnd: 'handleEnd',
- onAdd: 'handleAdd',
- onUpdate: 'handleUpdate',
- onRemove: 'handleRemove',
- onSort: 'handleSort',
- onFilter: 'handleFilter'
- }
- };
- },
-
-
componentDidMount: function () {
- var nextSibling,
- sortableProps = this.props.sortable,
- sortableOptions = {},
+ var options = _extend(_extend({}, _defaultOptions), this.sortableOptions || {}),
+ copyOptions = _extend({}, options),
- callMethod = function (/** string */type, /** Event */evt) {
- var method = this[sortableProps[type]];
+ emitEvent = function (/** string */type, /** Event */evt) {
+ var method = this[options[type]];
method && method.call(this, evt, this._sortableInstance);
}.bind(this);
- // Pass through unrecognized options
- for (var key in sortableProps) {
- sortableOptions[key] = sortableProps[key];
- }
-
-
// 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);
- }
- }.bind(this));
-
-
- sortableOptions.onStart = function (/** Event */evt) {
- nextSibling = evt.item.nextSibling;
- callMethod('onStart', evt);
- }.bind(this);
-
-
- sortableOptions.onSort = function (/** Event */evt) {
- evt.from.insertBefore(evt.item, nextSibling || null);
-
- var modelName = sortableProps.model,
- newState = {},
- items = this.state[modelName];
-
- if (items) {
- items = items.slice(); // clone
- items.splice(evt.newIndex, 0, items.splice(evt.oldIndex, 1)[0]);
-
- newState[modelName] = items;
- this.setState(newState);
- }
-
- callMethod('onSort', evt);
- }.bind(this);
+ '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);
+ }
+
+ 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);
},
diff --git a/st/app.js b/st/app.js
index f6e121d..fe50fe9 100644
--- a/st/app.js
+++ b/st/app.js
@@ -195,43 +195,6 @@
$scope.sortableConfig['on' + name] = console.log.bind(console, name);
});
}]);
-
-
-
- // React
- loadScripts({
- 'React': '//fb.me/react-0.12.2.js',
- 'SortableMixin': 'react-sortable-mixin.js'
- }, function (React, SortableMixin) {
- var SortableList = React.createClass({
- mixins: [SortableMixin],
-
- getInitialState: function() {
- return {
- items: [
- 'Mixin',
- 'Sortable'
- ]
- };
- },
-
- render: function() {
- return React.DOM.div(null,
- React.DOM.h4({ children: 'React mixin', className: 'layer title title_xl', style: { marginBottom: 0 } }),
- React.DOM.div({ style: { width: '30%', marginLeft: '10px', cursor: 'move' }, className: 'block__list_words' },
- React.DOM.ul({
- ref: 'list',
- children: this.state.items.map(function (v) {
- return React.DOM.li(null, v);
- })
- })
- )
- );
- }
- });
-
- React.render(React.createElement(SortableList, {}), byId('react-box'));
- });
})();