Skip to content

Commit c764303

Browse files
committed
Switch Resizable back to <Draggable> so it can handle bounds itself
1 parent 5e7f9aa commit c764303

File tree

3 files changed

+80
-69
lines changed

3 files changed

+80
-69
lines changed

lib/Resizable.jsx

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
1+
// @flow
12
import {default as React, PropTypes} from 'react';
2-
import {DraggableCore} from 'react-draggable';
3+
import Draggable from 'react-draggable';
34
import cloneElement from './cloneElement';
45

6+
type Bounds = {
7+
top: number,
8+
right: number,
9+
bottom: number,
10+
left: number
11+
};
512
type Position = {
613
deltaX: number,
714
deltaY: number
815
};
916
type State = {
1017
aspectRatio: number,
18+
bounds: Bounds,
1119
resizing: boolean,
1220
height: number,
1321
width: number,
1422
};
23+
type DragCallbackData = {
24+
node: HTMLElement,
25+
deltaX: number,
26+
deltaY: number,
27+
position: {left: number, top: number}
28+
};
1529

1630
export default class Resizable extends React.Component {
1731

@@ -34,6 +48,9 @@ export default class Resizable extends React.Component {
3448
// If you change this, be sure to update your css
3549
handleSize: PropTypes.array,
3650

51+
// If true, will only allow width/height to move in lockstep
52+
lockAspectRatio: PropTypes.bool,
53+
3754
// Min/max size
3855
minConstraints: PropTypes.arrayOf(PropTypes.number),
3956
maxConstraints: PropTypes.arrayOf(PropTypes.number),
@@ -48,55 +65,78 @@ export default class Resizable extends React.Component {
4865
};
4966

5067
static defaultProps = {
51-
handleSize: [20, 20]
68+
handleSize: [20, 20],
69+
lockAspectRatio: false,
70+
minConstraints: [20, 20],
71+
maxConstraints: [Infinity, Infinity]
5272
};
5373

54-
bounds: this.constraintsToBounds(),
5574
state: State = {
75+
aspectRatio: this.props.width / this.props.height,
76+
bounds: this.constraintsToBounds(),
77+
resizing: false,
78+
height: this.props.height,
5679
width: this.props.width,
57-
height: this.props.height
5880
};
5981

60-
componentWillReceiveProps(props: Object) {
61-
if (!this.state.resizing) {
82+
componentWillReceiveProps(nextProps: Object) {
83+
if (!this.state.resizing &&
84+
(nextProps.width !== this.props.width || nextProps.height !== this.props.height)) {
6285
this.setState({
63-
width: props.width,
64-
height: props.height,
65-
bounds: this.constraintsToBounds()
86+
width: nextProps.width,
87+
height: nextProps.height
6688
});
6789
}
6890
}
6991

70-
constraintsToBounds() {
71-
let p = this.props;
72-
let mins = p.minConstraints || p.handleSize;
73-
let maxes = p.maxConstraints || [Infinity, Infinity];
92+
constraintsToBounds(): Bounds {
93+
const {minConstraints, maxConstraints, height, width} = this.props;
7494
return {
75-
left: mins[0] - p.width,
76-
top: mins[1] - p.height,
77-
right: maxes[0] - p.width,
78-
bottom: maxes[1] - p.height
95+
left: minConstraints[0] - width,
96+
top: minConstraints[1] - height,
97+
right: maxConstraints[0] - width,
98+
bottom: maxConstraints[1] - height
7999
};
80100
}
81101

102+
lockAspectRatio(width: number, height: number, aspectRatio: number): [number, number] {
103+
height = width / this.state.aspectRatio;
104+
width = height * this.state.aspectRatio;
105+
return [width, height];
106+
}
107+
82108
/**
83109
* Wrapper around drag events to provide more useful data.
84110
*
85111
* @param {String} handlerName Handler name to wrap.
86112
* @return {Function} Handler function.
87113
*/
88114
resizeHandler(handlerName: string): Function {
89-
return (e, {node, position}: {node: HTMLElement, position: Position}) => {
90-
let width = this.state.width + position.deltaX, height = this.state.height + position.deltaY;
91-
this.props[handlerName] && this.props[handlerName](e, {node, size: {width, height}});
115+
return (e, {node, deltaX, deltaY}: DragCallbackData) => {
116+
let width = this.state.width + deltaX, height = this.state.height + deltaY;
117+
118+
let widthChanged = width !== this.state.width, heightChanged = height !== this.state.height;
119+
if (!widthChanged && !heightChanged) return;
120+
121+
if (this.props.lockAspectRatio) {
122+
[width, height] = this.lockAspectRatio(width, height, this.state.aspectRatio);
123+
}
92124

125+
// Set the appropriate state for this handler.
126+
let newState = {};
93127
if (handlerName === 'onResizeStart') {
94-
this.setState({resizing: true});
128+
newState.resizing = true;
95129
} else if (handlerName === 'onResizeStop') {
96-
this.setState({resizing: false});
130+
newState.resizing = false;
97131
} else {
98-
this.setState({width, height});
132+
newState.width = width;
133+
newState.height = height;
99134
}
135+
136+
this.setState(newState, () => {
137+
this.props[handlerName] && this.props[handlerName](e, {node, size: {width, height}});
138+
});
139+
100140
};
101141
}
102142

@@ -115,16 +155,17 @@ export default class Resizable extends React.Component {
115155
className,
116156
children: [
117157
p.children.props.children,
118-
<DraggableCore
158+
<Draggable
119159
{...p.draggableOpts}
120160
ref="draggable"
161+
axis="none"
121162
onStop={this.resizeHandler('onResizeStop')}
122163
onStart={this.resizeHandler('onResizeStart')}
123164
onDrag={this.resizeHandler('onResize')}
124165
bounds={this.state.bounds}
125166
>
126167
<span className="react-resizable-handle" />
127-
</DraggableCore>
168+
</Draggable>
128169
]
129170
});
130171
}

lib/ResizableBox.jsx

Lines changed: 13 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @flow
12
import {default as React, PropTypes} from 'react';
23
import Resizable from './Resizable';
34

@@ -8,79 +9,48 @@ type ResizeData = {element: Element, size: Size};
89
// An example use of Resizable.
910
export default class ResizableBox extends React.Component {
1011
static propTypes = {
11-
lockAspectRatio: PropTypes.bool,
12-
minConstraints: PropTypes.arrayOf(PropTypes.number),
13-
maxConstraints: PropTypes.arrayOf(PropTypes.number),
1412
height: PropTypes.number,
1513
width: PropTypes.number
1614
};
1715

1816
static defaultProps = {
19-
lockAspectRatio: false,
2017
handleSize: [20,20]
2118
};
2219

2320
state: State = {
2421
width: this.props.width,
2522
height: this.props.height,
26-
aspectRatio: this.props.width / this.props.height
2723
};
2824

2925
onResize = (event, {element, size}) => {
3026
let {width, height} = size;
31-
let widthChanged = width !== this.state.width, heightChanged = height !== this.state.height;
32-
if (!widthChanged && !heightChanged) return;
3327

34-
[width, height] = this.runConstraints(width, height);
35-
36-
this.setState({width, height}, () => {
37-
if (this.props.onResize) {
38-
this.props.onResize(event, {element, size: {width, height}});
39-
}
28+
this.setState(size, () => {
29+
this.props.onResize && this.props.onResize(event, {element, size});
4030
});
41-
}
42-
43-
// If you do this, be careful of constraints
44-
runConstraints(width: number, height: number) {
45-
let [min, max] = [this.props.minConstraints, this.props.maxConstraints];
46-
47-
if (this.props.lockAspectRatio) {
48-
height = width / this.state.aspectRatio;
49-
width = height * this.state.aspectRatio;
50-
}
51-
52-
if (min) {
53-
width = Math.max(min[0], width);
54-
height = Math.max(min[1], height);
55-
}
56-
if (max) {
57-
width = Math.min(max[0], width);
58-
height = Math.min(max[1], height);
59-
}
60-
return [width, height];
6131
};
6232
onResize: (event: Event, data: ResizeData) => void;
6333

6434
render(): ReactElement {
6535
// Basic wrapper around a Resizable instance.
66-
// If you use Resizable directly, you are responsible for updating the component
36+
// If you use Resizable directly, you are responsible for updating the child component
6737
// with a new width and height.
68-
let {handleSize, minConstraints, maxConstraints, ...props} = this.props;
38+
let {handleSize, onResizeStart, onResizeStop, draggableOpts,
39+
minConstraints, maxConstraints, lockAspectRatio, ...props} = this.props;
6940
return (
7041
<Resizable
71-
minConstraints={minConstraints}
72-
maxConstraints={maxConstraints}
7342
handleSize={handleSize}
7443
width={this.state.width}
7544
height={this.state.height}
76-
onResizeStart={this.props.onResizeStart}
45+
onResizeStart={onResizeStart}
7746
onResize={this.onResize}
78-
onResizeStop={this.props.onResizeStop}
79-
draggableOpts={this.props.draggableOpts}
47+
onResizeStop={onResizeStop}
48+
draggableOpts={draggableOpts}
49+
minConstraints={minConstraints}
50+
maxConstraints={maxConstraints}
51+
lockAspectRatio={lockAspectRatio}
8052
>
81-
<div style={{width: this.state.width + 'px', height: this.state.height + 'px'}} {...props}>
82-
{this.props.children}
83-
</div>
53+
<div style={{width: this.state.width + 'px', height: this.state.height + 'px'}} {...props} />
8454
</Resizable>
8555
);
8656
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"webpack-dev-server": "^1.14.1"
4949
},
5050
"dependencies": {
51-
"react-draggable": "^1.2.0"
51+
"react-draggable": "^1.3.1"
5252
},
5353
"peerDependencies": {
5454
"react": "^0.14.7",

0 commit comments

Comments
 (0)