Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 8 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 @@ -326,13 +331,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
22 changes: 16 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,23 @@ 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(controlPos: ControlPosition, positionOffset: PositionOffsetControlPosition): Object {
const translation = getTranslation(controlPos, positionOffset, 'px');
return {[browserPrefixToKey('transform', browserPrefix)]: translation };
}

export function createSVGTransform({x, y}: {x: number, y: number}): string {
return 'translate(' + x + ',' + y + ')';
export function createSVGTransform(controlPos: ControlPosition, positionOffset: PositionOffsetControlPosition): string {
const translation = getTranslation(controlPos, positionOffset, '');
return translation;
}
export function getTranslation({x, y}: ControlPosition, positionOffset: PositionOffsetControlPosition, unitSuffix: string): string {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

let translation = `translate(${x}${unitSuffix},${y}${unitSuffix})`;
if (positionOffset) {
const defaultX = `${(typeof positionOffset.x === 'string') ? positionOffset.x : positionOffset.x + unitSuffix}`;
const defaultY = `${(typeof positionOffset.y === 'string') ? positionOffset.y : positionOffset.y + unitSuffix}`;
translation = `translate(${defaultX}, ${defaultY})` + translation;
}
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