Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-draggable

Package Overview
Dependencies
Maintainers
2
Versions
79
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-draggable - npm Package Compare versions

Comparing version 0.4.2 to 0.5.0

bower.json

4

CHANGELOG.md

@@ -45,1 +45,5 @@ # Changelog

- Remove unnecessary `emptyFunction` and `React.addons.classSet` imports.
### 0.4.3 (Apr 30, 2015)
- Fix React.addons error caused by faulty test.

690

lib/draggable.js
'use strict';
/** @jsx React.DOM */
var React = require('react');
var emptyFunction = function(){};
var assign = require('object-assign');
var classNames = require('classnames');
//
// Helpers. See Element definition below this section.
//
function createUIEvent(draggable) {
return {
position: {
top: draggable.state.clientY,
left: draggable.state.clientX
}
};
// State changes are often (but not always!) async. We want the latest value.
var state = draggable._pendingState || draggable.state;
return {
node: draggable.getDOMNode(),
position: {
top: state.clientY,
left: state.clientX
}
};
}
function canDragY(draggable) {
return draggable.props.axis === 'both' ||
draggable.props.axis === 'y';
return draggable.props.axis === 'both' ||
draggable.props.axis === 'y';
}
function canDragX(draggable) {
return draggable.props.axis === 'both' ||
draggable.props.axis === 'x';
return draggable.props.axis === 'both' ||
draggable.props.axis === 'x';
}
function isFunction(func) {
return typeof func === 'function' || Object.prototype.toString.call(func) === '[object Function]'
return typeof func === 'function' || Object.prototype.toString.call(func) === '[object Function]';
}

@@ -32,4 +40,4 @@

function findInArray(array, callback) {
for (var i = 0, length = array.length, element = null; i < length, element = array[i]; i++) {
if (callback.apply(callback, [element, i, array])) return element;
for (var i = 0, length = array.length; i < length; i++) {
if (callback.apply(callback, [array[i], i, array])) return array[i];
}

@@ -59,4 +67,4 @@ }

// Do Browser Stuff
var isTouchDevice = 'ontouchstart' in window // works on most browsers
|| 'onmsgesturechange' in window; // works on ie10 on ms surface
var isTouchDevice = 'ontouchstart' in window || // works on most browsers
'onmsgesturechange' in window; // works on ie10 on ms surface

@@ -97,251 +105,274 @@ }

