├── .babelrc
├── .gitignore
├── README.md
├── demo.js
├── lib
├── Draggable.js
├── Droppable.js
├── index.js
└── utils.js
├── package-lock.json
├── package.json
├── src
├── Draggable.js
├── Droppable.js
├── index.js
└── utils.js
└── test
└── spec.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@babel/plugin-transform-react-jsx"
4 | ],
5 | "presets": [
6 | [
7 | "@babel/preset-env",
8 | {
9 | "useBuiltIns": "entry"
10 | }
11 | ]
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.swp
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Drag and Drop
2 |
3 | This library contains some very basic draggable and droppable components.
4 |
5 | You probably want to use something more stable and feature rich like [react-dnd](https://github.com/gaearon/react-dnd).
6 |
7 | ## Install
8 |
9 | ```sh
10 | npm install react-drag-and-drop
11 | ```
12 |
13 | ### Use
14 |
15 | ```js
16 | import { Draggable, Droppable } from 'react-drag-and-drop'
17 |
18 | class App extends React.Component {
19 | render() {
20 | return(
21 |
22 |
23 | - Banana
24 | - Apple
25 | - Silver
26 |
27 |
30 |
31 |
32 |
33 | )
34 | }
35 | onDrop(data) {
36 | console.log(data)
37 | // => banana
38 | }
39 | }
40 | ```
41 |
42 | So the idea is that you wrap your components in *Draggable* and *Droppable* containers (instead of using mixins), define *types* and *data* to carry. You can also hook into the different drag events to create more funk. The best way (for now) to figure out how is to peak inside the src
directory. The implementation is quite minimal.
43 |
44 | ## Changelog
45 |
46 | ### 3.0.0
47 |
48 | * Support for React 16
49 | * Updated most dev deps to latest
50 |
51 | ### 2.4.0
52 |
53 | * Support for `wrapperComponent` prop where one can pass a component to be used instead of the standard components for `Draggable` and `Droppable` :tada: Thanks to @aaa707 for this one :rocket:
54 |
55 | ### 2.3.0
56 |
57 | * Support for `enabled` prop for Droppable component
58 |
59 | ### 2.2.0
60 |
61 | * Droppable now accepts `className` as prop - thanks @abdennour :tada:
62 |
63 | ### 2.1.0
64 |
65 | * Support for React 15
66 |
67 | ### 2.0.2
68 |
69 | * Support for enable prop in Draggable component
70 |
71 | ### 2.0.1
72 |
73 | * Did a build (forgot for 2.0.0 release) :facepalm:
74 |
75 | ### 2.0.0
76 |
77 | * Updated to remove warning for React v0.14
78 |
79 | ### 1.1.0
80 |
81 | * Spreading this.props on both Draggable and Droppable
82 |
83 | ### 1.0.1
84 |
85 | * Added FireFox support (DOMStringList -> Array)
86 |
87 | ### 1.0.0
88 |
89 | * Initial release :tada:
90 |
91 | enjoy
92 |
--------------------------------------------------------------------------------
/demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import Draggable from './src/Draggable'
4 | import Droppable from './src/Droppable'
5 |
6 | class Demo extends React.Component {
7 | constructor(props) {
8 | super(props)
9 | this.state = {
10 | draggable : ['drag','us','plz','but-not-me'],
11 | dropped : '',
12 | hovering : false
13 | }
14 | }
15 | render() {
16 | let draggable = this.state.draggable.map((title, index) => {
17 | return (
18 |
19 | {title}
20 |
21 | )
22 | })
23 | let droppableStyle = {
24 | height : '200px'
25 | }
26 | if (this.state.hovering) droppableStyle.backgroundColor = 'pink'
27 | return (
28 |
29 |
30 |
31 |
Drop here...
32 |
38 | {this.state.dropped}
39 |
40 |
41 |
42 |
But not here...
43 |
48 | {this.state.dropped}
49 |
50 |
51 |
52 | )
53 | }
54 | onDragEnter() {
55 | this.setState({ hovering : true })
56 | }
57 | onDragLeave() {
58 | this.setState({ hovering : false })
59 | }
60 | onDrop(e) {
61 | this.setState({ hovering : false, dropped : e.yolo })
62 | clearTimeout(this.dropTimeout)
63 | this.dropTimeout = setTimeout(() => {
64 | this.setState({ dropped : '' })
65 | },1500)
66 | }
67 | }
68 |
69 | const app = document.createElement('div')
70 | document.body.append(app)
71 |
72 | ReactDOM.render(, app)
73 |
--------------------------------------------------------------------------------
/lib/Draggable.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | var _react = _interopRequireDefault(require("react"));
9 |
10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11 |
12 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
13 |
14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
15 |
16 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
17 |
18 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
19 |
20 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
21 |
22 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
23 |
24 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
25 |
26 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
27 |
28 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
29 |
30 | var Draggable =
31 | /*#__PURE__*/
32 | function (_React$Component) {
33 | _inherits(Draggable, _React$Component);
34 |
35 | function Draggable() {
36 | _classCallCheck(this, Draggable);
37 |
38 | return _possibleConstructorReturn(this, _getPrototypeOf(Draggable).apply(this, arguments));
39 | }
40 |
41 | _createClass(Draggable, [{
42 | key: "render",
43 | value: function render() {
44 | var Tag = 'div';
45 | var props = Object.assign({}, this.props);
46 |
47 | if (this.props.wrapperComponent) {
48 | Tag = this.props.wrapperComponent.type;
49 | props = Object.assign(props, this.props.wrapperComponent.props);
50 | delete props.wrapperComponent;
51 | }
52 |
53 | if (this.props.enabled) {
54 | props.draggable = 'true';
55 | props.onDragEnd = this.onDragEnd.bind(this);
56 | props.onDragStart = this.onDragStart.bind(this);
57 | }
58 |
59 | delete props.enabled;
60 | return _react.default.createElement(Tag, props, props.children);
61 | }
62 | }, {
63 | key: "onDragStart",
64 | value: function onDragStart(e) {
65 | if (typeof this.props.onDragStart === 'function') this.props.onDragStart(e);
66 | var props = Object.assign({}, this.props);
67 | if (this.props.wrapperComponent) props = Object.assign(props, this.props.wrapperComponent.props);
68 | e.dataTransfer.setData(props.type, props.data);
69 | }
70 | }, {
71 | key: "onDragEnd",
72 | value: function onDragEnd(e) {
73 | if (typeof this.props.onDragEnd === 'function') this.props.onDragEnd(e);
74 | }
75 | }]);
76 |
77 | return Draggable;
78 | }(_react.default.Component);
79 |
80 | exports.default = Draggable;
81 | Draggable.defaultProps = {
82 | enabled: true
83 | };
--------------------------------------------------------------------------------
/lib/Droppable.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | var _react = _interopRequireDefault(require("react"));
9 |
10 | var _utils = _interopRequireDefault(require("./utils"));
11 |
12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13 |
14 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
15 |
16 | function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
17 |
18 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
19 |
20 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
21 |
22 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
23 |
24 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
25 |
26 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
27 |
28 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
29 |
30 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
31 |
32 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
33 |
34 | function pickTypes(e) {
35 | return e.dataTransfer ? e.dataTransfer.types : [];
36 | }
37 |
38 | function filterProps(props) {
39 | var forbidden = ['types', 'className', 'enabled', 'wrapperComponent'];
40 | return Object.keys(props).reduce(function (p, c) {
41 | if (!forbidden.includes(c)) {
42 | p[c] = props[c];
43 | }
44 |
45 | return p;
46 | }, {});
47 | }
48 |
49 | var Droppable =
50 | /*#__PURE__*/
51 | function (_React$Component) {
52 | _inherits(Droppable, _React$Component);
53 |
54 | function Droppable(props) {
55 | var _this;
56 |
57 | _classCallCheck(this, Droppable);
58 |
59 | _this = _possibleConstructorReturn(this, _getPrototypeOf(Droppable).call(this, props));
60 | _this.state = {
61 | over: false
62 | };
63 | _this.droppable = _react.default.createRef();
64 | return _this;
65 | }
66 |
67 | _createClass(Droppable, [{
68 | key: "render",
69 | value: function render() {
70 | var Tag = 'div';
71 | var props = Object.assign({}, this.props);
72 |
73 | if (this.props.wrapperComponent) {
74 | Tag = this.props.wrapperComponent.type;
75 | props = Object.assign(props, this.props.wrapperComponent.props);
76 | }
77 |
78 | var classes = 'Droppable';
79 | if (props.className) classes += " ".concat(props.className);
80 | if (this.state.over) classes += ' over';
81 | return _react.default.createElement(Tag, _extends({
82 | ref: this.droppable,
83 | className: classes
84 | }, filterProps(props), {
85 | onDrop: this.onDrop.bind(this),
86 | onDragOver: this.onDragOver.bind(this),
87 | onDragEnter: this.onDragEnter.bind(this),
88 | onDragLeave: this.onDragLeave.bind(this),
89 | onDragExit: this.onDragLeave.bind(this)
90 | }), props.children);
91 | }
92 | }, {
93 | key: "onDragOver",
94 | value: function onDragOver(e) {
95 | e.preventDefault();
96 | if (!this.allowed(pickTypes(e))) return;
97 | if (typeof this.props.onDragOver === 'function') this.props.onDragOver(e);
98 | }
99 | }, {
100 | key: "onDragEnter",
101 | value: function onDragEnter(e) {
102 | e.preventDefault();
103 | if (this.state.over) return;
104 | if (!this.allowed(pickTypes(e))) return;
105 | if (typeof this.props.onDragEnter === 'function') this.props.onDragEnter(e);
106 | this.setState({
107 | over: true
108 | });
109 | }
110 | }, {
111 | key: "onDragLeave",
112 | value: function onDragLeave(e) {
113 | e.preventDefault();
114 | if (!this.allowed(pickTypes(e))) return;
115 | var over = true;
116 | if (e.clientX <= this.position.left || e.clientX >= this.position.right) over = false;
117 | if (e.clientY <= this.position.top || e.clientY >= this.position.bottom) over = false;
118 | if (over) return;
119 | this.setState({
120 | over: false
121 | });
122 | if (typeof this.props.onDragLeave === 'function') this.props.onDragLeave(e);
123 | }
124 | }, {
125 | key: "onDrop",
126 | value: function onDrop(e) {
127 | e.preventDefault();
128 | if (!this.allowed(pickTypes(e))) return;
129 | this.setState({
130 | over: false
131 | });
132 | var props = Object.assign({}, this.props);
133 | if (this.props.wrapperComponent) props = Object.assign(props, this.props.wrapperComponent.props);
134 | var data = !props.types ? null : [].concat(props.types).reduce(function (d, type) {
135 | d[type] = e.dataTransfer.getData(type);
136 | return d;
137 | }, {});
138 | if (typeof this.props.onDrop === 'function') this.props.onDrop(data, e);
139 | }
140 | }, {
141 | key: "allowed",
142 | value: function allowed(attemptingTypes) {
143 | var props = Object.assign({}, this.props);
144 | if (this.props.wrapperComponent) props = Object.assign(props, this.props.wrapperComponent.props);
145 | if (!props.enabled) return false;
146 |
147 | var _attemptingTypes = _utils.default.toArray(attemptingTypes);
148 |
149 | if (!props.types) return true;
150 | return [].concat(props.types).reduce(function (sum, type) {
151 | if (_attemptingTypes.indexOf(type) >= 0) return true;
152 | return sum;
153 | }, false);
154 | }
155 | }, {
156 | key: "componentDidMount",
157 | value: function componentDidMount() {
158 | // TODO: Listen for window resize?
159 | var node = this.droppable.current;
160 | this.position = {
161 | top: node.offsetTop + 5,
162 | left: node.offsetLeft + 5,
163 | right: node.offsetLeft + node.offsetWidth - 5,
164 | bottom: node.offsetTop + node.offsetHeight - 5
165 | };
166 | }
167 | }]);
168 |
169 | return Droppable;
170 | }(_react.default.Component);
171 |
172 | exports.default = Droppable;
173 | Droppable.defaultProps = {
174 | enabled: true
175 | };
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | Object.defineProperty(exports, "Draggable", {
7 | enumerable: true,
8 | get: function get() {
9 | return _Draggable.default;
10 | }
11 | });
12 | Object.defineProperty(exports, "Droppable", {
13 | enumerable: true,
14 | get: function get() {
15 | return _Droppable.default;
16 | }
17 | });
18 |
19 | var _Draggable = _interopRequireDefault(require("./Draggable"));
20 |
21 | var _Droppable = _interopRequireDefault(require("./Droppable"));
22 |
23 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | function toArray(obj) {
9 | var array = []; // iterate backwards ensuring that length is an UInt32
10 |
11 | for (var i = obj.length >>> 0; i--;) {
12 | array[i] = obj[i];
13 | }
14 |
15 | return array;
16 | }
17 |
18 | var _default = {
19 | toArray: toArray
20 | };
21 | exports.default = _default;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-drag-and-drop",
3 | "version": "3.0.0",
4 | "description": "Basic Drag and Drop for React",
5 | "main": "lib/index.js",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "start": "budo demo.js --live -- -t babelify",
11 | "test": "mocha -R nyan -w --check-leaks --require @babel/register",
12 | "build": "babel src/ --out-dir lib",
13 | "prepublish": "npm run build"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/asbjornenge/react-drag-and-drop.git"
18 | },
19 | "keywords": [
20 | "react",
21 | "react-component",
22 | "drag-and-drop"
23 | ],
24 | "author": "Asbjorn Enge ",
25 | "license": "BSD-3-Clause",
26 | "bugs": {
27 | "url": "https://github.com/asbjornenge/react-drag-and-drop/issues"
28 | },
29 | "homepage": "https://github.com/asbjornenge/react-drag-and-drop",
30 | "devDependencies": {
31 | "@babel/cli": "^7.2.0",
32 | "@babel/core": "^7.2.0",
33 | "@babel/plugin-transform-react-jsx": "^7.2.0",
34 | "@babel/preset-env": "^7.2.0",
35 | "@babel/register": "^7.0.0",
36 | "babelify": "^10.0.0",
37 | "budo": "^11.5.0",
38 | "enzyme": "^3.7.0",
39 | "enzyme-adapter-react-16": "^1.7.1",
40 | "expect": "^23.6.0",
41 | "mocha": "^5.2.0",
42 | "nanodom": "0.0.3",
43 | "react": "^16.6.3",
44 | "react-dom": "^16.6.3",
45 | "sinon": "^7.2.0",
46 | "testdom": "^3.0.0"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Draggable.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default class Draggable extends React.Component {
4 | render() {
5 | let Tag = 'div'
6 | let props = Object.assign({}, this.props)
7 | if (this.props.wrapperComponent) {
8 | Tag = this.props.wrapperComponent.type
9 | props = Object.assign(props, this.props.wrapperComponent.props)
10 | delete props.wrapperComponent
11 | }
12 | if (this.props.enabled) {
13 | props.draggable = 'true'
14 | props.onDragEnd = this.onDragEnd.bind(this)
15 | props.onDragStart = this.onDragStart.bind(this)
16 | }
17 | delete props.enabled
18 | return (
19 |
20 | {props.children}
21 |
22 | )
23 | }
24 | onDragStart(e) {
25 | if (typeof this.props.onDragStart === 'function') this.props.onDragStart(e)
26 | let props = Object.assign({}, this.props)
27 | if (this.props.wrapperComponent) props = Object.assign(props, this.props.wrapperComponent.props)
28 | e.dataTransfer.setData(props.type, props.data)
29 | }
30 | onDragEnd(e) {
31 | if (typeof this.props.onDragEnd === 'function') this.props.onDragEnd(e)
32 | }
33 | }
34 |
35 | Draggable.defaultProps = {
36 | enabled: true
37 | }
38 |
--------------------------------------------------------------------------------
/src/Droppable.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import utils from './utils'
3 |
4 | function pickTypes(e) {
5 | return e.dataTransfer ? e.dataTransfer.types : []
6 | }
7 |
8 | function filterProps(props) {
9 | let forbidden = ['types', 'className', 'enabled', 'wrapperComponent']
10 | return Object.keys(props).reduce((p, c) => {
11 | if (!forbidden.includes(c)) {
12 | p[c] = props[c]
13 | }
14 | return p
15 | }, {})
16 | }
17 |
18 | export default class Droppable extends React.Component {
19 | constructor(props) {
20 | super(props)
21 | this.state = {
22 | over : false
23 | }
24 | this.droppable = React.createRef()
25 | }
26 | render() {
27 | let Tag = 'div'
28 | let props = Object.assign({}, this.props)
29 | if (this.props.wrapperComponent) {
30 | Tag = this.props.wrapperComponent.type
31 | props = Object.assign(props, this.props.wrapperComponent.props)
32 | }
33 | let classes = 'Droppable';
34 | if(props.className) classes+=` ${props.className}`;
35 | if (this.state.over) classes+=' over';
36 | return (
37 |
43 | {props.children}
44 |
45 | )
46 | }
47 | onDragOver(e) {
48 | e.preventDefault()
49 | if (!this.allowed(pickTypes(e))) return
50 | if (typeof this.props.onDragOver === 'function') this.props.onDragOver(e)
51 | }
52 | onDragEnter(e) {
53 | e.preventDefault()
54 | if (this.state.over) return
55 | if (!this.allowed(pickTypes(e))) return
56 | if (typeof this.props.onDragEnter === 'function') this.props.onDragEnter(e)
57 | this.setState({ over : true })
58 | }
59 | onDragLeave(e) {
60 | e.preventDefault()
61 | if (!this.allowed(pickTypes(e))) return
62 | let over = true
63 | if (e.clientX <= this.position.left || e.clientX >= this.position.right) over = false
64 | if (e.clientY <= this.position.top || e.clientY >= this.position.bottom) over = false
65 | if (over) return
66 | this.setState({ over : false })
67 | if (typeof this.props.onDragLeave === 'function') this.props.onDragLeave(e)
68 | }
69 | onDrop(e) {
70 | e.preventDefault()
71 | if (!this.allowed(pickTypes(e))) return
72 | this.setState({ over : false })
73 | let props = Object.assign({}, this.props)
74 | if (this.props.wrapperComponent) props = Object.assign(props, this.props.wrapperComponent.props)
75 | const data = !props.types ? null : [].concat(props.types).reduce((d, type) => {
76 | d[type] = e.dataTransfer.getData(type)
77 | return d
78 | },{})
79 | if (typeof this.props.onDrop === 'function') this.props.onDrop(data, e)
80 | }
81 | allowed(attemptingTypes) {
82 | let props = Object.assign({}, this.props)
83 | if (this.props.wrapperComponent) props = Object.assign(props, this.props.wrapperComponent.props)
84 | if (!props.enabled) return false
85 | let _attemptingTypes = utils.toArray(attemptingTypes)
86 | if (!props.types) return true
87 | return [].concat(props.types).reduce((sum, type) => {
88 | if (_attemptingTypes.indexOf(type) >= 0) return true
89 | return sum
90 | }, false)
91 | }
92 | componentDidMount() {
93 | // TODO: Listen for window resize?
94 | var node = this.droppable.current
95 | this.position = {
96 | top : node.offsetTop+5,
97 | left : node.offsetLeft+5,
98 | right : node.offsetLeft+node.offsetWidth-5,
99 | bottom : node.offsetTop+node.offsetHeight-5
100 | }
101 | }
102 | }
103 |
104 | Droppable.defaultProps = {
105 | enabled: true
106 | }
107 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Draggable from './Draggable'
2 | import Droppable from './Droppable'
3 |
4 | export {
5 | Draggable,
6 | Droppable
7 | }
8 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | function toArray(obj) {
2 | var array = [];
3 | // iterate backwards ensuring that length is an UInt32
4 | for (var i = obj.length >>> 0; i--;) {
5 | array[i] = obj[i];
6 | }
7 | return array;
8 | }
9 |
10 | export default {
11 | toArray : toArray
12 | }
13 |
--------------------------------------------------------------------------------
/test/spec.js:
--------------------------------------------------------------------------------
1 | import testdom from 'testdom'
2 | import React from 'react'
3 | import ReactDOM from 'react-dom'
4 | import sinon from 'sinon'
5 | import expect from 'expect'
6 | import assert from 'assert'
7 | import nanodom from 'nanodom'
8 | import * as enzyme from 'enzyme'
9 | import Adapter from 'enzyme-adapter-react-16'
10 | import { Draggable, Droppable } from '../src/index'
11 | enzyme.configure({ adapter: new Adapter() })
12 |
13 | testdom('')
14 |
15 | class App extends React.Component {
16 | render() {
17 | return (
18 |
19 |
20 | I am draggable
21 |
22 |
23 | I am droppable
24 |
25 |
26 | )
27 | }
28 | }
29 |
30 | describe('drag-and-drop', () => {
31 |
32 | before((done) => {
33 | ReactDOM.render(, document.querySelector('#app'), done)
34 | })
35 |
36 | it('wraps droppable in a container', () => {
37 | let drop = nanodom('div').filter((div) => {
38 | return div.innerHTML == 'I am droppable'
39 | })
40 | assert(drop[0].parentNode.className == 'Droppable')
41 | })
42 |
43 | it(`appends "props className" with Droppable class`, () => {
44 | let anyOtherClass = "anyotherclass-something";
45 | const wrapper = enzyme.mount();
46 | expect(wrapper.find(`.Droppable.${anyOtherClass}`).length).toEqual(1);
47 | })
48 |
49 | it('wraps draggable in a container and marks it as draggable', () => {
50 | let drag = nanodom('div').filter((div) => {
51 | return div.innerHTML == 'I am draggable'
52 | })
53 | assert(drag[0].parentNode.getAttribute('draggable'))
54 | })
55 |
56 | // TODO: Add more and relevant tests
57 |
58 | it('supports disabling Droppable', () => {
59 | const onDrop = sinon.spy()
60 | const disabled = enzyme.mount()
61 | disabled.find('.Droppable').simulate('drop')
62 | assert(!onDrop.calledOnce)
63 | const enabled = enzyme.mount()
64 | enabled.find('.Droppable').simulate('drop')
65 | assert(onDrop.calledOnce)
66 | })
67 |
68 | it('supports disabling Draggable', () => {
69 | const enabled = enzyme.mount()
70 | assert(enabled.find('div').props('draggable').draggable)
71 | const disabled = enzyme.mount()
72 | assert(!disabled.find('div').props('draggable').draggable)
73 | })
74 |
75 | it('supports wrapper component for Droppable', () => {
76 | const onDrop = sinon.spy()
77 | const wrapper = enzyme.mount(} onDrop={onDrop} />)
78 | wrapper.find('section.Droppable').simulate('drop')
79 | assert(onDrop.calledOnce)
80 | })
81 |
82 | it('supports wrapper component for Draggable', () => {
83 | const wrapper = enzyme.mount(} />)
84 | assert(wrapper.find('span').props('draggable').draggable)
85 | })
86 |
87 | })
88 |
--------------------------------------------------------------------------------