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 Preferences from '../images/preferences.svg'; | ||||
| import Play from '../images/triangle-arrow-right.svg'; | ||||
| import More from '../images/more.svg'; | ||||
| import Code from '../images/code.svg'; | ||||
| import Terminal from '../images/terminal.svg'; | ||||
| 
 | ||||
|  | @ -77,4 +78,6 @@ export const ExitIcon = withLabel(Exit); | |||
| export const DropdownArrowIcon = withLabel(DropdownArrow); | ||||
| export const PreferencesIcon = withLabel(Preferences); | ||||
| export const PlayIcon = withLabel(Play); | ||||
| export const MoreIcon = withLabel(More); | ||||
| 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` | ||||
|   margin-left: ${props => (props.leftButton ? remSize(32) : remSize(4))}; | ||||
|   list-style: none; | ||||
|   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() { | ||||
|     return ( | ||||
|       <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} | ||||
|       </div> | ||||
|     ); | ||||
|  |  | |||
|  | @ -88,8 +88,6 @@ const Console = () => { | |||
| 
 | ||||
|   const cm = useRef({}); | ||||
| 
 | ||||
|   useDidUpdate(() => { cm.current.scrollTop = cm.current.scrollHeight; }); | ||||
| 
 | ||||
|   const consoleClass = classNames({ | ||||
|     'preview-console': true, | ||||
|     'preview-console--collapsed': !isExpanded | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import styled from 'styled-components'; | |||
| 
 | ||||
| // Imports to be Refactored | ||||
| import { bindActionCreators } from 'redux'; | ||||
| 
 | ||||
| import * as FileActions from '../actions/files'; | ||||
| import * as IDEActions from '../actions/ide'; | ||||
| import * as ProjectActions from '../actions/project'; | ||||
|  | @ -19,7 +20,7 @@ import { getHTMLFile } from '../reducers/files'; | |||
| 
 | ||||
| // Local Imports | ||||
| 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 Header from '../../../components/mobile/Header'; | ||||
|  | @ -28,7 +29,11 @@ import Footer from '../../../components/mobile/Footer'; | |||
| import IDEWrapper from '../../../components/mobile/IDEWrapper'; | ||||
| import Console from '../components/Console'; | ||||
| import { remSize } from '../../../theme'; | ||||
| // import OverlayManager from '../../../components/OverlayManager'; | ||||
| 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); | ||||
| 
 | ||||
|  | @ -37,17 +42,30 @@ const Expander = styled.div` | |||
|   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 { | ||||
|     preferences, ide, editorAccessibility, project, updateLintMessage, clearLintMessage, | ||||
|     selectedFile, updateFileContent, files, | ||||
|     closeEditorOptions, showEditorOptions, showKeyboardShortcutModal, setUnsavedChanges, | ||||
|     closeEditorOptions, showEditorOptions, | ||||
|     startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console, | ||||
|     showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch | ||||
|   } = props; | ||||
| 
 | ||||
