You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			141 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			TypeScript
		
	
		
		
			
		
	
	
			141 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			TypeScript
		
	
| 
											4 years ago
										 | import React, { useEffect, useRef, useState } from 'react'; | ||
|  | import styled from 'styled-components'; | ||
|  | 
 | ||
|  | type SplitViewProps = { | ||
|  |   top: React.ReactElement; | ||
|  |   bottom: React.ReactElement; | ||
|  |   disableTop: boolean; | ||
|  | }; | ||
|  | 
 | ||
|  | const SlyledSplitView = styled.div`
 | ||
|  |   height: 100%; | ||
|  |   display: flex; | ||
|  |   flex-direction: column; | ||
|  | `;
 | ||
|  | 
 | ||
| 
											4 years ago
										 | const Divider = styled.div`
 | ||
|  |   width: 100%; | ||
| 
											4 years ago
										 |   cursor: row-resize; | ||
| 
											4 years ago
										 |   height: 5px; | ||
|  |   background-color: var(--color-cell-background); | ||
|  |   margin-top: 2px; | ||
|  | `;
 | ||
|  | 
 | ||
|  | const DividerHandle = styled.div`
 | ||
|  |   width: 10%; | ||
|  |   height: 5px; | ||
|  |   cursor: row-resize; | ||
|  |   background-color: var(--color-text); | ||
| 
											4 years ago
										 |   flex-shrink: 0; | ||
| 
											4 years ago
										 |   position: relative; | ||
|  |   left: 50%; | ||
|  |   transform: translateX(-50%); | ||
| 
											4 years ago
										 | `;
 | ||
|  | 
 | ||
|  | const StyledTop = styled.div`
 | ||
|  |   display: flex; | ||
|  |   flex-direction: column; | ||
| 
											4 years ago
										 |   flex-grow: 1; | ||
| 
											4 years ago
										 | `;
 | ||
|  | 
 | ||
|  | const TopSplitViewPanel = ({ | ||
|  |   children, | ||
|  |   topHeight, | ||
|  |   setTopHeight, | ||
|  | }: { | ||
|  |   children: React.ReactNode; | ||
|  |   topHeight: number | undefined; | ||
|  |   setTopHeight: (value: number) => void; | ||
|  | }) => { | ||
|  |   const topRef = useRef<HTMLDivElement>(null); | ||
|  |   React.useEffect(() => { | ||
|  |     if (topRef.current) { | ||
|  |       if (!topHeight) { | ||
|  |         setTopHeight(Math.max(MIN_HEIGHT_TOP, topRef.current?.clientHeight / 2)); | ||
|  |         return; | ||
|  |       } | ||
|  | 
 | ||
|  |       topRef.current.style.height = `${topHeight}px`; | ||
| 
											4 years ago
										 |       topRef.current.style.minHeight = `${topHeight}px`; | ||
| 
											4 years ago
										 |     } | ||
|  |   }, [topRef, topHeight, setTopHeight]); | ||
|  | 
 | ||
|  |   return <StyledTop ref={topRef}>{children}</StyledTop>; | ||
|  | }; | ||
|  | 
 | ||
| 
											4 years ago
										 | const MIN_HEIGHT_TOP = 200; | ||
| 
											4 years ago
										 | const MIN_HEIGHT_BOTTOM = 0; | ||
|  | 
 | ||
|  | export const SplitViewContainer: React.FunctionComponent<SplitViewProps> = ({ | ||
|  |   disableTop, | ||
|  |   top, | ||
|  |   bottom, | ||
|  | }) => { | ||
|  |   const [topHeight, setTopHeight] = useState<undefined | number>(undefined); | ||
|  |   const [separatorYPosition, setSeparatorYPosition] = useState<undefined | number>(undefined); | ||
|  |   const [dragging, setDragging] = useState(false); | ||
|  | 
 | ||
|  |   const splitPaneRef = useRef<HTMLDivElement | null>(null); | ||
| 
											4 years ago
										 |   const dividerRef = useRef<HTMLDivElement | null>(null); | ||
| 
											4 years ago
										 | 
 | ||
| 
											4 years ago
										 |   function onMouseDown(e: any) { | ||
| 
											4 years ago
										 |     setSeparatorYPosition(e.clientY); | ||
|  |     setDragging(true); | ||
| 
											4 years ago
										 |   } | ||
| 
											4 years ago
										 | 
 | ||
| 
											4 years ago
										 |   function onWindowResize() { | ||
|  |     if ((dividerRef?.current?.offsetTop || 0) + 200 > window.innerHeight) { | ||
|  |       const clientY = Math.max(MIN_HEIGHT_TOP + 200, window.innerHeight / 2); | ||
|  |       onMouseMove({ clientY }, true); | ||
|  |     } | ||
|  |   } | ||
| 
											4 years ago
										 | 
 | ||
| 
											4 years ago
										 |   function onMouseUp() { | ||
|  |     setDragging(false); | ||
|  |   } | ||
|  |   function onMouseMove(e: { clientY: number }, overrideIsDragging = false) { | ||
|  |     if ((dragging || overrideIsDragging) && topHeight && separatorYPosition) { | ||
|  |       const newTopHeight = topHeight + e.clientY - separatorYPosition; | ||
| 
											4 years ago
										 |       setSeparatorYPosition(e.clientY); | ||
|  |       if (newTopHeight < MIN_HEIGHT_TOP) { | ||
|  |         setTopHeight(MIN_HEIGHT_TOP); | ||
|  |         return; | ||
|  |       } | ||
|  |       if (splitPaneRef.current) { | ||
|  |         const splitPaneHeight = splitPaneRef.current.clientHeight; | ||
|  | 
 | ||
|  |         if (newTopHeight > splitPaneHeight - MIN_HEIGHT_BOTTOM) { | ||
|  |           setTopHeight(splitPaneHeight - MIN_HEIGHT_BOTTOM); | ||
|  |           return; | ||
|  |         } | ||
|  |       } | ||
|  |       setTopHeight(newTopHeight); | ||
|  |     } | ||
| 
											4 years ago
										 |   } | ||
| 
											4 years ago
										 |   useEffect(() => { | ||
|  |     document.addEventListener('mousemove', onMouseMove); | ||
|  |     document.addEventListener('mouseup', onMouseUp); | ||
| 
											4 years ago
										 |     window.addEventListener('resize', onWindowResize); | ||
| 
											4 years ago
										 | 
 | ||
|  |     return () => { | ||
|  |       document.removeEventListener('mousemove', onMouseMove); | ||
|  |       document.removeEventListener('mouseup', onMouseUp); | ||
| 
											4 years ago
										 |       window.removeEventListener('resize', onWindowResize); | ||
| 
											4 years ago
										 |     }; | ||
|  |   }); | ||
|  | 
 | ||
|  |   return ( | ||
|  |     <SlyledSplitView ref={splitPaneRef}> | ||
|  |       {!disableTop && ( | ||
|  |         <TopSplitViewPanel topHeight={topHeight} setTopHeight={setTopHeight}> | ||
|  |           {top} | ||
| 
											4 years ago
										 |           <Divider ref={dividerRef} onMouseDown={onMouseDown}> | ||
|  |             <DividerHandle /> | ||
|  |           </Divider> | ||
| 
											4 years ago
										 |         </TopSplitViewPanel> | ||
|  |       )} | ||
|  |       {bottom} | ||
|  |     </SlyledSplitView> | ||
|  |   ); | ||
|  | }; |