-
Notifications
You must be signed in to change notification settings - Fork 50.2k
[DevTools] Added resize support for Components panel. #18046
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
6f5d8e5
65bbec5
16dcd67
1c6add2
23fba3a
0236754
1f2e54a
9cee4ef
fc02184
0c2ad23
3ba1909
554f7e8
c243f40
7f4dbd9
c773be3
27f5906
267f242
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| .ComponentsWrapper { | ||
| position: relative; | ||
| width: 100%; | ||
| height: 100%; | ||
| display: flex; | ||
| flex-direction: row; | ||
| background-color: var(--color-background); | ||
| color: var(--color-text); | ||
| font-family: var(--font-family-sans); | ||
| } | ||
|
|
||
| @media screen and (max-width: 600px) { | ||
| .ComponentsWrapper { | ||
| flex-direction: column; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,209 @@ | ||
| import type {ElementRef} from 'react'; | ||
|
|
||
| import * as React from 'react'; | ||
| import {useEffect, useLayoutEffect, useReducer, useRef} from 'react'; | ||
| import { | ||
| localStorageGetItem, | ||
| localStorageSetItem, | ||
| } from 'react-devtools-shared/src/storage'; | ||
| import styles from './ComponentsResizer.css'; | ||
|
|
||
| const LOCAL_STORAGE_KEY = 'React::DevTools::createResizeReducer'; | ||
| const VERTICAL_MODE_MAX_WIDTH = 600; | ||
| const MINIMUM_SIZE = 50; | ||
|
|
||
| type Props = {| | ||
| children: ({ | ||
| resizeElementRef: ElementRef<HTMLElement>, | ||
| onResizeStart: () => void, | ||
| }) => React$Node, | ||
| |}; | ||
|
|
||
| export default function ComponentsResizer({children}: Props) { | ||
| const wrapperElementRef = useRef(null); | ||
| const resizeElementRef = useRef(null); | ||
| const [state, dispatch] = createResizeReducer( | ||
| wrapperElementRef, | ||
| resizeElementRef, | ||
| ); | ||
|
|
||
| const {isResizing} = state; | ||
|
|
||
| const onResizeStart = () => | ||
| dispatch({type: 'ACTION_SET_IS_RESIZING', payload: true}); | ||
| const onResizeEnd = () => | ||
| dispatch({type: 'ACTION_SET_IS_RESIZING', payload: false}); | ||
|
|
||
| const onResize = event => { | ||
| const resizeElement = resizeElementRef.current; | ||
| const wrapperElement = wrapperElementRef.current; | ||
|
|
||
| if (!isResizing || wrapperElement === null || resizeElement === null) { | ||
| return; | ||
| } | ||
|
|
||
| event.preventDefault(); | ||
|
|
||
| const orientation = getOrientation(wrapperElementRef); | ||
|
|
||
| const {height, width, left, top} = wrapperElement.getBoundingClientRect(); | ||
|
|
||
| const currentMousePosition = | ||
| orientation === 'horizontal' ? event.clientX - left : event.clientY - top; | ||
|
|
||
| const boundaryMin = MINIMUM_SIZE; | ||
| const boundaryMax = | ||
| orientation === 'horizontal' | ||
| ? width - MINIMUM_SIZE | ||
| : height - MINIMUM_SIZE; | ||
|
|
||
| const isMousePositionInBounds = | ||
| currentMousePosition > boundaryMin && currentMousePosition < boundaryMax; | ||
|
|
||
| if (isMousePositionInBounds) { | ||
| const resizedElementDimension = | ||
| orientation === 'horizontal' ? width : height; | ||
| const actionType = | ||
| orientation === 'horizontal' | ||
| ? 'ACTION_SET_HORIZONTAL_PERCENTAGE' | ||
| : 'ACTION_SET_VERTICAL_PERCENTAGE'; | ||
| const percentage = (currentMousePosition / resizedElementDimension) * 100; | ||
|
|
||
| resizeElement.style.setProperty( | ||
| `--${orientation}-resize-percentage`, | ||
| `${percentage}%`, | ||
| ); | ||
|
||
|
|
||
| dispatch({ | ||
| type: actionType, | ||
| payload: currentMousePosition / resizedElementDimension, | ||
| }); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <div | ||
| ref={wrapperElementRef} | ||
| className={styles.ComponentsWrapper} | ||
| {...(isResizing && { | ||
| onMouseMove: onResize, | ||
| onMouseLeave: onResizeEnd, | ||
| onMouseUp: onResizeEnd, | ||
| })}> | ||
| {children({resizeElementRef, onResizeStart})} | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| type Orientation = 'horizontal' | 'vertical'; | ||
|
|
||
| type ResizeActionType = | ||
| | 'ACTION_SET_DID_MOUNT' | ||
| | 'ACTION_SET_IS_RESIZING' | ||
| | 'ACTION_SET_HORIZONTAL_PERCENTAGE' | ||
| | 'ACTION_SET_VERTICAL_PERCENTAGE'; | ||
|
|
||
| type ResizeAction = {| | ||
| type: ResizeActionType, | ||
| payload: any, | ||
| |}; | ||
|
|
||
| type ResizeState = {| | ||
| horizontalPercentage: number, | ||
| isResizing: boolean, | ||
| verticalPercentage: number, | ||
| |}; | ||
|
|
||
| function initResizeState(): ResizeState { | ||
| let horizontalPercentage = 0.65; | ||
| let verticalPercentage = 0.5; | ||
|
|
||
| try { | ||
| let data = localStorageGetItem(LOCAL_STORAGE_KEY); | ||
| if (data != null) { | ||
| data = JSON.parse(data); | ||
| horizontalPercentage = data.horizontalPercentage; | ||
| verticalPercentage = data.verticalPercentage; | ||
| } | ||
| } catch (error) {} | ||
|
|
||
| return { | ||
| horizontalPercentage, | ||
| isResizing: false, | ||
| verticalPercentage, | ||
| }; | ||
| } | ||
|
|
||
| function resizeReducer(state: ResizeState, action: ResizeAction): ResizeState { | ||
| switch (action.type) { | ||
| case 'ACTION_SET_IS_RESIZING': | ||
| return { | ||
| ...state, | ||
| isResizing: action.payload, | ||
| }; | ||
| case 'ACTION_SET_HORIZONTAL_PERCENTAGE': | ||
| return { | ||
| ...state, | ||
| horizontalPercentage: action.payload, | ||
| }; | ||
| case 'ACTION_SET_VERTICAL_PERCENTAGE': | ||
| return { | ||
| ...state, | ||
| verticalPercentage: action.payload, | ||
| }; | ||
| default: | ||
| return state; | ||
| } | ||
| } | ||
|
|
||
| function getOrientation( | ||
| wrapperElementRef: ElementRef<HTMLElement>, | ||
| ): null | Orientation { | ||
| const wrapperElement = wrapperElementRef.current; | ||
| if (wrapperElement != null) { | ||
| const {width} = wrapperElement.getBoundingClientRect(); | ||
| return width > VERTICAL_MODE_MAX_WIDTH ? 'horizontal' : 'vertical'; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| function createResizeReducer(wrapperElementRef, resizeElementRef) { | ||
| const [state, dispatch] = useReducer(resizeReducer, null, initResizeState); | ||
hristo-kanchev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const {horizontalPercentage, verticalPercentage} = state; | ||
| const orientationRef = useRef(null); | ||
|
|
||
| useLayoutEffect(() => { | ||
| const orientation = getOrientation(wrapperElementRef); | ||
|
|
||
| if (orientation !== orientationRef.current) { | ||
| orientationRef.current = orientation; | ||
|
|
||
| const percentage = | ||
| orientation === 'horizontal' | ||
| ? horizontalPercentage | ||
| : verticalPercentage; | ||
| const resizeElement = resizeElementRef.current; | ||
|
|
||
| resizeElement.style.setProperty( | ||
| `--${orientation}-resize-percentage`, | ||
| `${percentage * 100}%`, | ||
| ); | ||
hristo-kanchev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| }); | ||
|
|
||
| useEffect(() => { | ||
| const timeoutID = setTimeout(() => { | ||
| localStorageSetItem( | ||
| LOCAL_STORAGE_KEY, | ||
| JSON.stringify({ | ||
| horizontalPercentage, | ||
| verticalPercentage, | ||
| }), | ||
| ); | ||
| }, 500); | ||
|
|
||
| return () => clearTimeout(timeoutID); | ||
| }, [horizontalPercentage, verticalPercentage]); | ||
|
|
||
| return [state, dispatch]; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.