Skip to content

Commit d694219

Browse files
committed
Revert use of <Draggable> over <DraggableCore> and implement own constraints.
1 parent d6efb26 commit d694219

File tree

1 file changed

+56
-41
lines changed

1 file changed

+56
-41
lines changed

lib/Resizable.jsx

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
11
// @flow
22
import {default as React, PropTypes} from 'react';
3-
import Draggable from 'react-draggable';
3+
import {DraggableCore} from 'react-draggable';
44
import cloneElement from './cloneElement';
55

6-
type Bounds = {
7-
top: number,
8-
right: number,
9-
bottom: number,
10-
left: number
11-
};
126
type Position = {
137
deltaX: number,
148
deltaY: number
159
};
1610
type State = {
1711
resizing: boolean,
1812
width: number, height: number,
19-
initialWidth: number, initialHeight: number
13+
slackW: number, slackH: number
2014
};
2115
type DragCallbackData = {
2216
node: HTMLElement,
23-
deltaX: number,
24-
deltaY: number,
25-
position: {left: number, top: number}
17+
position: Position
2618
};
2719

2820
export default class Resizable extends React.Component {
@@ -71,10 +63,8 @@ export default class Resizable extends React.Component {
7163

7264
state: State = {
7365
resizing: false,
74-
initialHeight: this.props.height,
75-
initialWidth: this.props.width,
76-
height: this.props.height,
77-
width: this.props.width,
66+
width: this.props.width, height: this.props.height,
67+
slackW: 0, slackH: 0
7868
};
7969

8070
componentWillReceiveProps(nextProps: Object) {
@@ -83,50 +73,75 @@ export default class Resizable extends React.Component {
8373
(nextProps.width !== this.props.width || nextProps.height !== this.props.height)) {
8474
this.setState({
8575
width: nextProps.width,
86-
height: nextProps.height,
87-
initialWidth: nextProps.width,
88-
initialHeight: nextProps.height
76+
height: nextProps.height
8977
});
9078
}
9179
}
9280

93-
/**
94-
* Convert our constraints into bounds for the <Draggable>.
95-
* We have to use initialHeight/Width here because that's where the <Draggable>'s `0` is.
96-
*/
97-
constraintsToBounds(): Bounds {
98-
const {minConstraints, maxConstraints} = this.props;
99-
const {initialHeight, initialWidth} = this.state;
100-
return {
101-
left: minConstraints[0] - initialWidth,
102-
top: minConstraints[1] - initialHeight,
103-
right: maxConstraints[0] - initialWidth,
104-
bottom: maxConstraints[1] - initialHeight
105-
};
106-
}
107-
10881
lockAspectRatio(width: number, height: number, aspectRatio: number): [number, number] {
10982
height = width / aspectRatio;
11083
width = height * aspectRatio;
11184
return [width, height];
11285
}
11386

87+
// If you do this, be careful of constraints
88+
runConstraints(width: number, height: number): [number, number] {
89+
let [min, max] = [this.props.minConstraints, this.props.maxConstraints];
90+
91+
if (this.props.lockAspectRatio) {
92+
const ratio = this.state.width / this.state.height;
93+
height = width / ratio;
94+
width = height * ratio;
95+
}
96+
97+
if (!min && !max) return [width, height];
98+
99+
let [oldW, oldH] = [width, height];
100+
101+
// Add slack to the values used to calculate bound position. This will ensure that if
102+
// we start removing slack, the element won't react to it right away until it's been
103+
// completely removed.
104+
let {slackW, slackH} = this.state;
105+
width += slackW;
106+
height += slackH;
107+
108+
if (min) {
109+
width = Math.max(min[0], width);
110+
height = Math.max(min[1], height);
111+
}
112+
if (max) {
113+
width = Math.min(max[0], width);
114+
height = Math.min(max[1], height);
115+
}
116+
117+
// If the numbers changed, we must have introduced some slack. Record it for the next iteration.
118+
slackW += (oldW - width);
119+
slackH += (oldH - height);
120+
if (slackW !== this.state.slackW || slackH !== this.state.slackH) {
121+
this.setState({slackW, slackH});
122+
}
123+
124+
return [width, height];
125+
}
126+
114127
/**
115128
* Wrapper around drag events to provide more useful data.
116129
*
117130
* @param {String} handlerName Handler name to wrap.
118131
* @return {Function} Handler function.
119132
*/
120133
resizeHandler(handlerName: string): Function {
121-
return (e, {node, deltaX, deltaY}: DragCallbackData) => {
134+
return (e, {node, position}: DragCallbackData) => {
135+
const {deltaX, deltaY} = position;
122136
let width = this.state.width + deltaX, height = this.state.height + deltaY;
123137

138+
// Early return if no change
124139
let widthChanged = width !== this.state.width, heightChanged = height !== this.state.height;
125140
if (handlerName === 'onResize' && !widthChanged && !heightChanged) return;
126141

127-
if (this.props.lockAspectRatio) {
128-
[width, height] = this.lockAspectRatio(width, height, this.state.width / this.state.height);
129-
}
142+
console.log({width, height, slackW: this.state.slackW, slackH: this.state.slackH});
143+
[width, height] = this.runConstraints(width, height);
144+
console.log({width, height, slackW: this.state.slackW, slackH: this.state.slackH});
130145

131146
// Set the appropriate state for this handler.
132147
let newState = {};
@@ -135,6 +150,8 @@ export default class Resizable extends React.Component {
135150
} else if (handlerName === 'onResizeStop') {
136151
newState.resizing = false;
137152
} else {
153+
// Early return if no change after constraints
154+
if (width === this.state.width && height === this.state.height) return;
138155
newState.width = width;
139156
newState.height = height;
140157
}
@@ -161,17 +178,15 @@ export default class Resizable extends React.Component {
161178
className,
162179
children: [
163180
p.children.props.children,
164-
<Draggable
181+
<DraggableCore
165182
{...p.draggableOpts}
166183
ref="draggable"
167-
axis="none"
168184
onStop={this.resizeHandler('onResizeStop')}
169185
onStart={this.resizeHandler('onResizeStart')}
170186
onDrag={this.resizeHandler('onResize')}
171-
bounds={this.constraintsToBounds()}
172187
>
173188
<span className="react-resizable-handle" />
174-
</Draggable>
189+
</DraggableCore>
175190
]
176191
});
177192
}

0 commit comments

Comments
 (0)