Skip to content

Commit 49c822b

Browse files
committed
Convert project to <Draggable> and <DraggableCore> and ES6.
<DraggableCore> is a new component with very minimal state that handles the bulk of what <Draggable> used to handle. It is intended for use in more advanced applications. Events thrown by <DraggableCore> include a position with simple deltas, and it is up to the application to translate the <DraggableCore> object based on those deltas. <Draggable> still maintains internal state based on position and should function identically to previous. Bugs: Grid snapping is broken because deltas don't accumulate.
1 parent 302a500 commit 49c822b

File tree

12 files changed

+877
-725
lines changed

12 files changed

+877
-725
lines changed

.eslintrc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
{
2+
"parser": "babel-eslint",
23
"rules": {
34
"strict": 0,
45
"quotes": ["single"],
56
"curly": "multi-line",
67
"camelcase": 0,
8+
"comma-dangle": 1,
79
"no-use-before-define": "nofunc",
810
"no-underscore-dangle": 0,
9-
"no-unused-vars": 0,
10-
"new-cap": 0
11+
"no-unused-vars": 1,
12+
"new-cap": 0,
13+
"semi": 1
1114
},
1215
env: {
1316
"browser": true,

example/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@
4848
</style>
4949
</head>
5050
<body>
51-
<script src="//fb.me/react-with-addons-0.13.2.js"></script>
52-
<script src="//fb.me/JSXTransformer-0.13.2.js"></script>
51+
<script src="//cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react-with-addons.min.js"></script>
52+
<script src="//cdnjs.cloudflare.com/ajax/libs/react/0.13.3/JSXTransformer.js"></script>
5353
<script src="../dist/react-draggable.js"></script>
5454
<script type="text/jsx">
5555
var Draggable = ReactDraggable;

index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
module.exports = require('./lib/draggable');
1+
module.exports = require('./lib/Draggable');
2+
module.exports.DraggableCore = require('./lib/DraggableCore');

lib/Draggable.es6

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
'use strict';
2+
3+
import React from 'react';
4+
import classNames from 'classnames';
5+
import assign from 'object-assign';
6+
import {autobind} from './utils/shims';
7+
import {createUIEvent, createCSSTransform} from './utils/domFns';
8+
import {canDragX, canDragY, getBoundPosition, snapToGrid} from './utils/positionFns';
9+
import DraggableCore from './DraggableCore';
10+
11+
//
12+
// Define <Draggable>
13+
//
14+
15+
export default class Draggable extends DraggableCore {
16+
17+
constructor(props) {
18+
super(props);
19+
this.state = {
20+
// Whether or not we are currently dragging.
21+
dragging: false,
22+
23+
// Current transform x and y.
24+
clientX: props.start.x, clientY: props.start.y
25+
};
26+
autobind(this);
27+
}
28+
29+
onDragStart(e, coreEvent) {
30+
console.log('Draggable: onDragStart: %j', coreEvent.position);
31+
32+
// Short-circuit if user's callback killed it.
33+
let shouldStart = this.props.onStart(e, coreEvent);
34+
// Kills start event on core as well, so move handlers are never bound.
35+
if (shouldStart === false) return false;
36+
37+
this.setState({
38+
dragging: true
39+
});
40+
}
41+
42+
onDrag(e, coreEvent) {
43+
if (!this.state.dragging) return false;
44+
console.log('Draggable: onDrag: %j', coreEvent.position);
45+
46+
// Short-circuit if user's callback killed it.
47+
let shouldUpdate = this.props.onDrag(e, coreEvent);
48+
if (shouldUpdate === false) return false;
49+
50+
var clientX = this.state.clientX + coreEvent.position.deltaX;
51+
var clientY = this.state.clientY + coreEvent.position.deltaY;
52+
53+
// Snap to grid if prop has been provided
54+
if (Array.isArray(this.props.grid)) {
55+
[clientX, clientY] = snapToGrid(this.props.grid, clientX, clientY);
56+
}
57+
58+
// Keep within bounds.
59+
if (this.props.bounds) {
60+
[clientX, clientY] = getBoundPosition(this, clientX, clientY);
61+
}
62+
63+
// TODO create drag event using createUIEvent and call back with it
64+
// this.props.onDrag(createUIEvent(e));
65+
66+
this.setState({clientX, clientY});
67+
}
68+
69+
onDragEnd(e, coreEvent) {
70+
if (!this.state.dragging) return false;
71+
72+
// Short-circuit if user's callback killed it.
73+
let shouldStop = this.props.onStop(e, coreEvent);
74+
if (shouldStop === false) return false;
75+
76+
console.log('Draggable: onDragEnd: %j', coreEvent.position);
77+
78+
this.setState({
79+
dragging: false
80+
});
81+
}
82+
83+
render() {
84+
85+
// Add a CSS transform to move the element around. This allows us to move the element around
86+
// without worrying about whether or not it is relatively or absolutely positioned.
87+
// If the item you are dragging already has a transform set, wrap it in a <span> so <Draggable>
88+
// has a clean slate.
89+
var style = createCSSTransform({
90+
// Set left if horizontal drag is enabled
91+
x: canDragX(this) ?
92+
this.state.clientX :
93+
0,
94+
95+
// Set top if vertical drag is enabled
96+
y: canDragY(this) ?
97+
this.state.clientY :
98+
0
99+
});
100+
101+
// zIndex option
102+
if (this.state.dragging && !isNaN(this.props.zIndex)) {
103+
style.zIndex = this.props.zIndex;
104+
}
105+
106+
// Mark with class while dragging
107+
var className = classNames((this.props.children.props.className || ''), 'react-draggable', {
108+
'react-draggable-dragging': this.state.dragging,
109+
'react-draggable-dragged': this.state.dragged
110+
});
111+
112+
// Reuse the child provided
113+
// This makes it flexible to use whatever element is wanted (div, ul, etc)
114+
return (
115+
<DraggableCore {...this.props} style={style} className={className}
116+
onStart={this.onDragStart} onDrag={this.onDrag} onStop={this.onDragEnd}>
117+
{React.Children.only(this.props.children)}
118+
</DraggableCore>
119+
);
120+
}
121+
}
122+
123+
Draggable.propTypes = assign({}, DraggableCore.propTypes, {
124+
/**
125+
* `axis` determines which axis the draggable can move.
126+
*
127+
* 'both' allows movement horizontally and vertically.
128+
* 'x' limits movement to horizontal axis.
129+
* 'y' limits movement to vertical axis.
130+
*
131+
* Defaults to 'both'.
132+
*/
133+
axis: React.PropTypes.oneOf(['both', 'x', 'y']),
134+
135+
/**
136+
* `bounds` determines the range of movement available to the element.
137+
* Available values are:
138+
*
139+
* 'parent' restricts movement within the Draggable's parent node.
140+
*
141+
* Alternatively, pass an object with the following properties, all of which are optional:
142+
*
143+
* {left: LEFT_BOUND, right: RIGHT_BOUND, bottom: BOTTOM_BOUND, top: TOP_BOUND}
144+
*
145+
* All values are in px.
146+
*
147+
* Example:
148+
*
149+
* ```jsx
150+
* var App = React.createClass({
151+
* render: function () {
152+
* return (
153+
* <Draggable bounds={{right: 300, bottom: 300}}>
154+
* <div>Content</div>
155+
* </Draggable>
156+
* );
157+
* }
158+
* });
159+
* ```
160+
*/
161+
bounds: React.PropTypes.oneOfType([
162+
React.PropTypes.shape({
163+
left: React.PropTypes.Number,
164+
right: React.PropTypes.Number,
165+
top: React.PropTypes.Number,
166+
bottom: React.PropTypes.Number
167+
}),
168+
React.PropTypes.oneOf(['parent', false])
169+
]),
170+
171+
/**
172+
* `grid` specifies the x and y that dragging should snap to.
173+
*
174+
* Example:
175+
*
176+
* ```jsx
177+
* var App = React.createClass({
178+
* render: function () {
179+
* return (
180+
* <Draggable grid={[25, 25]}>
181+
* <div>I snap to a 25 x 25 grid</div>
182+
* </Draggable>
183+
* );
184+
* }
185+
* });
186+
* ```
187+
*/
188+
grid: React.PropTypes.arrayOf(React.PropTypes.number),
189+
190+
/**
191+
* `start` specifies the x and y that the dragged item should start at
192+
*
193+
* Example:
194+
*
195+
* ```jsx
196+
* var App = React.createClass({
197+
* render: function () {
198+
* return (
199+
* <Draggable start={{x: 25, y: 25}}>
200+
* <div>I start with transformX: 25px and transformY: 25px;</div>
201+
* </Draggable>
202+
* );
203+
* }
204+
* });
205+
* ```
206+
*/
207+
start: React.PropTypes.shape({
208+
x: React.PropTypes.number,
209+
y: React.PropTypes.number
210+
}),
211+
212+
/**
213+
* `zIndex` specifies the zIndex to use while dragging.
214+
*
215+
* Example:
216+
*
217+
* ```jsx
218+
* var App = React.createClass({
219+
* render: function () {
220+
* return (
221+
* <Draggable zIndex={100}>
222+
* <div>I have a zIndex</div>
223+
* </Draggable>
224+
* );
225+
* }
226+
* });
227+
* ```
228+
*/
229+
zIndex: React.PropTypes.number
230+
});
231+
232+
Draggable.defaultProps = assign({}, DraggableCore.defaultProps, {
233+
axis: 'both',
234+
bounds: false,
235+
grid: null,
236+
start: {x: 0, y: 0},
237+
zIndex: NaN
238+
});
239+
240+
//
241+
// Helpers.
242+
//
243+
244+
245+
//
246+
// End Helpers.
247+
//

0 commit comments

Comments
 (0)