Skip to content

Commit cfa99ba

Browse files
author
Matt Zabriskie
committed
Adding support for snapping to a grid
1 parent 9bb7d33 commit cfa99ba

File tree

3 files changed

+91
-56
lines changed

3 files changed

+91
-56
lines changed

example/main.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @jsx React.DOM */
2-
var React = require('react'),
3-
Draggable = require('../lib/main');
2+
var React = require('react');
3+
var Draggable = require('../lib/main');
44

55
var App = React.createClass({
66
getInitialState: function () {
@@ -11,18 +11,12 @@ var App = React.createClass({
1111
};
1212
},
1313

14-
handleStart: function (e, ui) {
15-
},
16-
1714
handleDrag: function (e, ui) {
1815
this.setState({
1916
position: ui.position
2017
});
2118
},
2219

23-
handleStop: function (e, ui) {
24-
},
25-
2620
render: function () {
2721
return (
2822
<div>
@@ -57,6 +51,12 @@ var App = React.createClass({
5751
<div>Dragging here works</div>
5852
</div>
5953
</Draggable>
54+
<Draggable grid={[25, 25]}>
55+
<div className="box">I snap to a 25 x 25 grid</div>
56+
</Draggable>
57+
<Draggable grid={[50, 50]}>
58+
<div className="box">I snap to a 50 x 50 grid</div>
59+
</Draggable>
6060
</div>
6161
);
6262
}

lib/draggable.js

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use strict';
22

33
/** @jsx React.DOM */
4-
var React = require('react/addons'),
5-
invariant = require('react/lib/invariant'),
6-
emptyFunction = require('react/lib/emptyFunction');
4+
var React = require('react/addons');
5+
var invariant = require('react/lib/invariant');
6+
var emptyFunction = require('react/lib/emptyFunction');
77

88
function createUIEvent(draggable) {
99
return {
@@ -125,6 +125,25 @@ module.exports = React.createClass({
125125
*/
126126
cancel: React.PropTypes.string,
127127

128+
/**
129+
* `grid` specifies the x and y that dragging should snap to.
130+
*
131+
* Example:
132+
*
133+
* ```jsx
134+
* var App = React.createClass({
135+
* render: function () {
136+
* return (
137+
* <Draggable grid={[25, 25]}>
138+
* <div>I snap to a 25 x 25 grid</div>
139+
* </Draggable>
140+
* );
141+
* }
142+
* });
143+
* ```
144+
*/
145+
grid: React.PropTypes.arrayOf(React.PropTypes.number),
146+
128147
/**
129148
* `zIndex` specifies the zIndex to use while dragging.
130149
*
@@ -210,6 +229,7 @@ module.exports = React.createClass({
210229
axis: 'both',
211230
handle: null,
212231
cancel: null,
232+
grid: null,
213233
zIndex: NaN,
214234
onStart: emptyFunction,
215235
onDrag: emptyFunction,
@@ -271,9 +291,22 @@ module.exports = React.createClass({
271291
},
272292

273293
handleMouseMove: function (e) {
294+
var clientX = (this.state.startX + (e.clientX - this.state.offsetX));
295+
var clientY = (this.state.startY + (e.clientY - this.state.offsetY));
296+
297+
if (Array.isArray(this.props.grid)) {
298+
clientX = Math.abs(clientX - this.state.clientX) >= this.props.grid[0]
299+
? clientX
300+
: this.state.clientX;
301+
302+
clientY = Math.abs(clientY - this.state.clientY) >= this.props.grid[1]
303+
? clientY
304+
: this.state.clientY;
305+
}
306+
274307
this.setState({
275-
clientX: (this.state.startX + (e.clientX - this.state.offsetX)),
276-
clientY: (this.state.startY + (e.clientY - this.state.offsetY))
308+
clientX: clientX,
309+
clientY: clientY
277310
});
278311

279312
this.props.onDrag(e, createUIEvent(this));

specs/draggable.spec.js

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/** @jsx React.DOM */
2-
var React = require('react/addons'),
3-
TestUtils = React.addons.TestUtils,
4-
Draggable = require('../lib/main');
2+
var React = require('react/addons');
3+
var TestUtils = React.addons.TestUtils;
4+
var Draggable = require('../lib/main');
55

66
describe('react-draggable', function () {
77
describe('props', function () {
@@ -23,49 +23,51 @@ describe('react-draggable', function () {
2323
function handleStop() {}
2424

2525
var drag = TestUtils.renderIntoDocument(
26-
<Draggable
27-
axis="y"
28-
handle=".handle"
29-
cancel=".cancel"
30-
zIndex={1000}
31-
onStart={handleStart}
32-
onDrag={handleDrag}
33-
onStop={handleStop}>
34-
<div>
35-
<div className="handle"/>
36-
<div className="cancel"/>
37-
</div>
38-
</Draggable>
39-
);
26+
<Draggable
27+
axis="y"
28+
handle=".handle"
29+
cancel=".cancel"
30+
grid={[10, 10]}
31+
zIndex={1000}
32+
onStart={handleStart}
33+
onDrag={handleDrag}
34+
onStop={handleStop}>
35+
<div>
36+
<div className="handle"/>
37+
<div className="cancel"/>
38+
</div>
39+
</Draggable>
40+
);
4041

4142
expect(drag.props.axis).toEqual('y');
4243
expect(drag.props.handle).toEqual('.handle');
4344
expect(drag.props.cancel).toEqual('.cancel');
45+
expect(drag.props.grid).toEqual([10, 10]);
4446
expect(drag.props.zIndex).toEqual(1000);
4547
expect(drag.props.onStart).toEqual(handleStart);
4648
expect(drag.props.onDrag).toEqual(handleDrag);
4749
expect(drag.props.onStop).toEqual(handleStop);
4850
});
4951

5052
it('should call onStart when dragging begins', function () {
51-
var called = false,
52-
drag = TestUtils.renderIntoDocument(
53-
<Draggable onStart={function () { called = true; }}>
54-
<div/>
55-
</Draggable>
56-
);
53+
var called = false;
54+
var drag = TestUtils.renderIntoDocument(
55+
<Draggable onStart={function () { called = true; }}>
56+
<div/>
57+
</Draggable>
58+
);
5759

5860
TestUtils.Simulate.mouseDown(drag.getDOMNode());
5961
expect(called).toEqual(true);
6062
});
6163

6264
it('should call onStop when dragging ends', function () {
63-
var called = false,
64-
drag = TestUtils.renderIntoDocument(
65-
<Draggable onStop={function () { called = true; }}>
66-
<div/>
67-
</Draggable>
68-
);
65+
var called = false;
66+
var drag = TestUtils.renderIntoDocument(
67+
<Draggable onStop={function () { called = true; }}>
68+
<div/>
69+
</Draggable>
70+
);
6971

7072
TestUtils.Simulate.mouseDown(drag.getDOMNode());
7173
TestUtils.Simulate.mouseUp(drag.getDOMNode());
@@ -83,13 +85,13 @@ describe('react-draggable', function () {
8385

8486
it('should only initialize dragging onmousedown of handle', function () {
8587
var drag = TestUtils.renderIntoDocument(
86-
<Draggable handle=".handle">
87-
<div>
88-
<div className="handle">Handle</div>
89-
<div className="content">Lorem ipsum...</div>
90-
</div>
91-
</Draggable>
92-
);
88+
<Draggable handle=".handle">
89+
<div>
90+
<div className="handle">Handle</div>
91+
<div className="content">Lorem ipsum...</div>
92+
</div>
93+
</Draggable>
94+
);
9395

9496
TestUtils.Simulate.mouseDown(drag.getDOMNode().querySelector('.content'));
9597
expect(drag.state.dragging).toEqual(false);
@@ -100,13 +102,13 @@ describe('react-draggable', function () {
100102

101103
it('should not initialize dragging onmousedown of cancel', function () {
102104
var drag = TestUtils.renderIntoDocument(
103-
<Draggable cancel=".cancel">
104-
<div>
105-
<div className="cancel">Cancel</div>
106-
<div className="content">Lorem ipsum...</div>
107-
</div>
108-
</Draggable>
109-
);
105+
<Draggable cancel=".cancel">
106+
<div>
107+
<div className="cancel">Cancel</div>
108+
<div className="content">Lorem ipsum...</div>
109+
</div>
110+
</Draggable>
111+
);
110112

111113
TestUtils.Simulate.mouseDown(drag.getDOMNode().querySelector('.cancel'));
112114
expect(drag.state.dragging).toEqual(false);

0 commit comments

Comments
 (0)