8
8
*/
9
9
10
10
import * as React from 'react' ;
11
- import { Suspense , Fragment } from 'react' ;
11
+ import {
12
+ Fragment ,
13
+ Suspense ,
14
+ useEffect ,
15
+ useLayoutEffect ,
16
+ useReducer ,
17
+ useRef ,
18
+ } from 'react' ;
12
19
import Tree from './Tree' ;
13
- import SelectedElement from './SelectedElement' ;
14
20
import { InspectedElementContextController } from './InspectedElementContext' ;
15
- import { NativeStyleContextController } from './NativeStyleEditor/context' ;
16
21
import { OwnersListContextController } from './OwnersListContext' ;
17
- import ComponentsResizer from './ComponentsResizer' ;
18
22
import portaledContent from '../portaledContent' ;
23
+ import { SettingsModalContextController } from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContext' ;
24
+ import {
25
+ localStorageGetItem ,
26
+ localStorageSetItem ,
27
+ } from 'react-devtools-shared/src/storage' ;
28
+ import SelectedElement from './SelectedElement' ;
19
29
import { ModalDialog } from '../ModalDialog' ;
20
30
import SettingsModal from 'react-devtools-shared/src/devtools/views/Settings/SettingsModal' ;
21
- import { SettingsModalContextController } from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContext ' ;
31
+ import { NativeStyleContextController } from './NativeStyleEditor/context ' ;
22
32
23
33
import styles from './Components.css' ;
24
34
25
35
function Components ( _ : { || } ) {
36
+ const wrapperElementRef = useRef < HTMLElement > ( null ) ;
37
+ const resizeElementRef = useRef < HTMLElement > ( null ) ;
38
+
39
+ const [ state , dispatch ] = useReducer < ResizeState , ResizeAction > (
40
+ resizeReducer ,
41
+ null ,
42
+ initResizeState ,
43
+ ) ;
44
+
45
+ const { horizontalPercentage, verticalPercentage} = state ;
46
+
47
+ useLayoutEffect ( ( ) => {
48
+ const resizeElement = resizeElementRef . current ;
49
+
50
+ setResizeCSSVariable (
51
+ resizeElement ,
52
+ 'horizontal' ,
53
+ horizontalPercentage * 100 ,
54
+ ) ;
55
+ setResizeCSSVariable ( resizeElement , 'vertical' , verticalPercentage * 100 ) ;
56
+ } , [ ] ) ;
57
+
58
+ useEffect ( ( ) => {
59
+ const timeoutID = setTimeout ( ( ) => {
60
+ localStorageSetItem (
61
+ LOCAL_STORAGE_KEY ,
62
+ JSON . stringify ( {
63
+ horizontalPercentage,
64
+ verticalPercentage,
65
+ } ) ,
66
+ ) ;
67
+ } , 500 ) ;
68
+
69
+ return ( ) => clearTimeout ( timeoutID ) ;
70
+ } , [ horizontalPercentage , verticalPercentage ] ) ;
71
+
72
+ const { isResizing} = state ;
73
+
74
+ const onResizeStart = ( ) =>
75
+ dispatch ( { type : 'ACTION_SET_IS_RESIZING' , payload : true } ) ;
76
+
77
+ let onResize ;
78
+ let onResizeEnd ;
79
+ if ( isResizing ) {
80
+ onResizeEnd = ( ) =>
81
+ dispatch ( { type : 'ACTION_SET_IS_RESIZING' , payload : false } ) ;
82
+
83
+ onResize = event => {
84
+ const resizeElement = resizeElementRef . current ;
85
+ const wrapperElement = wrapperElementRef . current ;
86
+
87
+ if ( ! isResizing || wrapperElement === null || resizeElement === null ) {
88
+ return ;
89
+ }
90
+
91
+ event . preventDefault ( ) ;
92
+
93
+ const orientation = getOrientation ( wrapperElement ) ;
94
+
95
+ const { height, width, left, top} = wrapperElement . getBoundingClientRect ( ) ;
96
+
97
+ const currentMousePosition =
98
+ orientation === 'horizontal'
99
+ ? event . clientX - left
100
+ : event . clientY - top ;
101
+
102
+ const boundaryMin = MINIMUM_SIZE ;
103
+ const boundaryMax =
104
+ orientation === 'horizontal'
105
+ ? width - MINIMUM_SIZE
106
+ : height - MINIMUM_SIZE ;
107
+
108
+ const isMousePositionInBounds =
109
+ currentMousePosition > boundaryMin &&
110
+ currentMousePosition < boundaryMax ;
111
+
112
+ if ( isMousePositionInBounds ) {
113
+ const resizedElementDimension =
114
+ orientation === 'horizontal' ? width : height ;
115
+ const actionType =
116
+ orientation === 'horizontal'
117
+ ? 'ACTION_SET_HORIZONTAL_PERCENTAGE'
118
+ : 'ACTION_SET_VERTICAL_PERCENTAGE' ;
119
+ const percentage =
120
+ ( currentMousePosition / resizedElementDimension ) * 100 ;
121
+
122
+ setResizeCSSVariable ( resizeElement , orientation , percentage ) ;
123
+
124
+ dispatch ( {
125
+ type : actionType ,
126
+ payload : currentMousePosition / resizedElementDimension ,
127
+ } ) ;
128
+ }
129
+ } ;
130
+ }
131
+
26
132
return (
27
133
< SettingsModalContextController >
28
134
< OwnersListContextController >
29
135
< InspectedElementContextController >
30
- < ComponentsResizer >
31
- { ( { resizeElementRef , onResizeStart } ) => (
32
- < Fragment >
33
- < div ref = { resizeElementRef } className = { styles . TreeWrapper } >
34
- < Tree />
35
- </ div >
36
- < div className = { styles . ResizeBarWrapper } >
37
- < div
38
- onMouseDown = { onResizeStart }
39
- className = { styles . ResizeBar }
40
- / >
41
- </ div >
42
- < div className = { styles . SelectedElementWrapper } >
43
- < NativeStyleContextController >
44
- < Suspense fallback = { < Loading /> } >
45
- < SelectedElement / >
46
- </ Suspense >
47
- </ NativeStyleContextController >
48
- </ div >
49
- < ModalDialog / >
50
- < SettingsModal />
51
- </ Fragment >
52
- ) }
53
- </ ComponentsResizer >
136
+ < div
137
+ ref = { wrapperElementRef }
138
+ className = { styles . Components }
139
+ onMouseMove = { onResize }
140
+ onMouseLeave = { onResizeEnd }
141
+ onMouseUp = { onResizeEnd } >
142
+ < Fragment >
143
+ < div ref = { resizeElementRef } className = { styles . TreeWrapper } >
144
+ < Tree />
145
+ </ div >
146
+ < div className = { styles . ResizeBarWrapper } >
147
+ < div onMouseDown = { onResizeStart } className = { styles . ResizeBar } / >
148
+ </ div >
149
+ < div className = { styles . SelectedElementWrapper } >
150
+ < NativeStyleContextController >
151
+ < Suspense fallback = { < Loading /> } >
152
+ < SelectedElement / >
153
+ </ Suspense >
154
+ </ NativeStyleContextController >
155
+ </ div >
156
+ < ModalDialog />
157
+ < SettingsModal / >
158
+ </ Fragment >
159
+ </ div >
54
160
</ InspectedElementContextController >
55
161
</ OwnersListContextController >
56
162
</ SettingsModalContextController >
@@ -61,4 +167,92 @@ function Loading() {
61
167
return < div className = { styles . Loading } > Loading...</ div > ;
62
168
}
63
169
170
+ const LOCAL_STORAGE_KEY = 'React::DevTools::createResizeReducer' ;
171
+ const VERTICAL_MODE_MAX_WIDTH = 600 ;
172
+ const MINIMUM_SIZE = 50 ;
173
+
174
+ type Orientation = 'horizontal' | 'vertical' ;
175
+
176
+ type ResizeActionType =
177
+ | 'ACTION_SET_DID_MOUNT'
178
+ | 'ACTION_SET_IS_RESIZING'
179
+ | 'ACTION_SET_HORIZONTAL_PERCENTAGE'
180
+ | 'ACTION_SET_VERTICAL_PERCENTAGE' ;
181
+
182
+ type ResizeAction = { |
183
+ type : ResizeActionType ,
184
+ payload : any ,
185
+ | } ;
186
+
187
+ type ResizeState = { |
188
+ horizontalPercentage : number ,
189
+ isResizing : boolean ,
190
+ verticalPercentage : number ,
191
+ | } ;
192
+
193
+ function initResizeState ( ) : ResizeState {
194
+ let horizontalPercentage = 0.65 ;
195
+ let verticalPercentage = 0.5 ;
196
+
197
+ try {
198
+ let data = localStorageGetItem ( LOCAL_STORAGE_KEY ) ;
199
+ if ( data != null ) {
200
+ data = JSON . parse ( data ) ;
201
+ horizontalPercentage = data . horizontalPercentage ;
202
+ verticalPercentage = data . verticalPercentage ;
203
+ }
204
+ } catch ( error ) { }
205
+
206
+ return {
207
+ horizontalPercentage,
208
+ isResizing : false ,
209
+ verticalPercentage,
210
+ } ;
211
+ }
212
+
213
+ function resizeReducer ( state : ResizeState , action : ResizeAction ) : ResizeState {
214
+ switch ( action . type ) {
215
+ case 'ACTION_SET_IS_RESIZING' :
216
+ return {
217
+ ...state ,
218
+ isResizing : action . payload ,
219
+ } ;
220
+ case 'ACTION_SET_HORIZONTAL_PERCENTAGE' :
221
+ return {
222
+ ...state ,
223
+ horizontalPercentage : action . payload ,
224
+ } ;
225
+ case 'ACTION_SET_VERTICAL_PERCENTAGE' :
226
+ return {
227
+ ...state ,
228
+ verticalPercentage : action . payload ,
229
+ } ;
230
+ default :
231
+ return state ;
232
+ }
233
+ }
234
+
235
+ function getOrientation (
236
+ wrapperElement : null | HTMLElement ,
237
+ ) : null | Orientation {
238
+ if ( wrapperElement != null ) {
239
+ const { width} = wrapperElement . getBoundingClientRect ( ) ;
240
+ return width > VERTICAL_MODE_MAX_WIDTH ? 'horizontal' : 'vertical' ;
241
+ }
242
+ return null ;
243
+ }
244
+
245
+ function setResizeCSSVariable (
246
+ resizeElement : null | HTMLElement ,
247
+ orientation : null | Orientation ,
248
+ percentage : number ,
249
+ ) : void {
250
+ if ( resizeElement !== null && orientation !== null ) {
251
+ resizeElement . style . setProperty (
252
+ `--${ orientation } -resize-percentage` ,
253
+ `${ percentage } %` ,
254
+ ) ;
255
+ }
256
+ }
257
+
64
258
export default portaledContent ( Components ) ;
0 commit comments