|   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 ( | ||||
|     <Screen fullscreen> | ||||
|  | @ -58,13 +76,17 @@ const MobileIDEView = (props) => { | |||
|           <IconButton to="/mobile" icon={ExitIcon} aria-label="Return to original editor" /> | ||||
|         } | ||||
|       > | ||||
|         <IconButton | ||||
|           to="/mobile/preferences" | ||||
|           onClick={() => setOverlay('preferences')} | ||||
|           icon={PreferencesIcon} | ||||
|           aria-label="Open preferences menu" | ||||
|         /> | ||||
|         <IconButton to="/mobile/preview" onClick={() => { startSketch(); }} icon={PlayIcon} aria-label="Run sketch" /> | ||||
|         <NavItem> | ||||
|           <IconButton | ||||
|             onClick={triggerNavDropdown} | ||||
|             icon={MoreIcon} | ||||
|             aria-label="Options" | ||||
|           /> | ||||
|           <NavDropDown /> | ||||
|         </NavItem> | ||||
|         <li> | ||||
|           <IconButton to="/mobile/preview" onClick={() => { startSketch(); }} icon={PlayIcon} aria-label="Run sketch" /> | ||||
|         </li> | ||||
|       </Header> | ||||
| 
 | ||||
|       <IDEWrapper> | ||||
|  | @ -82,9 +104,7 @@ const MobileIDEView = (props) => { | |||
|           editorOptionsVisible={ide.editorOptionsVisible} | ||||
|           showEditorOptions={showEditorOptions} | ||||
|           closeEditorOptions={closeEditorOptions} | ||||
|           showKeyboardShortcutModal={showKeyboardShortcutModal} | ||||
|           setUnsavedChanges={setUnsavedChanges} | ||||
|           isPlaying={ide.isPlaying} | ||||
|           showKeyboard={ide.isPlaying} | ||||
|           theme={preferences.theme} | ||||
|           startRefreshSketch={startRefreshSketch} | ||||
|           stopSketch={stopSketch} | ||||
|  | @ -103,6 +123,7 @@ const MobileIDEView = (props) => { | |||
|           provideController={setTmController} | ||||
|         /> | ||||
|       </IDEWrapper> | ||||
| 
 | ||||
|       <Footer> | ||||
|         {ide.consoleIsExpanded && <Expander expanded><Console /></Expander>} | ||||
|         <ActionStrip /> | ||||
|  | @ -111,7 +132,6 @@ const MobileIDEView = (props) => { | |||
|   ); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| MobileIDEView.propTypes = { | ||||
| 
 | ||||
|   preferences: PropTypes.shape({ | ||||
|  | @ -130,7 +150,7 @@ MobileIDEView.propTypes = { | |||
|   ide: PropTypes.shape({ | ||||
|     isPlaying: PropTypes.bool.isRequired, | ||||
|     isAccessibleOutputPlaying: PropTypes.bool.isRequired, | ||||
|     consoleEvent: PropTypes.array, | ||||
|     consoleEvent: PropTypes.arrayOf(PropTypes.shape({})), | ||||
|     modalIsVisible: PropTypes.bool.isRequired, | ||||
|     sidebarIsExpanded: PropTypes.bool.isRequired, | ||||
|     consoleIsExpanded: PropTypes.bool.isRequired, | ||||
|  | @ -156,7 +176,7 @@ MobileIDEView.propTypes = { | |||
|   }).isRequired, | ||||
| 
 | ||||
|   editorAccessibility: PropTypes.shape({ | ||||
|     lintMessages: PropTypes.array.isRequired, | ||||
|     lintMessages: PropTypes.arrayOf(PropTypes.shape({})).isRequired, | ||||
|   }).isRequired, | ||||
| 
 | ||||
|   project: PropTypes.shape({ | ||||
|  | @ -193,10 +213,6 @@ MobileIDEView.propTypes = { | |||
| 
 | ||||
|   showEditorOptions: PropTypes.func.isRequired, | ||||
| 
 | ||||
|   showKeyboardShortcutModal: PropTypes.func.isRequired, | ||||
| 
 | ||||
|   setUnsavedChanges: PropTypes.func.isRequired, | ||||
| 
 | ||||
|   startRefreshSketch: PropTypes.func.isRequired, | ||||
| 
 | ||||
|   stopSketch: PropTypes.func.isRequired, | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
| import { connect, useSelector, useDispatch } from 'react-redux'; | ||||
| import { useSelector, useDispatch } from 'react-redux'; | ||||
| import styled from 'styled-components'; | ||||
| import Header from '../../components/mobile/Header'; | ||||
| import IconButton from '../../components/mobile/IconButton'; | ||||
|  | @ -25,7 +24,7 @@ const Content = styled.div` | |||
|   margin-top: ${remSize(68)}; | ||||
| `; | ||||
| 
 | ||||
| const MobileSketchView = (props) => { | ||||
| const MobileSketchView = () => { | ||||
|   const { files, ide, preferences } = useSelector(state => state); | ||||
| 
 | ||||
|   const htmlFile = useSelector(state => getHTMLFile(state.files)); | ||||
|  |  | |||
|  | @ -41,7 +41,8 @@ export const grays = { | |||
| }; | ||||
| 
 | ||||
| export const common = { | ||||
|   baseFontSize: 12 | ||||
|   baseFontSize: 12, | ||||
|   shadowColor: 'rgba(0, 0, 0, 0.16)' | ||||
| }; | ||||
| 
 | ||||
| export const remSize = size => `${size / common.baseFontSize}rem`; | ||||
|  | @ -97,6 +98,10 @@ export default { | |||
|         border: grays.middleLight, | ||||
|       }, | ||||
|     }, | ||||
|     Modal: { | ||||
|       background: grays.light, | ||||
|       border: grays.middleLight | ||||
|     }, | ||||
|     Separator: grays.middleLight, | ||||
|   }, | ||||
|   [Theme.dark]: { | ||||
|  | @ -138,6 +143,10 @@ export default { | |||
|         border: grays.middleDark, | ||||
|       }, | ||||
|     }, | ||||
|     Modal: { | ||||
|       background: grays.dark, | ||||
|       border: grays.middleDark | ||||
|     }, | ||||
|     Separator: grays.middleDark, | ||||
|   }, | ||||
|   [Theme.contrast]: { | ||||
|  | @ -179,6 +188,10 @@ export default { | |||
|         border: grays.middleDark, | ||||
|       }, | ||||
|     }, | ||||
|     Modal: { | ||||
|       background: grays.dark, | ||||
|       border: 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 = () => {}; | ||||
| 
 | ||||
|  | @ -13,3 +13,30 @@ export const useDidUpdate = (callback, 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