Skip to content

Commit 264f48d

Browse files
committedJul 4, 2016
v0.1.1
0 parents  commit 264f48d

File tree

5 files changed

+356
-0
lines changed

5 files changed

+356
-0
lines changed
 

‎.editorconfig

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = tab
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true
10+
11+
[*.md]
12+
trim_trailing_whitespace = false

‎.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
mock.png
3+
.*.sw*
4+
.build*
5+
jquery.fn.*

‎README.md

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# THIS PROJECT NEEDS A MAINTAINER.
2+
3+
---
4+
5+
react-mixin-sortablejs
6+
-------------------
7+
React mixin for [SortableJS](https://github.com/RubaXa/Sortable/).
8+
9+
Demo: http://rubaxa.github.io/Sortable/
10+
11+
12+
<a name="react"></a>
13+
### Support React
14+
Include [react-sortable-mixin.js](react-sortable-mixin.js).
15+
See [more options](react-sortable-mixin.js#L26).
16+
17+
18+
```jsx
19+
var SortableList = React.createClass({
20+
mixins: [SortableMixin],
21+
22+
getInitialState: function() {
23+
return {
24+
items: ['Mixin', 'Sortable']
25+
};
26+
},
27+
28+
handleSort: function (/** Event */evt) { /*..*/ },
29+
30+
render: function() {
31+
return <ul>{
32+
this.state.items.map(function (text) {
33+
return <li>{text}</li>
34+
})
35+
}</ul>
36+
}
37+
});
38+
39+
React.render(<SortableList />, document.body);
40+
41+
42+
//
43+
// Groups
44+
//
45+
var AllUsers = React.createClass({
46+
mixins: [SortableMixin],
47+
48+
sortableOptions: {
49+
ref: "user",
50+
group: "shared",
51+
model: "users"
52+
},
53+
54+
getInitialState: function() {
55+
return { users: ['Abbi', 'Adela', 'Bud', 'Cate', 'Davis', 'Eric']; };
56+
},
57+
58+
render: function() {
59+
return (
60+
<h1>Users</h1>
61+
<ul ref="user">{
62+
this.state.users.map(function (text) {
63+
return <li>{text}</li>
64+
})
65+
}</ul>
66+
);
67+
}
68+
});
69+
70+
var ApprovedUsers = React.createClass({
71+
mixins: [SortableMixin],
72+
sortableOptions: { group: "shared" },
73+
74+
getInitialState: function() {
75+
return { items: ['Hal', 'Judy']; };
76+
},
77+
78+
render: function() {
79+
return <ul>{
80+
this.state.items.map(function (text) {
81+
return <li>{text}</li>
82+
})
83+
}</ul>
84+
}
85+
});
86+
87+
React.render(<div>
88+
<AllUsers/>
89+
<hr/>
90+
<ApprovedUsers/>
91+
</div>, document.body);
92+
```
93+
94+
### Support React ES2015 / TypeScript syntax
95+
As mixins are not supported in ES2015 / TypeScript syntax here is example of ES2015 ref based implementation.
96+
Using refs is the preferred (by facebook) "escape hatch" to underlaying DOM nodes: [React: The ref Callback Attribute](https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute)
97+
98+
```js
99+
import * as React from "react";
100+
import Sortable from 'sortablejs';
101+
102+
export class SortableExampleEsnext extends React.Component {
103+
104+
sortableContainersDecorator = (componentBackingInstance) => {
105+
// check if backing instance not null
106+
if (componentBackingInstance) {
107+
let options = {
108+
handle: ".group-title" // Restricts sort start click/touch to the specified element
109+
};
110+
Sortable.create(componentBackingInstance, options);
111+
}
112+
};
113+
114+
sortableGroupDecorator = (componentBackingInstance) => {
115+
// check if backing instance not null
116+
if (componentBackingInstance) {
117+
let options = {
118+
draggable: "div", // Specifies which items inside the element should be sortable
119+
group: "shared"
120+
};
121+
Sortable.create(componentBackingInstance, options);
122+
}
123+
};
124+
125+
render() {
126+
return (
127+
<div className="container" ref={this.sortableContainersDecorator}>
128+
<div className="group">
129+
<h2 className="group-title">Group 1</h2>
130+
<div className="group-list" ref={this.sortableGroupDecorator}>
131+
<div>Swap them around</div>
132+
<div>Swap us around</div>
133+
<div>Swap things around</div>
134+
<div>Swap everything around</div>
135+
</div>
136+
</div>
137+
<div className="group">
138+
<h2 className="group-title">Group 2</h2>
139+
<div className="group-list" ref={this.sortableGroupDecorator}>
140+
<div>Swap them around</div>
141+
<div>Swap us around</div>
142+
<div>Swap things around</div>
143+
<div>Swap everything around</div>
144+
</div>
145+
</div>
146+
</div>
147+
);
148+
}
149+
}
150+
```

‎package.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "react-mixin-sortablejs",
3+
"version": "0.1.1",
4+
"description": "React mixin for SortableJS.",
5+
"main": "react-mixin-sortable.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/SortableJS/react-mixin-sortablejs.git"
12+
},
13+
"keywords": [
14+
"react",
15+
"mixin",
16+
"sortable"
17+
],
18+
"author": "RubaXa <trash@rubaxa.org>",
19+
"license": "MIT",
20+
"bugs": {
21+
"url": "https://github.com/SortableJS/react-mixin-sortablejs/issues"
22+
},
23+
"homepage": "https://github.com/SortableJS/react-mixin-sortablejs#readme"
24+
}

