diff --git a/index.html b/index.html index 6b7a46decc1a3f42a0359f24cf1fd12fbaeb3891..3e8334644a83ae6d5869c25d93039cba2eebec6e 100644 --- a/index.html +++ b/index.html @@ -210,14 +210,6 @@ - - -
-
-
-
- -
diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js index 5415a13d52325c144f9026b7bc59cb8e2a99ab76..72a215deb185c2f52ae9722c6ef1da7447ea9632 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 f6e121ddeb28ec03e2a8d17c792b0179a1bcd477..fe50fe9d7713fcd45d6f0f2a82cfd70b9fab9fb5 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')); - }); })();