Merge branch 'develop' into bug/sketch-name-character-limit
This commit is contained in:
		
						commit
						766a160a5f
					
				
					 12 changed files with 221 additions and 28 deletions
				
			
		|  | @ -12,6 +12,7 @@ import Exit from '../images/exit.svg'; | ||||||
| import DropdownArrow from '../images/down-filled-triangle.svg'; | import DropdownArrow from '../images/down-filled-triangle.svg'; | ||||||
| import Preferences from '../images/preferences.svg'; | import Preferences from '../images/preferences.svg'; | ||||||
| import Play from '../images/triangle-arrow-right.svg'; | import Play from '../images/triangle-arrow-right.svg'; | ||||||
|  | import More from '../images/more.svg'; | ||||||
| import Code from '../images/code.svg'; | import Code from '../images/code.svg'; | ||||||
| import Terminal from '../images/terminal.svg'; | import Terminal from '../images/terminal.svg'; | ||||||
| 
 | 
 | ||||||
|  | @ -77,4 +78,6 @@ export const ExitIcon = withLabel(Exit); | ||||||
| export const DropdownArrowIcon = withLabel(DropdownArrow); | export const DropdownArrowIcon = withLabel(DropdownArrow); | ||||||
| export const PreferencesIcon = withLabel(Preferences); | export const PreferencesIcon = withLabel(Preferences); | ||||||
| export const PlayIcon = withLabel(Play); | export const PlayIcon = withLabel(Play); | ||||||
|  | export const MoreIcon = withLabel(More); | ||||||
| export const TerminalIcon = withLabel(Terminal); | export const TerminalIcon = withLabel(Terminal); | ||||||
|  | export const CodeIcon = withLabel(Code); | ||||||
|  |  | ||||||
							
								
								
									
										92
									
								
								client/components/Dropdown.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								client/components/Dropdown.jsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | ||||||
|  | import React from 'react'; | ||||||
|  | import PropTypes from 'prop-types'; | ||||||
|  | import { Link } from 'react-router'; | ||||||
|  | import styled from 'styled-components'; | ||||||
|  | import { remSize, prop, common } from '../theme'; | ||||||
|  | import IconButton from './mobile/IconButton'; | ||||||
|  | import Button from '../common/Button'; | ||||||
|  | 
 | ||||||
|  | const DropdownWrapper = styled.ul` | ||||||
|  |   background-color: ${prop('Modal.background')}; | ||||||
|  |   border: 1px solid ${prop('Modal.border')}; | ||||||
|  |   box-shadow: 0 0 18px 0 ${prop('shadowColor')}; | ||||||
|  |   color: ${prop('primaryTextColor')}; | ||||||
|  | 
 | ||||||
|  |   position: absolute; | ||||||
|  |   right: ${props => (props.right ? 0 : 'initial')}; | ||||||
|  |   left: ${props => (props.left ? 0 : 'initial')}; | ||||||
|  | 
 | ||||||
|  |   ${props => (props.align === 'right' && 'right: 0;')} | ||||||
|  |   ${props => (props.align === 'left' && 'left: 0;')} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   text-align: left; | ||||||
|  |   width: ${remSize(180)}; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   height: auto; | ||||||
|  |   z-index: 9999; | ||||||
|  |   border-radius: ${remSize(6)}; | ||||||
|  | 
 | ||||||
|  |   & li:first-child { border-radius: ${remSize(5)} ${remSize(5)} 0 0; } | ||||||
|  |   & li:last-child  { border-radius: 0 0 ${remSize(5)} ${remSize(5)}; } | ||||||
|  | 
 | ||||||
|  |   & li:hover { | ||||||
|  |      | ||||||
|  |     background-color: ${prop('Button.hover.background')}; | ||||||
|  |     color: ${prop('Button.hover.foreground')}; | ||||||
|  | 
 | ||||||
|  |     & button, & a { | ||||||
|  |       color: ${prop('Button.hover.foreground')}; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   li { | ||||||
|  |     height: ${remSize(36)}; | ||||||
|  |     cursor: pointer; | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  | 
 | ||||||
|  |     & button, | ||||||
|  |     & a { | ||||||
|  |       color: ${prop('primaryTextColor')}; | ||||||
|  |       width: 100%; | ||||||
|  |       text-align: left; | ||||||
|  |       padding: ${remSize(8)} ${remSize(16)}; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | `; | ||||||
|  | 
 | ||||||