‎react-mixin-sortable.js

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**
2+
* @author RubaXa <trash@rubaxa.org>
3+
* @licence MIT
4+
*/
5+
6+
(function (factory) {
7+
'use strict';
8+
9+
if (typeof module != 'undefined' && typeof module.exports != 'undefined') {
10+
module.exports = factory(require('./Sortable'));
11+
}
12+
else if (typeof define === 'function' && define.amd) {
13+
define(['./Sortable'], factory);
14+
}
15+
else {
16+
/* jshint sub:true */
17+
window['SortableMixin'] = factory(Sortable);
18+
}
19+
})(function (/** Sortable */Sortable) {
20+
'use strict';
21+
22+
var _nextSibling;
23+
24+
var _activeComponent;
25+
26+
var _defaultOptions = {
27+
ref: 'list',
28+
model: 'items',
29+
30+
animation: 100,
31+
onStart: 'handleStart',
32+
onEnd: 'handleEnd',
33+
onAdd: 'handleAdd',
34+
onUpdate: 'handleUpdate',
35+
onRemove: 'handleRemove',
36+
onSort: 'handleSort',
37+
onFilter: 'handleFilter',
38+
onMove: 'handleMove'
39+
};
40+
41+
42+
function _getModelName(component) {
43+
return component.sortableOptions && component.sortableOptions.model || _defaultOptions.model;
44+
}
45+
46+
47+
function _getModelItems(component) {
48+
var name = _getModelName(component),
49+
items = component.state && component.state[name] || component.props[name];
50+
51+
return items.slice();
52+
}
53+
54+
55+
function _extend(dst, src) {
56+
for (var key in src) {
57+
if (src.hasOwnProperty(key)) {
58+
dst[key] = src[key];
59+
}
60+
}
61+
62+
return dst;
63+
}
64+
65+
66+
/**
67+
* Simple and easy mixin-wrapper for rubaxa/Sortable library, in order to
68+
* make reorderable drag-and-drop lists on modern browsers and touch devices.
69+
*
70+
* @mixin
71+
*/
72+
var SortableMixin = {
73+
sortableMixinVersion: '0.1.1',
74+
75+
76+
/**
77+
* @type {Sortable}
78+
* @private
79+
*/
80+
_sortableInstance: null,
81+
82+
83+
componentDidMount: function () {
84+
var DOMNode, options = _extend(_extend({}, _defaultOptions), this.sortableOptions || {}),
85+
copyOptions = _extend({}, options),
86+
87+
emitEvent = function (/** string */type, /** Event */evt) {
88+
var method = this[options[type]];
89+
method && method.call(this, evt, this._sortableInstance);
90+
}.bind(this);
91+
92+
93+
// Bind callbacks so that "this" refers to the component
94+
'onStart onEnd onAdd onSort onUpdate onRemove onFilter onMove'.split(' ').forEach(function (/** string */name) {
95+
copyOptions[name] = function (evt) {
96+
if (name === 'onStart') {
97+
_nextSibling = evt.item.nextElementSibling;
98+
_activeComponent = this;
99+
}
100+
else if (name === 'onAdd' || name === 'onUpdate') {
101+
evt.from.insertBefore(evt.item, _nextSibling);
102+
103+
var newState = {},
104+
remoteState = {},
105+
oldIndex = evt.oldIndex,
106+
newIndex = evt.newIndex,
107+
items = _getModelItems(this),
108+
remoteItems,
109+
item;
110+
111+
if (name === 'onAdd') {
112+
remoteItems = _getModelItems(_activeComponent);
113+
item = remoteItems.splice(oldIndex, 1)[0];
114+
items.splice(newIndex, 0, item);
115+
116+
remoteState[_getModelName(_activeComponent)] = remoteItems;
117+
}
118+
else {
119+
items.splice(newIndex, 0, items.splice(oldIndex, 1)[0]);
120+
}
121+
122+
newState[_getModelName(this)] = items;
123+
124+
if (copyOptions.stateHandler) {
125+
this[copyOptions.stateHandler](newState);
126+
} else {
127+
this.setState(newState);
128+
}
129+
130+
(this !== _activeComponent) && _activeComponent.setState(remoteState);
131+
}
132+
133+
setTimeout(function () {
134+
emitEvent(name, evt);
135+
}, 0);
136+
}.bind(this);
137+
}, this);
138+
139+
DOMNode = this.getDOMNode() ? (this.refs[options.ref] || this).getDOMNode() : this.refs[options.ref] || this;
140+
141+
/** @namespace this.refs — http://facebook.github.io/react/docs/more-about-refs.html */
142+
this._sortableInstance = Sortable.create(DOMNode, copyOptions);
143+
},
144+
145+
componentWillReceiveProps: function (nextProps) {
146+
var newState = {},
147+
modelName = _getModelName(this),
148+
items = nextProps[modelName];
149+
150+
if (items) {
151+
newState[modelName] = items;
152+
this.setState(newState);
153+
}
154+
},
155+
156+
componentWillUnmount: function () {
157+
this._sortableInstance.destroy();
158+
this._sortableInstance = null;
159+
}
160+
};
161+
162+
163+
// Export
164+
return SortableMixin;
165+
});

0 commit comments

Comments
 (0)
Please sign in to comment.