Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions example/example.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ var App = React.createClass({
{"I have a default position of {x: 25, y: 25}, so I'm slightly offset."}
</div>
</Draggable>
<Draggable positionOffset={{x: '-10%', y: '-10%'}} {...dragHandlers}>
<div className="box">
{'I have a default position based on percents {x: \'-10%\', y: \'-10%\'}, so I\'m slightly offset.'}
</div>
</Draggable>
<Draggable position={controlledPosition} {...dragHandlers} onDrag={this.onControlledDrag}>
<div className="box">
My position can be changed programmatically. <br />
Expand Down
13 changes: 10 additions & 3 deletions lib/Draggable.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {createCSSTransform, createSVGTransform} from './utils/domFns';
import {canDragX, canDragY, createDraggableData, getBoundPosition} from './utils/positionFns';
import {dontSetMe} from './utils/shims';
import DraggableCore from './DraggableCore';
import type {ControlPosition, DraggableBounds, DraggableCoreProps} from './DraggableCore';
import type {ControlPosition, PositionOffsetControlPosition, DraggableBounds, DraggableCoreProps} from './DraggableCore';
import log from './utils/log';
import type {DraggableEventHandler} from './utils/types';
import type {Element as ReactElement} from 'react';
Expand All @@ -28,6 +28,7 @@ export type DraggableProps = {
defaultClassNameDragging: string,
defaultClassNameDragged: string,
defaultPosition: ControlPosition,
positionOffset: PositionOffsetControlPosition,
position: ControlPosition,
scale: number
};
Expand Down Expand Up @@ -121,6 +122,10 @@ export default class Draggable extends React.Component<DraggableProps, Draggable
x: PropTypes.number,
y: PropTypes.number
}),
positionOffset: PropTypes.shape({
x: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
y: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
}),

/**
* `position`, if present, defines the current position of the element.
Expand Down Expand Up @@ -178,6 +183,8 @@ export default class Draggable extends React.Component<DraggableProps, Draggable
dragged: false,

// Current transform x and y.
// x: props.position ? props.position.x : 0,
// y: props.position ? props.position.y : 0,
x: props.position ? props.position.x : props.defaultPosition.x,
y: props.position ? props.position.y : props.defaultPosition.y,

Expand Down Expand Up @@ -326,13 +333,13 @@ export default class Draggable extends React.Component<DraggableProps, Draggable

// If this element was SVG, we use the `transform` attribute.
if (this.state.isElementSVG) {
svgTransform = createSVGTransform(transformOpts);
svgTransform = createSVGTransform(transformOpts, this.props.positionOffset);
} else {
// 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.
style = createCSSTransform(transformOpts);
style = createCSSTransform(transformOpts, this.props.positionOffset);
}

const {
Expand Down
1 change: 1 addition & 0 deletions lib/DraggableCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export type DraggableData = {
export type DraggableEventHandler = (e: MouseEvent, data: DraggableData) => void;

export type ControlPosition = {x: number, y: number};
export type PositionOffsetControlPosition = {x: number|string, y: number|string};

export type DraggableCoreProps = {
allowAnyClick: boolean,
Expand Down
27 changes: 21 additions & 6 deletions lib/utils/domFns.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import {findInArray, isFunction, int} from './shims';
import browserPrefix, {browserPrefixToKey} from './getPrefix';

import type {ControlPosition, MouseTouchEvent} from './types';
import type {ControlPosition, PositionOffsetControlPosition, MouseTouchEvent} from './types';

let matchesSelectorFunc = '';
export function matchesSelector(el: Node, selector: string): boolean {
Expand Down Expand Up @@ -109,13 +109,28 @@ export function offsetXYFromParent(evt: {clientX: number, clientY: number}, offs
return {x, y};
}

export function createCSSTransform({x, y}: {x: number, y: number}): Object {
// Replace unitless items with px
return {[browserPrefixToKey('transform', browserPrefix)]: 'translate(' + x + 'px,' + y + 'px)'};
export function createCSSTransform({x, y}: ControlPosition, positionOffset: PositionOffsetControlPosition): Object {
let translation;
if (positionOffset && (positionOffset.x !== 0 || positionOffset.y !== 0)) {
const defaultX = `${(typeof positionOffset.x === 'string') ? positionOffset.x : positionOffset.x + 'px'}`;
const defaultY = `${(typeof positionOffset.y === 'string') ? positionOffset.y : positionOffset.y + 'px'}`;
translation = `translate(${defaultX}, ${defaultY}) translate(${x}px,${y}px)`;
} else {
translation = `translate(${x}px,${y}px)`;
}
return {[browserPrefixToKey('transform', browserPrefix)]: translation };
}

export function createSVGTransform({x, y}: {x: number, y: number}): string {
return 'translate(' + x + ',' + y + ')';
export function createSVGTransform({x, y}: ControlPosition, positionOffset: PositionOffsetControlPosition): string {
let translation;
if (positionOffset && (positionOffset.x !== 0 || positionOffset.y !== 0)) {
const defaultX = (typeof positionOffset.x === 'string') ? positionOffset.x : positionOffset.x;
const defaultY = (typeof positionOffset.y === 'string') ? positionOffset.y : positionOffset.y;
translation = `translate(${defaultX}, ${defaultY}) translate(${x},${y})`;
} else {
translation = `translate(${x},${y})`;
}
return translation;
}

export function getTouch(e: MouseTouchEvent, identifier: number): ?{clientX: number, clientY: number} {
Expand Down
1 change: 1 addition & 0 deletions lib/utils/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type Bounds = {
left: number, top: number, right: number, bottom: number
};
export type ControlPosition = {x: number, y: number};
export type PositionOffsetControlPosition = {x: number|string, y: number|string};
export type EventHandler<T> = (e: T) => void | false;

// Missing in Flow
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@
"classnames": "^2.2.5",
"prop-types": "^15.6.0"
}
}
}
16 changes: 16 additions & 0 deletions specs/draggable.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,22 @@ describe('react-draggable', function () {
assert(style.indexOf('transform: translate(100px, 100px);') >= 0);
});

it('should render with positionOffset set as string transform and handle subsequent translate() for DOM nodes', function () {
let dragged = false;
drag = TestUtils.renderIntoDocument(
<Draggable positionOffset={{x: '10%', y: '10%'}} onDrag={function() { dragged = true; }}>
<div />
</Draggable>
);

const node = ReactDOM.findDOMNode(drag);
simulateMovementFromTo(drag, 0, 0, 100, 100);

const style = node.getAttribute('style');
assert(dragged === true);
assert(style.indexOf('translate(10%, 10%) translate(100px, 100px);') >= 0);
});

it('should honor "x" axis', function () {
let dragged = false;
drag = TestUtils.renderIntoDocument(
Expand Down