@@ -19,27 +19,34 @@ export default class Resizable extends React.Component<Props, ResizableState> {
1919 transformScale : 1
2020 } ;
2121
22- state : ResizableState = {
23- slackW : 0 , slackH : 0 ,
24- } ;
22+ state : ResizableState = undefined ;
2523
2624 lastHandleRect : ?ClientRect = null ;
27- draggingNode : ?HTMLElement = null ;
25+ slack : ?[ number , number ] = null ;
26+
27+ componentWillUnmount ( ) {
28+ this . resetData ( ) ;
29+ }
2830
2931 lockAspectRatio ( width : number , height : number , aspectRatio : number ) : [ number , number ] {
3032 height = width / aspectRatio ;
3133 width = height * aspectRatio ;
3234 return [ width , height ] ;
3335 }
3436
35- // If you do this, be careful of constraints
37+ resetData ( ) {
38+ this . lastHandleRect = this . slack = null ;
39+ }
40+
41+ // Clamp width and height within provided constraints
3642 runConstraints ( width : number , height : number ) : [ number , number ] {
3743 const [ min , max ] = [ this . props . minConstraints , this . props . maxConstraints ] ;
3844 if ( ! min && ! max ) return [ width , height ] ;
3945
40- // Fit width & height to aspect ratio
46+ // If constraining to min and max, we need to also fit width and height to aspect ratio.
4147 if ( this . props . lockAspectRatio ) {
42- if ( height === this . props . height ) {
48+ const resizingHorizontally = height === this . props . height ;
49+ if ( resizingHorizontally ) {
4350 const ratio = this . props . width / this . props . height ;
4451 height = width / ratio ;
4552 width = height * ratio ;
@@ -57,7 +64,7 @@ export default class Resizable extends React.Component<Props, ResizableState> {
5764 // Add slack to the values used to calculate bound position. This will ensure that if
5865 // we start removing slack, the element won't react to it right away until it's been
5966 // completely removed.
60- let { slackW, slackH} = this . state ;
67+ let [ slackW , slackH ] = this . slack || [ 0 , 0 ] ;
6168 width += slackW ;
6269 height += slackH ;
6370
@@ -70,12 +77,8 @@ export default class Resizable extends React.Component<Props, ResizableState> {
7077 height = Math . min ( max [ 1 ] , height ) ;
7178 }
7279
73- // If the numbers changed, we must have introduced some slack. Record it for the next iteration.
74- slackW += ( oldW - width ) ;
75- slackH += ( oldH - height ) ;
76- if ( slackW !== this . state . slackW || slackH !== this . state . slackH ) {
77- this . setState ( { slackW, slackH} ) ;
78- }
80+ // If the width or height changed, we must have introduced some slack. Record it for the next iteration.
81+ this . slack = [ slackW + ( oldW - width ) , slackH + ( oldH - height ) ] ;
7982
8083 return [ width , height ] ;
8184 }
@@ -86,83 +89,72 @@ export default class Resizable extends React.Component<Props, ResizableState> {
8689 * @param {String } handlerName Handler name to wrap.
8790 * @return {Function } Handler function.
8891 */
89- resizeHandler ( handlerName : string , axis : ResizeHandleAxis ) : Function {
90- return ( e : SyntheticEvent < > | MouseEvent , { node , deltaX , deltaY } : DragCallbackData ) => {
92+ resizeHandler ( handlerName : 'onResize' | 'onResizeStart' | 'onResizeStop' , axis : ResizeHandleAxis ) : Function {
93+ return ( e : SyntheticEvent < > , { node, deltaX, deltaY} : DragCallbackData ) => {
94+ // Reset data in case it was left over somehow (should not be possible)
95+ if ( handlerName === 'onResizeStart' ) this . resetData ( ) ;
96+
97+ // Handle scale parameters
9198 deltaX /= this . props . transformScale ;
9299 deltaY /= this . props . transformScale ;
93100
94101 // Axis restrictions
95- const canDragX = ( this . props . axis === 'both' || this . props . axis === 'x' ) && [ 'n' , 's' ] . indexOf ( axis ) === - 1 ;
96- const canDragY = ( this . props . axis === 'both' || this . props . axis === 'y' ) && [ 'e' , 'w' ] . indexOf ( axis ) === - 1 ;
97-
98- /*
99- Track the element being dragged to account for changes in position.
100- If a handle's position is changed between callbacks, we need to factor this in to the next callback
101- */
102- if ( this . draggingNode == null && e . target instanceof HTMLElement ) {
103- this . draggingNode = e . target ;
104- }
105- if ( this . draggingNode instanceof HTMLElement ) {
106- const handleRect = this . draggingNode . getBoundingClientRect ( ) ;
107- if ( this . lastHandleRect != null ) {
108- // Find how much the handle has moved since the last callback
102+ const canDragX = ( this . props . axis === 'both' || this . props . axis === 'x' ) && axis !== 'n' && axis !== 's' ;
103+ const canDragY = ( this . props . axis === 'both' || this . props . axis === 'y' ) && axis !== 'e' && axis !== 'w' ;
104+ // No dragging possible.
105+ if ( ! canDragX && ! canDragY ) return ;
106+
107+ // Decompose axis for later use
108+ const axisV = axis [ 0 ] ;
109+ const axisH = axis [ axis . length - 1 ] ; // intentionally not axis[1], so that this catches axis === 'w' for example
110+
111+ // Track the element being dragged to account for changes in position.
112+ // If a handle's position is changed between callbacks, we need to factor this in to the next callback.
113+ // Failure to do so will cause the element to "skip" when resized upwards or leftwards.
114+ const handleRect = node . getBoundingClientRect ( ) ;
115+ if ( this . lastHandleRect != null ) {
116+ // If the handle has repositioned on either axis since last render,
117+ // we need to increase our callback values by this much.
118+ // Only checking 'n', 'w' since resizing by 's', 'w' won't affect the overall position on page,
119+ if ( axisH === 'w' ) {
109120 const deltaLeftSinceLast = handleRect . left - this . lastHandleRect . left ;
121+ deltaX += deltaLeftSinceLast / this . props . transformScale ;
122+ }
123+ if ( axisV === 'n' ) {
110124 const deltaTopSinceLast = handleRect . top - this . lastHandleRect . top ;
111-
112- // If the handle has repositioned on either axis since last render,
113- // we need to increase our callback values by this much.
114- // Only checking 'n', 'w' since resizing by 's', 'w' won't affect the overall position on page
115- if ( canDragX && axis [ axis . length - 1 ] === 'w' ) {
116- deltaX += deltaLeftSinceLast / this . props . transformScale ;
117- }
118- if ( canDragY && axis [ 0 ] === 'n' ) {
119- deltaY += deltaTopSinceLast / this . props . transformScale ;
120- }
125+ deltaY += deltaTopSinceLast / this . props . transformScale ;
121126 }
122- this . lastHandleRect = {
123- top : handleRect . top ,
124- left : handleRect . left ,
125- } ;
126127 }
128+ // Storage of last rect so we know how much it has really moved.
129+ this . lastHandleRect = {
130+ top : handleRect . top ,
131+ left : handleRect . left ,
132+ } ;
127133
128- // reverse delta if using top or left drag handles
129- if ( canDragX && axis [ axis . length - 1 ] === 'w' ) {
130- deltaX = - deltaX ;
131- }
132- if ( canDragY && axis [ 0 ] === 'n' ) {
133- deltaY = - deltaY ;
134- }
134+ // Reverse delta if using top or left drag handles.
135+ if ( axisH === 'w' ) deltaX = - deltaX ;
136+ if ( axisV === 'n' ) deltaY = - deltaY ;
135137
136- // Update w/h
138+ // Update w/h by the deltas.
137139 let width = this . props . width + ( canDragX ? deltaX : 0 ) ;
138140 let height = this . props . height + ( canDragY ? deltaY : 0 ) ;
139141
140- // Early return if no change
141- const widthChanged = width !== this . props . width , heightChanged = height !== this . props . height ;
142- if ( handlerName === 'onResize' && ! widthChanged && ! heightChanged ) return ;
143-
142+ // Run user-provided constraints.
144143 [ width , height ] = this . runConstraints ( width , height ) ;
145144
146- // Set the appropriate state for this handler.
147- const newState = { } ;
148- if ( handlerName === 'onResizeStart' ) {
149- // nothing
150- } else if ( handlerName === 'onResizeStop' ) {
151- newState . slackW = newState . slackH = 0 ;
152- this . lastHandleRect = this . draggingNode = null ;
153- } else {
154- // Early return if no change after constraints
155- if ( width === this . props . width && height === this . props . height ) return ;
156- }
145+ const dimensionsChanged = width !== this . props . width || height !== this . props . height ;
157146
158- const hasCb = typeof this . props [ handlerName ] === 'function' ;
159- if ( hasCb ) {
160- // $FlowIgnore isn't refining this correctly to SyntheticEvent
147+ // Call user-supplied callback if present.
148+ const cb = typeof this . props [ handlerName ] === 'function' ? this . props [ handlerName ] : null ;
149+ // Don't call 'onResize' if dimensions haven't changed.
150+ const shouldSkipCb = handlerName === 'onResize' && ! dimensionsChanged ;
151+ if ( cb && ! shouldSkipCb ) {
161152 if ( typeof e . persist === 'function' ) e . persist ( ) ;
162- this . setState ( newState , ( ) => this . props [ handlerName ] ( e , { node, size : { width, height} , handle : axis } ) ) ;
163- } else {
164- this . setState ( newState ) ;
153+ cb ( e , { node, size : { width, height} , handle : axis } ) ;
165154 }
155+
156+ // Reset internal data
157+ if ( handlerName === 'onResizeStop' ) this . resetData ( ) ;
166158 } ;
167159 }
168160
@@ -188,15 +180,15 @@ export default class Resizable extends React.Component<Props, ResizableState> {
188180 className : `${ className ? `${ className } ` : '' } react-resizable` ,
189181 children : [
190182 ...children . props . children ,
191- ...resizeHandles . map ( h => (
183+ ...resizeHandles . map ( ( handleAxis ) => (
192184 < DraggableCore
193185 { ...draggableOpts }
194- key = { `resizableHandle-${ h } ` }
195- onStop = { this . resizeHandler ( 'onResizeStop' , h ) }
196- onStart = { this . resizeHandler ( 'onResizeStart' , h ) }
197- onDrag = { this . resizeHandler ( 'onResize' , h ) }
186+ key = { `resizableHandle-${ handleAxis } ` }
187+ onStop = { this . resizeHandler ( 'onResizeStop' , handleAxis ) }
188+ onStart = { this . resizeHandler ( 'onResizeStart' , handleAxis ) }
189+ onDrag = { this . resizeHandler ( 'onResize' , handleAxis ) }
198190 >
199- { this . renderResizeHandle ( h ) }
191+ { this . renderResizeHandle ( handleAxis ) }
200192 </ DraggableCore >
201193 ) )
202194 ]
0 commit comments