|  | // TODO: Add Icon to the left of the items in the menu | ||||||
|  | // const MaybeIcon = (Element, label) => Element && <Element aria-label={label} />; | ||||||
|  | 
 | ||||||
|  | const Dropdown = ({ items, align }) => ( | ||||||
|  |   <DropdownWrapper align={align} > | ||||||
|  |     {/* className="nav__items-left" */} | ||||||
|  |     {items && items.map(({ title, icon, href }) => ( | ||||||
|  |       <li key={`nav-${title && title.toLowerCase()}`}> | ||||||
|  |         <Link to={href}> | ||||||
|  |           {/* {MaybeIcon(icon, `Navigate to ${title}`)} */} | ||||||
|  |           {title} | ||||||
|  |         </Link> | ||||||
|  |       </li> | ||||||
|  |     )) | ||||||
|  |     } | ||||||
|  |   </DropdownWrapper> | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | Dropdown.propTypes = { | ||||||
|  |   align: PropTypes.oneOf(['left', 'right']), | ||||||
|  |   items: PropTypes.arrayOf(PropTypes.shape({ | ||||||
|  |     action: PropTypes.func, | ||||||
|  |     icon: PropTypes.func, | ||||||
|  |     href: PropTypes.string | ||||||
|  |   })), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Dropdown.defaultProps = { | ||||||
|  |   items: [], | ||||||
|  |   align: null | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default Dropdown; | ||||||
							
								
								
									
										27
									
								
								client/components/OverlayManager.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								client/components/OverlayManager.jsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | import React from 'react'; | ||||||
|  | import PropTypes from 'prop-types'; | ||||||
|  | import { createPortal } from 'react-dom'; | ||||||
|  | 
 | ||||||
|  | const OverlayManager = ({ overlay, hideOverlay }) => { | ||||||
|  |   // const [visible, trigger, setRef] = useModalBehavior(); | ||||||
|  | 
 | ||||||
|  |   const jsx = ( | ||||||
|  |     <React.Fragment> | ||||||
|  |       {/* <div ref={setRef} > | ||||||
|  |         {visible && <Dropdown items={headerNavOptions} />} | ||||||
|  |       </div> */} | ||||||
|  |     </React.Fragment> | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   return jsx && createPortal(jsx, document.body); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | OverlayManager.propTypes = { | ||||||
|  |   overlay: PropTypes.string, | ||||||
|  |   hideOverlay: PropTypes.func.isRequired, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | OverlayManager.defaultProps = { overlay: null }; | ||||||
|  | 
 | ||||||
|  | export default OverlayManager; | ||||||
|  | @ -31,7 +31,9 @@ const HeaderDiv = styled.div` | ||||||
| 
 | 
 | ||||||
| const IconContainer = styled.div` | const IconContainer = styled.div` | ||||||
|   margin-left: ${props => (props.leftButton ? remSize(32) : remSize(4))}; |   margin-left: ${props => (props.leftButton ? remSize(32) : remSize(4))}; | ||||||
|  |   list-style: none; | ||||||
|   display: flex; |   display: flex; | ||||||
|  |   flex-direction: horizontal; | ||||||
| `; | `; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								client/components/useAsModal.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								client/components/useAsModal.jsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | import React from 'react'; | ||||||
|  | import { useModalBehavior } from '../utils/custom-hooks'; | ||||||
|  | 
 | ||||||
|  | export default (component) => { | ||||||
|  |   const [visible, trigger, setRef] = useModalBehavior(); | ||||||
|  | 
 | ||||||
|  |   const wrapper = () => <div ref={setRef}> {visible && component} </div>; // eslint-disable-line | ||||||
|  | 
 | ||||||
|  |   return [trigger, wrapper]; | ||||||
|  | }; | ||||||
							
								
								
									
										5
									
								
								client/images/more.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								client/images/more.svg
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  | <path d="M9.28004 13.76C10.5207 13.76 11.52 14.7324 11.52 16.0174C11.52 17.2676 10.5207 18.24 9.28004 18.24C8.03942 18.24 7.04004 17.2676 7.04004 16.0174C7.04004 14.7324 8.03942 13.76 9.28004 13.76Z" fill="#333333"/> | ||||||
|  | <path d="M18.24 16C18.24 14.7629 17.2371 13.76 16 13.76C14.7629 13.76 13.76 14.7629 13.76 16C13.76 17.2371 14.7629 18.24 16 18.24C17.2371 18.24 18.24 17.2371 18.24 16Z" fill="#333333"/> | ||||||
|  | <path d="M22.72 13.76C23.9606 13.76 24.96 14.705 24.96 15.965C24.96 17.295 23.9606 18.24 22.72 18.24C21.4794 18.24 20.48 17.295 20.48 15.965C20.48 14.74 21.4794 13.76 22.72 13.76Z" fill="#333333"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 704 B | 
|  | @ -34,7 +34,8 @@ class App extends React.Component { | ||||||
|   render() { |   render() { | ||||||
|     return ( |     return ( | ||||||
|       <div className="app"> |       <div className="app"> | ||||||
|         {this.state.isMounted && !window.devToolsExtension && getConfig('NODE_ENV') === 'development' && <DevTools />} |         {/* FIXME: Remove false */} | ||||||
|  |         {false && this.state.isMounted && !window.devToolsExtension && getConfig('NODE_ENV') === 'development' && <DevTools />} | ||||||
|         {this.props.children} |         {this.props.children} | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  | @ -88,8 +88,6 @@ const Console = () => { | ||||||
| 
 | 
 | ||||||
|   const cm = useRef({}); |   const cm = useRef({}); | ||||||
| 
 | 
 | ||||||
|   useDidUpdate(() => { cm.current.scrollTop = cm.current.scrollHeight; }); |  | ||||||
| 
 |  | ||||||
|   const consoleClass = classNames({ |   const consoleClass = classNames({ | ||||||
|     'preview-console': true, |     'preview-console': true, | ||||||
|     'preview-console--collapsed': !isExpanded |     'preview-console--collapsed': !isExpanded | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import styled from 'styled-components'; | ||||||
| 
 | 
 | ||||||
| // Imports to be Refactored | // Imports to be Refactored | ||||||
| import { bindActionCreators } from 'redux'; | import { bindActionCreators } from 'redux'; | ||||||
|  | 
 | ||||||
| import * as FileActions from '../actions/files'; | import * as FileActions from '../actions/files'; | ||||||
| import * as IDEActions from '../actions/ide'; | import * as IDEActions from '../actions/ide'; | ||||||
| import * as ProjectActions from '../actions/project'; | import * as ProjectActions from '../actions/project'; | ||||||
|  | @ -19,7 +20,7 @@ import { getHTMLFile } from '../reducers/files'; | ||||||
| 
 | 
 | ||||||
| // Local Imports | // Local Imports | ||||||
| import Editor from '../components/Editor'; | import Editor from '../components/Editor'; | ||||||
| import { PreferencesIcon, PlayIcon, ExitIcon } from '../../../common/icons'; | import { PlayIcon, ExitIcon, MoreIcon } from '../../../common/icons'; | ||||||
| 
 | 
 | ||||||
| import IconButton from '../../../components/mobile/IconButton'; | import IconButton from '../../../components/mobile/IconButton'; | ||||||
| import Header from '../../../components/mobile/Header'; | import Header from '../../../components/mobile/Header'; | ||||||
|  | @ -28,7 +29,11 @@ import Footer from '../../../components/mobile/Footer'; | ||||||
| import IDEWrapper from '../../../components/mobile/IDEWrapper'; | import IDEWrapper from '../../../components/mobile/IDEWrapper'; | ||||||
| import Console from '../components/Console'; | import Console from '../components/Console'; | ||||||
| import { remSize } from '../../../theme'; | import { remSize } from '../../../theme'; | ||||||
|  | // import OverlayManager from '../../../components/OverlayManager'; | ||||||
| import ActionStrip from '../../../components/mobile/ActionStrip'; | import ActionStrip from '../../../components/mobile/ActionStrip'; | ||||||
|  | import useAsModal from '../../../components/useAsModal'; | ||||||
|  | import { PreferencesIcon } from '../../../common/icons'; | ||||||
|  | import Dropdown from '../../../components/Dropdown'; | ||||||
| 
 | 
 | ||||||
| const isUserOwner = ({ project, user }) => (project.owner && project.owner.id === user.id); | const isUserOwner = ({ project, user }) => (project.owner && project.owner.id === user.id); | ||||||
| 
 | 
 | ||||||
|  | @ -37,17 +42,30 @@ const Expander = styled.div` | ||||||
|   height: ${props => (props.expanded ? remSize(160) : remSize(27))}; |   height: ${props => (props.expanded ? remSize(160) : remSize(27))}; | ||||||
| `; | `; | ||||||
| 
 | 
 | ||||||
|  | const NavItem = styled.li` | ||||||
|  |   position: relative; | ||||||
|  | `; | ||||||
|  | 
 | ||||||
|  | const headerNavOptions = [ | ||||||
|  |   { icon: PreferencesIcon, title: 'Preferences', href: '/mobile/preferences', }, | ||||||
|  |   { icon: PreferencesIcon, title: 'Examples', href: '/mobile/examples' }, | ||||||
|  |   { icon: PreferencesIcon, title: 'Original Editor', href: '/', }, | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| const MobileIDEView = (props) => { | const MobileIDEView = (props) => { | ||||||
|   const { |   const { | ||||||
|     preferences, ide, editorAccessibility, project, updateLintMessage, clearLintMessage, |     preferences, ide, editorAccessibility, project, updateLintMessage, clearLintMessage, | ||||||
|     selectedFile, updateFileContent, files, |     selectedFile, updateFileContent, files, | ||||||
|     closeEditorOptions, showEditorOptions, showKeyboardShortcutModal, setUnsavedChanges, |     closeEditorOptions, showEditorOptions, | ||||||
|     startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console, |     startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console, | ||||||
|     showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch |     showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch | ||||||
|   } = props; |   } = props; | ||||||
| 
 | 
 | ||||||
|   const [tmController, setTmController] = useState(null); // eslint-disable-line |   const [tmController, setTmController] = useState(null); // eslint-disable-line | ||||||
|   const [overlay, setOverlay] = useState(null); // eslint-disable-line | 
 | ||||||
|  | 
 | ||||||
|  |   const [triggerNavDropdown, NavDropDown] = useAsModal(<Dropdown align="right" items={headerNavOptions} />); | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <Screen fullscreen> |     <Screen fullscreen> | ||||||
|  | @ -58,13 +76,17 @@ const MobileIDEView = (props) => { | ||||||
|           <IconButton to="/mobile" icon={ExitIcon} aria-label="Return to original editor" /> |           <IconButton to="/mobile" icon={ExitIcon} aria-label="Return to original editor" /> | ||||||
|         } |         } | ||||||
|       > |       > | ||||||
|  |         <NavItem> | ||||||
|           <IconButton |           <IconButton | ||||||
|           to="/mobile/preferences" |             onClick={triggerNavDropdown} | ||||||
|           onClick={() => setOverlay('preferences')} |             icon={MoreIcon} | ||||||
|           icon={PreferencesIcon} |             aria-label="Options" | ||||||
|           aria-label="Open preferences menu" |  | ||||||
|           /> |           /> | ||||||
|  |           <NavDropDown /> | ||||||
|  |         </NavItem> | ||||||
|  |         <li> | ||||||
|           <IconButton to="/mobile/preview" onClick={() => { startSketch(); }} icon={PlayIcon} aria-label="Run sketch" /> |           <IconButton to="/mobile/preview" onClick={() => { startSketch(); }} icon={PlayIcon} aria-label="Run sketch" /> | ||||||
|  |         </li> | ||||||
|       </Header> |       </Header> | ||||||
| 
 | 
 | ||||||
|       <IDEWrapper> |       <IDEWrapper> | ||||||
|  | @ -82,9 +104,7 @@ const MobileIDEView = (props) => { | ||||||
|           editorOptionsVisible={ide.editorOptionsVisible} |           editorOptionsVisible={ide.editorOptionsVisible} | ||||||
|           showEditorOptions={showEditorOptions} |           showEditorOptions={showEditorOptions} | ||||||
|           closeEditorOptions={closeEditorOptions} |           closeEditorOptions={closeEditorOptions} | ||||||
|           showKeyboardShortcutModal={showKeyboardShortcutModal} |           showKeyboard={ide.isPlaying} | ||||||
|           setUnsavedChanges={setUnsavedChanges} |  | ||||||
|           isPlaying={ide.isPlaying} |  | ||||||
|           theme={preferences.theme} |           theme={preferences.theme} | ||||||
|           startRefreshSketch={startRefreshSketch} |           startRefreshSketch={startRefreshSketch} | ||||||
|           stopSketch={stopSketch} |           stopSketch={stopSketch} | ||||||
|  | @ -103,6 +123,7 @@ const MobileIDEView = (props) => { | ||||||
|           provideController={setTmController} |           provideController={setTmController} | ||||||
|         /> |         /> | ||||||
|       </IDEWrapper> |       </IDEWrapper> | ||||||
|  | 
 | ||||||
|       <Footer> |       <Footer> | ||||||
|         {ide.consoleIsExpanded && <Expander expanded><Console /></Expander>} |         {ide.consoleIsExpanded && <Expander expanded><Console /></Expander>} | ||||||
|         <ActionStrip /> |         <ActionStrip /> | ||||||
|  | @ -111,7 +132,6 @@ const MobileIDEView = (props) => { | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| MobileIDEView.propTypes = { | MobileIDEView.propTypes = { | ||||||
| 
 | 
 | ||||||
|   preferences: PropTypes.shape({ |   preferences: PropTypes.shape({ | ||||||
|  | @ -130,7 +150,7 @@ MobileIDEView.propTypes = { | ||||||
|   ide: PropTypes.shape({ |   ide: PropTypes.shape({ | ||||||
|     isPlaying: PropTypes.bool.isRequired, |     isPlaying: PropTypes.bool.isRequired, | ||||||
|     isAccessibleOutputPlaying: PropTypes.bool.isRequired, |     isAccessibleOutputPlaying: PropTypes.bool.isRequired, | ||||||
|     consoleEvent: PropTypes.array, |     consoleEvent: PropTypes.arrayOf(PropTypes.shape({})), | ||||||
|     modalIsVisible: PropTypes.bool.isRequired, |     modalIsVisible: PropTypes.bool.isRequired, | ||||||
|     sidebarIsExpanded: PropTypes.bool.isRequired, |     sidebarIsExpanded: PropTypes.bool.isRequired, | ||||||
|     consoleIsExpanded: PropTypes.bool.isRequired, |     consoleIsExpanded: PropTypes.bool.isRequired, | ||||||
|  | @ -156,7 +176,7 @@ MobileIDEView.propTypes = { | ||||||
|   }).isRequired, |   }).isRequired, | ||||||
| 
 | 
 | ||||||
|   editorAccessibility: PropTypes.shape({ |   editorAccessibility: PropTypes.shape({ | ||||||
|     lintMessages: PropTypes.array.isRequired, |     lintMessages: PropTypes.arrayOf(PropTypes.shape({})).isRequired, | ||||||
|   }).isRequired, |   }).isRequired, | ||||||
| 
 | 
 | ||||||
|   project: PropTypes.shape({ |   project: PropTypes.shape({ | ||||||
|  | @ -193,10 +213,6 @@ MobileIDEView.propTypes = { | ||||||
| 
 | 
 | ||||||
|   showEditorOptions: PropTypes.func.isRequired, |   showEditorOptions: PropTypes.func.isRequired, | ||||||
| 
 | 
 | ||||||
|   showKeyboardShortcutModal: PropTypes.func.isRequired, |  | ||||||
| 
 |  | ||||||
|   setUnsavedChanges: PropTypes.func.isRequired, |  | ||||||
| 
 |  | ||||||
|   startRefreshSketch: PropTypes.func.isRequired, |   startRefreshSketch: PropTypes.func.isRequired, | ||||||
| 
 | 
 | ||||||
|   stopSketch: PropTypes.func.isRequired, |   stopSketch: PropTypes.func.isRequired, | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import { bindActionCreators } from 'redux'; | import { bindActionCreators } from 'redux'; | ||||||
| import { connect, useSelector, useDispatch } from 'react-redux'; | import { useSelector, useDispatch } from 'react-redux'; | ||||||
| import styled from 'styled-components'; | import styled from 'styled-components'; | ||||||
| import Header from '../../components/mobile/Header'; | import Header from '../../components/mobile/Header'; | ||||||
| import IconButton from '../../components/mobile/IconButton'; | import IconButton from '../../components/mobile/IconButton'; | ||||||
|  | @ -25,7 +24,7 @@ const Content = styled.div` | ||||||
|   margin-top: ${remSize(68)}; |   margin-top: ${remSize(68)}; | ||||||
| `; | `; | ||||||
| 
 | 
 | ||||||
| const MobileSketchView = (props) => { | const MobileSketchView = () => { | ||||||
|   const { files, ide, preferences } = useSelector(state => state); |   const { files, ide, preferences } = useSelector(state => state); | ||||||
| 
 | 
 | ||||||
|   const htmlFile = useSelector(state => getHTMLFile(state.files)); |   const htmlFile = useSelector(state => getHTMLFile(state.files)); | ||||||
|  |  | ||||||
|  | @ -41,7 +41,8 @@ export const grays = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const common = { | export const common = { | ||||||
|   baseFontSize: 12 |   baseFontSize: 12, | ||||||
|  |   shadowColor: 'rgba(0, 0, 0, 0.16)' | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const remSize = size => `${size / common.baseFontSize}rem`; | export const remSize = size => `${size / common.baseFontSize}rem`; | ||||||
|  | @ -97,6 +98,10 @@ export default { | ||||||
|         border: grays.middleLight, |         border: grays.middleLight, | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|  |     Modal: { | ||||||
|  |       background: grays.light, | ||||||
|  |       border: grays.middleLight | ||||||
|  |     }, | ||||||
|     Separator: grays.middleLight, |     Separator: grays.middleLight, | ||||||
|   }, |   }, | ||||||
|   [Theme.dark]: { |   [Theme.dark]: { | ||||||
|  | @ -138,6 +143,10 @@ export default { | ||||||
|         border: grays.middleDark, |         border: grays.middleDark, | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|  |     Modal: { | ||||||
|  |       background: grays.dark, | ||||||
|  |       border: grays.middleDark | ||||||
|  |     }, | ||||||
|     Separator: grays.middleDark, |     Separator: grays.middleDark, | ||||||
|   }, |   }, | ||||||
|   [Theme.contrast]: { |   [Theme.contrast]: { | ||||||
|  | @ -179,6 +188,10 @@ export default { | ||||||
|         border: grays.middleDark, |         border: grays.middleDark, | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|  |     Modal: { | ||||||
|  |       background: grays.dark, | ||||||
|  |       border: grays.middleDark | ||||||
|  |     }, | ||||||
|     Separator: grays.middleDark, |     Separator: grays.middleDark, | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import React, { useEffect, useRef } from 'react'; | import React, { useEffect, useRef, useState } from 'react'; | ||||||
| 
 | 
 | ||||||
| export const noop = () => {}; | export const noop = () => {}; | ||||||
| 
 | 
 | ||||||
|  | @ -13,3 +13,30 @@ export const useDidUpdate = (callback, deps) => { | ||||||
|     } |     } | ||||||
|   }, deps); |   }, deps); | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | // Usage: const ref = useModalBehavior(() => setSomeState(false))
 | ||||||
|  | // place this ref on a component
 | ||||||
|  | export const useModalBehavior = (hideOverlay) => { | ||||||
|  |   const ref = useRef({}); | ||||||
|  | 
 | ||||||
|  |   // Return values
 | ||||||
|  |   const setRef = (r) => { ref.current = r; }; | ||||||
|  |   const [visible, setVisible] = useState(false); | ||||||
|  |   const trigger = () => setVisible(!visible); | ||||||
|  | 
 | ||||||
|  |   const hide = () => setVisible(false); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   const handleClickOutside = ({ target }) => { | ||||||
|  |     if (ref && ref.current && !ref.current.contains(target)) { | ||||||
|  |       hide(); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     document.addEventListener('mousedown', handleClickOutside); | ||||||
|  |     return () => document.removeEventListener('mousedown', handleClickOutside); | ||||||
|  |   }, [ref]); | ||||||
|  | 
 | ||||||
|  |   return [visible, trigger, setRef]; | ||||||
|  | }; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Cassie Tarakajian
						Cassie Tarakajian