Skip to content

Commit 7566939

Browse files
committed
feat: support to use absolute offset positioning
Currently `react-draggable` uses `transform: translate(...)` to change position of dragable component. Issue with using of transform is that it creates a new stacking context and thus prevents some css attributes like `mix-blend-multiply` from working in child components. This change adds a new `useTransfrom` property. If `useTransfrom` is set to false, absolute positioning and left/top offset will be used instead of using `transfrom`.
1 parent 31798e9 commit 7566939

File tree

5 files changed

+47
-4
lines changed

5 files changed

+47
-4
lines changed

example/example.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ class App extends React.Component {
206206
</div>
207207
</div>
208208
</Draggable>
209+
<Draggable {...dragHandlers} defaultPosition={{x: 50, y: 750}} useTransform={false}>
210+
<div className="box">
211+
I don't use transform for positioning, rather I have absolute position and use left/top offset.
212+
</div>
213+
</Draggable>
209214

210215
</div>
211216
);

lib/Draggable.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import * as React from 'react';
33
import PropTypes from 'prop-types';
44
import ReactDOM from 'react-dom';
55
import clsx from 'clsx';
6-
import {createCSSTransform, createSVGTransform} from './utils/domFns';
6+
import {
7+
createCSSOffset,
8+
createCSSTransform,
9+
createSVGTransform,
10+
} from './utils/domFns';
711
import {canDragX, canDragY, createDraggableData, getBoundPosition} from './utils/positionFns';
812
import {dontSetMe} from './utils/shims';
913
import DraggableCore from './DraggableCore';
@@ -30,6 +34,7 @@ export type DraggableDefaultProps = {
3034
defaultClassNameDragged: string,
3135
defaultPosition: ControlPosition,
3236
scale: number,
37+
useTransform: boolean,
3338
};
3439

3540
export type DraggableProps = {
@@ -174,7 +179,8 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
174179
defaultClassNameDragging: 'react-draggable-dragging',
175180
defaultClassNameDragged: 'react-draggable-dragged',
176181
defaultPosition: {x: 0, y: 0},
177-
scale: 1
182+
scale: 1,
183+
useTransform: true
178184
};
179185

180186
// React 16.3+
@@ -340,6 +346,7 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
340346
position,
341347
positionOffset,
342348
scale,
349+
useTransform,
343350
...draggableCoreProps
344351
} = this.props;
345352

@@ -366,12 +373,14 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
366373
// If this element was SVG, we use the `transform` attribute.
367374
if (this.state.isElementSVG) {
368375
svgTransform = createSVGTransform(transformOpts, positionOffset);
369-
} else {
376+
} else if (useTransform) {
370377
// Add a CSS transform to move the element around. This allows us to move the element around
371378
// without worrying about whether or not it is relatively or absolutely positioned.
372379
// If the item you are dragging already has a transform set, wrap it in a <span> so <Draggable>
373380
// has a clean slate.
374381
style = createCSSTransform(transformOpts, positionOffset);
382+
} else {
383+
style = createCSSOffset(transformOpts, positionOffset);
375384
}
376385

377386
// Mark with class while dragging

lib/utils/domFns.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export function createSVGTransform(controlPos: ControlPosition, positionOffset:
126126
const translation = getTranslation(controlPos, positionOffset, '');
127127
return translation;
128128
}
129+
129130
export function getTranslation({x, y}: ControlPosition, positionOffset: PositionOffsetControlPosition, unitSuffix: string): string {
130131
let translation = `translate(${x}${unitSuffix},${y}${unitSuffix})`;
131132
if (positionOffset) {
@@ -136,6 +137,16 @@ export function getTranslation({x, y}: ControlPosition, positionOffset: Position
136137
return translation;
137138
}
138139

140+
export function createCSSOffset({x, y}: ControlPosition, positionOffset: PositionOffsetControlPosition): Object {
141+
const unitSuffix = 'px';
142+
if (positionOffset) {
143+
const defaultX = `${(typeof positionOffset.x === 'string') ? positionOffset.x : positionOffset.x + unitSuffix}`;
144+
const defaultY = `${(typeof positionOffset.y === 'string') ? positionOffset.y : positionOffset.y + unitSuffix}`;
145+
return {left: defaultX, top: defaultY};
146+
}
147+
return {position: 'absolute', left: x + unitSuffix, top: y + unitSuffix};
148+
}
149+
139150
export function getTouch(e: MouseTouchEvent, identifier: number): ?{clientX: number, clientY: number} {
140151
return (e.targetTouches && findInArray(e.targetTouches, t => identifier === t.identifier)) ||
141152
(e.changedTouches && findInArray(e.changedTouches, t => identifier === t.identifier));

specs/draggable.spec.jsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,23 @@ describe('react-draggable', function () {
256256
assert(style.indexOf('transform: translate(100px, 100px);') >= 0);
257257
});
258258

259+
it('should render with style left/top for DOM nodes if useTranslate is false', function () {
260+
let dragged = false;
261+
drag = TestUtils.renderIntoDocument(
262+
<Draggable onDrag={function() { dragged = true; }} useTransform={false}>
263+
<div />
264+
</Draggable>
265+
);
266+
267+
const node = ReactDOM.findDOMNode(drag);
268+
simulateMovementFromTo(drag, 0, 0, 100, 100);
269+
270+
const style = node.getAttribute('style');
271+
assert(dragged === true);
272+
assert(style.indexOf('left: 100px;') >= 0);
273+
assert(style.indexOf('top: 100px;') >= 0);
274+
});
275+
259276
it('should render with positionOffset set as string transform and handle subsequent translate() for DOM nodes', function () {
260277
let dragged = false;
261278
drag = TestUtils.renderIntoDocument(

typings/index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ declare module 'react-draggable' {
1616
defaultClassNameDragged: string,
1717
defaultPosition: ControlPosition,
1818
positionOffset: PositionOffsetControlPosition,
19-
position: ControlPosition
19+
position: ControlPosition,
20+
useTransform: boolean,
2021
}
2122

2223
export type DraggableEvent = React.MouseEvent<HTMLElement | SVGElement>

0 commit comments

Comments
 (0)