88 */
99
1010import * 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' ;
1219import Tree from './Tree' ;
13- import SelectedElement from './SelectedElement' ;
1420import { InspectedElementContextController } from './InspectedElementContext' ;
15- import { NativeStyleContextController } from './NativeStyleEditor/context' ;
1621import { OwnersListContextController } from './OwnersListContext' ;
17- import ComponentsResizer from './ComponentsResizer' ;
1822import 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' ;
1929import { ModalDialog } from '../ModalDialog' ;
2030import 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 ' ;
2232
2333import styles from './Components.css' ;
2434
2535function 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+
26132 return (
27133 < SettingsModalContextController >
28134 < OwnersListContextController >
29135 < 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 >
54160 </ InspectedElementContextController >
55161 </ OwnersListContextController >
56162 </ SettingsModalContextController >
@@ -61,4 +167,92 @@ function Loading() {
61167 return < div className = { styles . Loading } > Loading...</ div > ;
62168}
63169
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+
64258export default portaledContent ( Components ) ;
0 commit comments