1+ // @flow
12import { default as React , PropTypes } from 'react' ;
2- import { DraggableCore } from 'react-draggable' ;
3+ import Draggable from 'react-draggable' ;
34import cloneElement from './cloneElement' ;
45
6+ type Bounds = {
7+ top : number ,
8+ right : number ,
9+ bottom : number ,
10+ left : number
11+ } ;
512type Position = {
613 deltaX : number ,
714 deltaY : number
815} ;
916type 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
1630export 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 }
0 commit comments