@@ -10,10 +10,10 @@ var classNames = require('classnames');
1010//
1111
1212function createUIEvent ( draggable ) {
13- // State changes are often (but not always!) async. We want the latest value.
14- var state = draggable . _pendingState || draggable . state ;
13+ // State changes are often (but not always!) async. We want the latest value.
14+ var state = draggable . _pendingState || draggable . state ;
1515 return {
16- node : draggable . getDOMNode ( ) ,
16+ node : draggable . getDOMNode ( ) ,
1717 position : {
1818 top : state . clientY ,
1919 left : state . clientX
@@ -126,18 +126,20 @@ function removeEvent(el, event, handler) {
126126}
127127
128128function snapToGrid ( draggable , clientX , clientY ) {
129- var directionX = clientX < parseInt ( draggable . state . clientX , 10 ) ? - 1 : 1 ;
130- var directionY = clientY < parseInt ( draggable . state . clientY , 10 ) ? - 1 : 1 ;
129+ var stateX = parseInt ( draggable . state . clientX , 10 ) ;
130+ var stateY = parseInt ( draggable . state . clientY , 10 ) ;
131+ var directionX = clientX < stateX ? - 1 : 1 ;
132+ var directionY = clientY < stateY ? - 1 : 1 ;
131133
132- clientX = Math . abs ( clientX - parseInt ( draggable . state . clientX , 10 ) ) >= draggable . props . grid [ 0 ] ?
133- ( parseInt ( draggable . state . clientX , 10 ) + ( draggable . props . grid [ 0 ] * directionX ) ) :
134- draggable . state . clientX ;
134+ clientX = Math . abs ( clientX - stateX ) >= draggable . props . grid [ 0 ] ?
135+ ( stateX + ( draggable . props . grid [ 0 ] * directionX ) ) :
136+ stateX ;
135137
136- clientY = Math . abs ( clientY - parseInt ( draggable . state . clientY , 10 ) ) >= draggable . props . grid [ 1 ] ?
137- ( parseInt ( draggable . state . clientY , 10 ) + ( draggable . props . grid [ 1 ] * directionY ) ) :
138- draggable . state . clientY ;
138+ clientY = Math . abs ( clientY - stateY ) >= draggable . props . grid [ 1 ] ?
139+ ( stateY + ( draggable . props . grid [ 1 ] * directionY ) ) :
140+ stateY ;
139141
140- return [ clientX , clientY ] ;
142+ return [ clientX , clientY ] ;
141143}
142144
143145// Useful for preventing blue highlights all over everything when dragging.
@@ -149,6 +151,21 @@ var userSelectStyle = {
149151 userSelect : 'none' ,
150152} ;
151153
154+ function createCSSTransform ( style ) {
155+ if ( ! style . x && ! style . y ) return { } ;
156+ // Replace unitless items with px
157+ var x = style . x + 'px' ;
158+ var y = style . y + 'px' ;
159+ return {
160+ transform : 'translate(' + x + ',' + y + ')' ,
161+ WebkitTransform : 'translate(' + x + ',' + y + ')' ,
162+ OTransform : 'translate(' + x + ',' + y + ')' ,
163+ msTransform : 'translate(' + x + ',' + y + ')' ,
164+ MozTransform : 'translate(' + x + ',' + y + ')'
165+ } ;
166+ }
167+
168+
152169//
153170// End Helpers.
154171//
@@ -315,23 +332,12 @@ module.exports = React.createClass({
315332 onStop : React . PropTypes . func ,
316333
317334 /**
318- * 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)
319- *
335+ * A workaround option which can be passed if onMouseDown needs to be accessed,
336+ * since it'll always be blocked (due to that there's internal use of onMouseDown)
320337 */
321338 onMouseDown : React . PropTypes . func ,
322339 } ,
323340
324- componentDidMount : function ( ) {
325- var node = this . getDOMNode ( ) ;
326- this . setState ( {
327- mounted : true ,
328- startX : node . offsetLeft ,
329- startY : node . offsetTop ,
330- clientX : node . offsetLeft ,
331- clientY : node . offsetTop
332- } ) ;
333- } ,
334-
335341 componentWillUnmount : function ( ) {
336342 // Remove any leftover event handlers
337343 removeEvent ( window , dragEventFor [ 'move' ] , this . handleDrag ) ;
@@ -354,19 +360,13 @@ module.exports = React.createClass({
354360
355361 getInitialState : function ( ) {
356362 return {
357- // Whether or not this has been mounted
358- mounted : false ,
359-
360- // Whether or not currently dragging
363+ // Whether or not we are currently dragging.
361364 dragging : false ,
362365
363- // Start top/left of this.getDOMNode()
364- startX : 0 , startY : 0 ,
365-
366- // Offset between start top/left and mouse top/left
366+ // Offset between start top/left and mouse top/left while dragging.
367367 offsetX : 0 , offsetY : 0 ,
368368
369- // Current top/left of this.getDOMNode()
369+ // Current transform x and y.
370370 clientX : 0 , clientY : 0
371371 } ;
372372 } ,
@@ -379,8 +379,6 @@ module.exports = React.createClass({
379379 // return
380380 // }
381381
382- var node = this . getDOMNode ( ) ;
383-
384382 // Make it possible to attach event handlers on top of this one
385383 this . props . onMouseDown ( e ) ;
386384
@@ -392,15 +390,13 @@ module.exports = React.createClass({
392390
393391 var dragPoint = getControlPosition ( e ) ;
394392
395- // Initiate dragging
393+ // Initiate dragging. Set the current x and y as offsets
394+ // so we know how much we've moved during the drag. This allows us
395+ // to drag elements around even if they have been moved, without issue.
396396 this . setState ( {
397397 dragging : true ,
398- offsetX : parseInt ( dragPoint . clientX , 10 ) ,
399- offsetY : parseInt ( dragPoint . clientY , 10 ) ,
400- // Reset starting offsets. It's possible this item might have been
401- // moved by external forces, class changes, whatever.
402- startX : node . offsetLeft ,
403- startY : node . offsetTop
398+ offsetX : dragPoint . clientX - this . state . clientX ,
399+ offsetY : dragPoint . clientY - this . state . clientY
404400 } ) ;
405401
406402 // Call event handler
@@ -433,20 +429,20 @@ module.exports = React.createClass({
433429 handleDrag : function ( e ) {
434430 var dragPoint = getControlPosition ( e ) ;
435431
436- // Calculate top and left
437- var clientX = ( this . state . startX + ( dragPoint . clientX - this . state . offsetX ) ) ;
438- var clientY = ( this . state . startY + ( dragPoint . clientY - this . state . offsetY ) ) ;
432+ // Calculate X and Y
433+ var clientX = dragPoint . clientX - this . state . offsetX ;
434+ var clientY = dragPoint . clientY - this . state . offsetY ;
439435
440436 // Snap to grid if prop has been provided
441437 if ( Array . isArray ( this . props . grid ) ) {
442438 var coords = snapToGrid ( this , clientX , clientY ) ;
443439 clientX = coords [ 0 ] , clientY = coords [ 1 ] ;
444440 }
445441
446- // Update top and left
442+ // Update transform
447443 this . setState ( {
448- clientX : clientX ,
449- clientY : clientY
444+ clientX : clientX ,
445+ clientY : clientY
450446 } ) ;
451447
452448 // Call event handler
@@ -456,28 +452,24 @@ module.exports = React.createClass({
456452 render : function ( ) {
457453 // Create style object. We extend from existing styles so we don't
458454 // remove anything already set (like background, color, etc).
459- // We do completely overwrite the position.
460- var style = this . props . children . props . style || { } ;
461- if ( this . state . mounted ) {
462- style = assign ( { } , userSelectStyle , style , {
463- // Set top if vertical drag is enabled
464- top : canDragY ( this ) ?
465- this . state . clientY :
466- this . state . startY ,
467-
468- // Set left if horizontal drag is enabled
469- left : canDragX ( this ) ?
470- this . state . clientX :
471- this . state . startX ,
472-
473- position : 'absolute'
474- } ) ;
475-
476- // We only position with top/left. Delete bottom/right as they
477- // will screw us up.
478- delete style . bottom ;
479- delete style . right ;
480- }
455+ var childStyle = this . props . children . props . style || { } ;
456+
457+ // Add a CSS transform to move the element around. This allows us to move the element around
458+ // without worrying about whether or not it is relatively or absolutely positioned.
459+ // If the item you are dragging already has a transform set, wrap it in a <span> so <Draggable>
460+ // has a clean slate.
461+ var transform = createCSSTransform ( {
462+ // Set left if horizontal drag is enabled
463+ x : canDragX ( this ) ?
464+ this . state . clientX :
465+ 0 ,
466+
467+ // Set top if vertical drag is enabled
468+ y : canDragY ( this ) ?
469+ this . state . clientY :
470+ 0
471+ } ) ;
472+ var style = assign ( { } , userSelectStyle , childStyle , transform ) ;
481473
482474 // Set zIndex if currently dragging and prop has been provided
483475 if ( this . state . dragging && ! isNaN ( this . props . zIndex ) ) {
0 commit comments