clientY: position.clientY
}
};
}
function addEvent(el, event, handler) {
if (!el) { return; }
if (el.attachEvent) {
el.attachEvent('on' + event, handler);
} else if (el.addEventListener) {
el.addEventListener(event, handler, true);
} else {
el['on' + event] = handler;
}
if (!el) { return; }
if (el.attachEvent) {
el.attachEvent('on' + event, handler);
} else if (el.addEventListener) {
el.addEventListener(event, handler, true);
} else {
el['on' + event] = handler;
}
}
function removeEvent(el, event, handler) {
if (!el) { return; }
if (el.detachEvent) {
el.detachEvent('on' + event, handler);
} else if (el.removeEventListener) {
el.removeEventListener(event, handler, true);
} else {
el['on' + event] = null;
}
if (!el) { return; }
if (el.detachEvent) {
el.detachEvent('on' + event, handler);
} else if (el.removeEventListener) {
el.removeEventListener(event, handler, true);
} else {
el['on' + event] = null;
}
}
module.exports = React.createClass({
displayName: 'Draggable',
function snapToGrid(draggable, clientX, clientY) {
var stateX = parseInt(draggable.state.clientX, 10);
var stateY = parseInt(draggable.state.clientY, 10);
var directionX = clientX < stateX ? -1 : 1;
var directionY = clientY < stateY ? -1 : 1;
propTypes: {
/**
* `axis` determines which axis the draggable can move.
*
* 'both' allows movement horizontally and vertically.
* 'x' limits movement to horizontal axis.
* 'y' limits movement to vertical axis.
*
* Defaults to 'both'.
*/
axis: React.PropTypes.oneOf(['both', 'x', 'y']),
clientX = Math.abs(clientX - stateX) >= draggable.props.grid[0] ?
(stateX + (draggable.props.grid[0] * directionX)) :
stateX;
/**
* `handle` specifies a selector to be used as the handle that initiates drag.
*
* Example:
*
* ```jsx
* var App = React.createClass({
* render: function () {
* return (
* <Draggable handle=".handle">
* <div>
* <div className="handle">Click me to drag</div>
* <div>This is some other content</div>
* </div>
* </Draggable>
* );
* }
* });
* ```
*/
handle: React.PropTypes.string,
clientY = Math.abs(clientY - stateY) >= draggable.props.grid[1] ?
(stateY + (draggable.props.grid[1] * directionY)) :
stateY;
/**
* `cancel` specifies a selector to be used to prevent drag initialization.
*
* Example:
*
* ```jsx
* var App = React.createClass({
* render: function () {
* return(
* <Draggable cancel=".cancel">
* <div>
* <div className="cancel">You can't drag from here</div>
* <div>Dragging here works fine</div>
* </div>
* </Draggable>
* );
* }
* });
* ```
*/
cancel: React.PropTypes.string,
return [clientX, clientY];
}
/**
* `grid` specifies the x and y that dragging should snap to.
*
* Example:
*
* ```jsx
* var App = React.createClass({
* render: function () {
* return (
* <Draggable grid={[25, 25]}>
* <div>I snap to a 25 x 25 grid</div>
* </Draggable>
* );
* }
* });
* ```
*/
grid: React.PropTypes.arrayOf(React.PropTypes.number),
// Useful for preventing blue highlights all over everything when dragging.
var userSelectStyle = {
WebkitUserSelect: 'none',
MozUserSelect: 'none',
msUserSelect: 'none',
OUserSelect: 'none',
userSelect: 'none',
};
/**
* `start` specifies the x and y that the dragged item should start at
*
* Example:
*
* ```jsx
* var App = React.createClass({
* render: function () {
* return (
* <Draggable start={{x: 25, y: 25}}>
* <div>I start with left: 25px; top: 25px;</div>
* </Draggable>
* );
* }
* });
* ```
*/
start: React.PropTypes.object,
function createCSSTransform(style) {
if (!style.x && !style.y) return {};
// Replace unitless items with px
var x = style.x + 'px';
var y = style.y + 'px';
return {
transform: 'translate(' + x + ',' + y + ')',
WebkitTransform: 'translate(' + x + ',' + y + ')',
OTransform: 'translate(' + x + ',' + y + ')',
msTransform: 'translate(' + x + ',' + y + ')',
MozTransform: 'translate(' + x + ',' + y + ')'
};
}
/**
* `zIndex` specifies the zIndex to use while dragging.
*
* Example:
*
* ```jsx
* var App = React.createClass({
* render: function () {
* return (
* <Draggable zIndex={100}>
* <div>I have a zIndex</div>
* </Draggable>
* );
* }
* });
* ```
*/
zIndex: React.PropTypes.number,
/**
* Called when dragging starts.
*
* Example:
*
* ```js
* function (event, ui) {}
* ```
*
* `event` is the Event that was triggered.
* `ui` is an object:
*
* ```js
* {
* position: {top: 0, left: 0}
* }
* ```
*/
onStart: React.PropTypes.func,
//
// End Helpers.
//
/**
* Called while dragging.
*
* Example:
*
* ```js
* function (event, ui) {}
* ```
*
* `event` is the Event that was triggered.
* `ui` is an object:
*
* ```js
* {
* position: {top: 0, left: 0}
* }
* ```
*/
onDrag: React.PropTypes.func,
//
// Define <Draggable>
//
/**
* Called when dragging stops.
*
* Example:
*
* ```js
* function (event, ui) {}
* ```
*
* `event` is the Event that was triggered.
* `ui` is an object:
*
* ```js
* {
* position: {top: 0, left: 0}
* }
* ```
*/
onStop: React.PropTypes.func,
module.exports = React.createClass({
displayName: 'Draggable',
/**
* A workaround option which can be passed if onMouseDown needs to be accessed, since it'll always be blocked (due to that there's internal use of onMouseDown)
*
*/
onMouseDown: React.PropTypes.func
},
propTypes: {
/**
* `axis` determines which axis the draggable can move.
*
* 'both' allows movement horizontally and vertically.
* 'x' limits movement to horizontal axis.
* 'y' limits movement to vertical axis.
*
* Defaults to 'both'.
*/
axis: React.PropTypes.oneOf(['both', 'x', 'y']),
componentWillUnmount: function() {
// Remove any leftover event handlers
removeEvent(window, dragEventFor['move'], this.handleDrag);
removeEvent(window, dragEventFor['end'], this.handleDragEnd);
},
/**
* `handle` specifies a selector to be used as the handle that initiates drag.
*
* Example:
*
* ```jsx
* var App = React.createClass({
* render: function () {
* return (
* <Draggable handle=".handle">
* <div>
* <div className="handle">Click me to drag</div>
* <div>This is some other content</div>
* </div>
* </Draggable>
* );
* }
* });
* ```
*/
handle: React.PropTypes.string,
getDefaultProps: function () {
return {
axis: 'both',
handle: null,
cancel: null,
grid: null,
start: {
x: 0,
y: 0
},
zIndex: NaN,
onStart: emptyFunction,
onDrag: emptyFunction,
onStop: emptyFunction,
onMouseDown: emptyFunction
};
},
/**
* `cancel` specifies a selector to be used to prevent drag initialization.
*
* Example:
*
* ```jsx
* var App = React.createClass({
* render: function () {
* return(
* <Draggable cancel=".cancel">
* <div>
* <div className="cancel">You can't drag from here</div>
* <div>Dragging here works fine</div>
* </div>
* </Draggable>
* );
* }
* });
* ```
*/
cancel: React.PropTypes.string,
getInitialState: function () {
return {
// Whether or not currently dragging
dragging: false,
/**
* `grid` specifies the x and y that dragging should snap to.
*
* Example:
*
* ```jsx
* var App = React.createClass({
* render: function () {
* return (
* <Draggable grid={[25, 25]}>
* <div>I snap to a 25 x 25 grid</div>
* </Draggable>
* );
* }
* });
* ```
*/
grid: React.PropTypes.arrayOf(React.PropTypes.number),
// Start top/left of this.getDOMNode()
startX: 0, startY: 0,
/**
* `zIndex` specifies the zIndex to use while dragging.
*
* Example:
*
* ```jsx
* var App = React.createClass({
* render: function () {
* return (
* <Draggable zIndex={100}>
* <div>I have a zIndex</div>
* </Draggable>
* );
* }
* });
* ```
*/
zIndex: React.PropTypes.number,
// Offset between start top/left and mouse top/left
offsetX: 0, offsetY: 0,
/**
* Called when dragging starts.
*
* Example:
*
* ```js
* function (event, ui) {}
* ```
*
* `event` is the Event that was triggered.
* `ui` is an object:
*
* ```js
* {
* position: {top: 0, left: 0}
* }
* ```
*/
onStart: React.PropTypes.func,
// Current top/left of this.getDOMNode()
clientX: this.props.start.x, clientY: this.props.start.y
};
},
/**
* Called while dragging.
*
* Example:
*
* ```js
* function (event, ui) {}
* ```
*
* `event` is the Event that was triggered.
* `ui` is an object:
*
* ```js
* {
* position: {top: 0, left: 0}
* }
* ```
*/
onDrag: React.PropTypes.func,
handleDragStart: function (e) {
/**
* Called when dragging stops.
*
* Example:
*
* ```js
* function (event, ui) {}
* ```
*
* `event` is the Event that was triggered.
* `ui` is an object:
*
* ```js
* {
* position: {top: 0, left: 0}
* }
* ```
*/
onStop: React.PropTypes.func,
/**
* A workaround option which can be passed if onMouseDown needs to be accessed,
* since it'll always be blocked (due to that there's internal use of onMouseDown)
*/
onMouseDown: React.PropTypes.func,
},
componentWillUnmount: function() {
// Remove any leftover event handlers
removeEvent(window, dragEventFor['move'], this.handleDrag);
removeEvent(window, dragEventFor['end'], this.handleDragEnd);
},
getDefaultProps: function () {
return {
axis: 'both',
handle: null,
cancel: null,
grid: null,
zIndex: NaN,
onStart: emptyFunction,
onDrag: emptyFunction,
onStop: emptyFunction,
onMouseDown: emptyFunction
};
},
getInitialState: function () {
return {
// Whether or not we are currently dragging.
dragging: false,
// Offset between start top/left and mouse top/left while dragging.
offsetX: 0, offsetY: 0,
// Current transform x and y.
clientX: 0, clientY: 0
};
},
handleDragStart: function (e) {
// todo: write right implementation to prevent multitouch drag

@@ -354,113 +385,112 @@ // prevent multi-touch events

// Make it possible to attach event handlers on top of this one
this.props.onMouseDown(e);
// Make it possible to attach event handlers on top of this one
this.props.onMouseDown(e);
var node = this.getDOMNode();
// Short circuit if handle or cancel prop was provided and selector doesn't match
if ((this.props.handle && !matchesSelector(e.target, this.props.handle)) ||
(this.props.cancel && matchesSelector(e.target, this.props.cancel))) {
return;
}
// Short circuit if handle or cancel prop was provided and selector doesn't match
if ((this.props.handle && !matchesSelector(e.target, this.props.handle)) ||
(this.props.cancel && matchesSelector(e.target, this.props.cancel))) {
return;
}
var dragPoint = getControlPosition(e);
// Initiate dragging
this.setState({
dragging: true,
offsetX: parseInt(dragPoint.clientX, 10),
offsetY: parseInt(dragPoint.clientY, 10),
startX: parseInt(node.style.left, 10) || 0,
startY: parseInt(node.style.top, 10) || 0
});
// Initiate dragging. Set the current x and y as offsets
// so we know how much we've moved during the drag. This allows us
// to drag elements around even if they have been moved, without issue.
this.setState({
dragging: true,
offsetX: dragPoint.clientX - this.state.clientX,
offsetY: dragPoint.clientY - this.state.clientY
});
// Call event handler
this.props.onStart(e, createUIEvent(this));
// Call event handler
this.props.onStart(e, createUIEvent(this));
// Add event handlers
addEvent(window, dragEventFor['move'], this.handleDrag);
addEvent(window, dragEventFor['end'], this.handleDragEnd);
},
// Add event handlers
addEvent(window, dragEventFor['move'], this.handleDrag);
addEvent(window, dragEventFor['end'], this.handleDragEnd);
},
handleDragEnd: function (e) {
// Short circuit if not currently dragging
if (!this.state.dragging) {
return;
}
handleDragEnd: function (e) {
// Short circuit if not currently dragging
if (!this.state.dragging) {
return;
}
// Turn off dragging
this.setState({
dragging: false
});
// Turn off dragging
this.setState({
dragging: false
});
// Call event handler
this.props.onStop(e, createUIEvent(this));
// Call event handler
this.props.onStop(e, createUIEvent(this));
// Remove event handlers
// Remove event handlers
removeEvent(window, dragEventFor['move'], this.handleDrag);
removeEvent(window, dragEventFor['end'], this.handleDragEnd);
},
},
handleDrag: function (e) {
handleDrag: function (e) {
var dragPoint = getControlPosition(e);
// Calculate top and left
var clientX = (this.state.startX + (dragPoint.clientX - this.state.offsetX));
var clientY = (this.state.startY + (dragPoint.clientY - this.state.offsetY));
// Calculate X and Y
var clientX = dragPoint.clientX - this.state.offsetX;
var clientY = dragPoint.clientY - this.state.offsetY;
// Snap to grid if prop has been provided
if (Array.isArray(this.props.grid)) {
var directionX = clientX < parseInt(this.state.clientX, 10) ? -1 : 1;
var directionY = clientY < parseInt(this.state.clientY, 10) ? -1 : 1;
// Snap to grid if prop has been provided
if (Array.isArray(this.props.grid)) {
var coords = snapToGrid(this, clientX, clientY);
clientX = coords[0], clientY = coords[1];
}
clientX = Math.abs(clientX - parseInt(this.state.clientX, 10)) >= this.props.grid[0]
? (parseInt(this.state.clientX, 10) + (this.props.grid[0] * directionX))
: this.state.clientX;
// Update transform
this.setState({
clientX: clientX,
clientY: clientY
});
clientY = Math.abs(clientY - parseInt(this.state.clientY, 10)) >= this.props.grid[1]
? (parseInt(this.state.clientY, 10) + (this.props.grid[1] * directionY))
: this.state.clientY;
}
// Call event handler
this.props.onDrag(e, createUIEvent(this));
},
// Update top and left
this.setState({
clientX: clientX,
clientY: clientY
});
render: function () {
// Create style object. We extend from existing styles so we don't
// remove anything already set (like background, color, etc).
var childStyle = this.props.children.props.style || {};
// Call event handler
this.props.onDrag(e, createUIEvent(this));
},
// Add a CSS transform to move the element around. This allows us to move the element around
// without worrying about whether or not it is relatively or absolutely positioned.
// If the item you are dragging already has a transform set, wrap it in a <span> so <Draggable>
// has a clean slate.
var transform = createCSSTransform({
// Set left if horizontal drag is enabled
x: canDragX(this) ?
this.state.clientX :
0,
render: function () {
var style = {
// Set top if vertical drag is enabled
top: canDragY(this)
? this.state.clientY
: this.state.startY,
// Set top if vertical drag is enabled
y: canDragY(this) ?
this.state.clientY :
0
});
var style = assign({}, userSelectStyle, childStyle, transform);
// Set left if horizontal drag is enabled
left: canDragX(this)
? this.state.clientX
: this.state.startX
};
// Set zIndex if currently dragging and prop has been provided
if (this.state.dragging && !isNaN(this.props.zIndex)) {
style.zIndex = this.props.zIndex;
}
// Set zIndex if currently dragging and prop has been provided
if (this.state.dragging && !isNaN(this.props.zIndex)) {
style.zIndex = this.props.zIndex;
}
var className = classNames((this.props.children.props.className || ''), 'react-draggable', {
'react-draggable-dragging': this.state.dragging,
'react-draggable-dragged': this.state.dragged
});
var className = 'react-draggable';
if (this.state.dragging) {
className += ' react-draggable-dragging';
}
// Reuse the child provided
// This makes it flexible to use whatever element is wanted (div, ul, etc)
return React.cloneElement(React.Children.only(this.props.children), {
style: style,
className: className,
// Reuse the child provided
// This makes it flexible to use whatever element is wanted (div, ul, etc)
return React.addons.cloneWithProps(React.Children.only(this.props.children), {
style: style,
className: className,
onMouseDown: this.handleDragStart,
onTouchStart: function(ev){
onMouseDown: this.handleDragStart,
onTouchStart: function(ev){
ev.preventDefault(); // prevent for scroll

@@ -470,6 +500,6 @@ return this.handleDragStart.apply(this, arguments);

onMouseUp: this.handleDragEnd,
onTouchEnd: this.handleDragEnd
});
}
onMouseUp: this.handleDragEnd,
onTouchEnd: this.handleDragEnd
});
}
});
{
"name": "react-draggable",
"version": "0.4.2",
"version": "0.5.0",
"description": "React draggable component",
"main": "index.js",
"scripts": {
"test": "script/test --browsers Firefox --single-run",
"start": "script/build"
"test": "make test",
"dev": "make dev",
"build": "make build"
},
"browser": "dist/react-draggable.js",
"repository": {

@@ -27,3 +27,3 @@ "type": "git",

"devDependencies": {
"jsx-loader": "^0.12.0",
"jsx-loader": "^0.13.2",
"karma": "^0.12.19",

@@ -35,8 +35,14 @@ "karma-chrome-launcher": "^0.1.4",

"karma-webpack": "^1.2.1",
"react": "^0.12.0",
"reactify": "^0.17.1",
"open": "0.0.5",
"react": "^0.13.2",
"semver": "^4.3.3",
"static-server": "^2.0.0",
"uglify-js": "^2.4.15",
"webpack": "^1.3.2-beta8",
"webpack-dev-server": "^1.4.7"
},
"dependencies": {
"classnames": "^1.2.2",
"object-assign": "^2.0.0"
}
}
}

@@ -9,3 +9,3 @@ # react-draggable [![Build Status](https://travis-ci.org/mzabriskie/react-draggable.svg?branch=master)](https://travis-ci.org/mzabriskie/react-draggable)

http://mzabriskie.github.io/react-draggable/example/
[View Demo](http://mzabriskie.github.io/react-draggable/example/)

@@ -17,6 +17,20 @@

$ npm install react-draggable
# or
$ bower install react-draggable
```
If you aren't using browserify/webpack, a
[UMD version of react-draggable](http://mzabriskie.github.io/react-draggable/example/react-draggable.js)
is updated in the `gh-pages` branch and used for the demo. You can generate it yourself from master by cloning this
repository and running `$ make`. This will create umd dist files in the `dist/` folder.
## Details
A `<Draggable>` element wraps an existing element and extends it with new event handlers and styles.
It does not create a wrapper element in the DOM.
Draggable items are moved using CSS Transforms. This allows items to be dragged regardless of their current
positioning (relative, absolute, or static). Elements can also be moved between drags without incident.
If the item you are dragging already has a CSS Transform applied, it will be overwritten by `<Draggable>`. Use
an intermediate wrapper (`<Draggable><span>...</span></Draggable>`) in this case.
## Example

@@ -51,2 +65,4 @@

//
// The element is moved from its current position using absolute positioning.
//
// `axis` determines which axis the draggable can move.

@@ -63,4 +79,2 @@ // - 'both' allows movement horizontally and vertically (default).

//
// `start` specifies the x and y that the dragged item should start at
//
// `zIndex` specifies the zIndex to use while dragging.

@@ -78,3 +92,2 @@ //

grid={[25, 25]}
start={{x: 25, y: 25}}
zIndex={100}

@@ -99,6 +112,6 @@ onStart={this.handleStart}

- Fork the project
- `$ npm install && npm start`
- Make changes, webpack will watch and rebuild as you make changes
- Run the project in development mode: `$ make dev`
- Make changes.
- Add appropriate tests
- `$ npm test`
- `$ make test`
- If tests don't pass, make them pass.

@@ -108,4 +121,10 @@ - Update README with appropriate docs.

## Release checklist
- Update CHANGELOG
- `make release-patch`, `make release-minor`, or `make-release-major`
- `make publish`
## License
MIT
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc