diff --git a/client/components/RootPage.jsx b/client/components/RootPage.jsx index ca6a4723cd..cece68f39e 100644 --- a/client/components/RootPage.jsx +++ b/client/components/RootPage.jsx @@ -4,10 +4,16 @@ import { prop } from '../theme'; const RootPage = styled.div` min-height: 100%; display: flex; + justify-content: start; flex-direction: column; color: ${prop('primaryTextColor')}; background-color: ${prop('backgroundColor')}; height: ${({ fixedHeight }) => fixedHeight || 'initial'}; + + @media (max-width: 770px) { + height: 100%; + overflow: hidden; + } `; export default RootPage; diff --git a/client/images/plus-icon.svg b/client/images/plus-icon.svg index da91915a19..eb6cafb6c6 100644 --- a/client/images/plus-icon.svg +++ b/client/images/plus-icon.svg @@ -1,12 +1,3 @@ - - - - - - - - - - - + + diff --git a/client/index.integration.test.jsx b/client/index.integration.test.jsx index d76345293d..7c237bcb56 100644 --- a/client/index.integration.test.jsx +++ b/client/index.integration.test.jsx @@ -59,6 +59,7 @@ describe('index.jsx integration', () => { // spy on this function and wait for it to be called before making assertions const spy = jest.spyOn(Actions, 'getUser'); + window.process.env.PREVIEW_URL = 'http://localhost:8002'; beforeEach(async () => { act(() => { subject(); diff --git a/client/modules/IDE/components/Editor.unit.test.jsx b/client/modules/IDE/components/Editor/Editor.unit.test.jsx similarity index 75% rename from client/modules/IDE/components/Editor.unit.test.jsx rename to client/modules/IDE/components/Editor/Editor.unit.test.jsx index 073a357fdb..1af90615b0 100644 --- a/client/modules/IDE/components/Editor.unit.test.jsx +++ b/client/modules/IDE/components/Editor/Editor.unit.test.jsx @@ -2,11 +2,11 @@ import React from 'react'; import configureStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import { act } from 'react-dom/test-utils'; -import Editor from './Editor'; -import { reduxRender } from '../../../test-utils'; -import { initialTestState } from '../../../testData/testReduxStore'; +import Editor from '.'; +import { reduxRender } from '../../../../test-utils'; +import { initialTestState } from '../../../../testData/testReduxStore'; -jest.mock('../../../i18n'); +jest.mock('../../../../i18n'); describe('', () => { const mockStore = configureStore([thunk]); diff --git a/client/modules/IDE/components/Editor/MobileEditor.jsx b/client/modules/IDE/components/Editor/MobileEditor.jsx new file mode 100644 index 0000000000..c3e56afd79 --- /dev/null +++ b/client/modules/IDE/components/Editor/MobileEditor.jsx @@ -0,0 +1,79 @@ +import styled from 'styled-components'; +import { prop, remSize } from '../../../../theme'; + +export const EditorContainer = styled.div` + display: flex; + flex-direction: column; + height: 100%; + padding-bottom: 5rem; + transform: ${(props) => + props.expanded ? 'translateX(50%)' : 'translateX(0)'}; + + > header { + display: flex; + ${prop('MobilePanel.secondary')} + > span { + display: flex; + justify-content: center; + align-items: center; + padding: ${remSize(10)}; + font-weight: bold; + ${prop('MobilePanel.default')} + } + } + + > section { + display: flex; + flex-direction: column; + height: 100%; + width: 100vw; + overflow-y: auto; + } +`; + +export const EditorHolder = styled.div` + min-height: 100%; +`; + +export const PreviewWrapper = styled.div` + display: ${(props) => (props.show ? 'block' : 'none')}; + position: relative; + height: 100vh; + min-width: 100%; + + .preview-console { + z-index: 1; + } +`; + +export const EditorSidebarWrapper = styled.div` + display: ${(props) => (props.show ? 'block' : 'none')}; + height: 100%; + position: relative; +`; + +export const FileDrawer = styled.div` + height: 100%; + width: 50vw; + display: flex; + flex-direction: column; + position: absolute; + /* z-index: 10; */ + background: ${prop('backgroundColor')}; + + > button[data-backdrop='filedrawer'] { + position: absolute; + background-color: #0005; + height: 100%; + width: 100%; + z-index: 2; + transform: translateX(100%); + } + + @media (min-width: 770px) { + width: 100%; + > button[data-backdrop='filedrawer'] { + display: none; + } + } +`; diff --git a/client/modules/IDE/components/Editor.jsx b/client/modules/IDE/components/Editor/index.jsx similarity index 80% rename from client/modules/IDE/components/Editor.jsx rename to client/modules/IDE/components/Editor/index.jsx index d3658d5290..35e47248f8 100644 --- a/client/modules/IDE/components/Editor.jsx +++ b/client/modules/IDE/components/Editor/index.jsx @@ -1,3 +1,5 @@ +// TODO: convert to functional component + import PropTypes from 'prop-types'; import React from 'react'; import CodeMirror from 'codemirror'; @@ -40,32 +42,35 @@ import classNames from 'classnames'; import { debounce } from 'lodash'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import '../../../utils/htmlmixed'; -import '../../../utils/p5-javascript'; -import Timer from '../components/Timer'; -import EditorAccessibility from '../components/EditorAccessibility'; -import { selectActiveFile } from '../selectors/files'; -import AssetPreview from './AssetPreview'; -import { metaKey } from '../../../utils/metaKey'; -import './show-hint'; -import * as hinter from '../../../utils/p5-hinter'; - -import '../../../utils/codemirror-search'; - -import beepUrl from '../../../sounds/audioAlert.mp3'; -import UnsavedChangesDotIcon from '../../../images/unsaved-changes-dot.svg'; -import RightArrowIcon from '../../../images/right-arrow.svg'; -import LeftArrowIcon from '../../../images/left-arrow.svg'; -import { getHTMLFile } from '../reducers/files'; - -import * as FileActions from '../actions/files'; -import * as IDEActions from '../actions/ide'; -import * as ProjectActions from '../actions/project'; -import * as EditorAccessibilityActions from '../actions/editorAccessibility'; -import * as PreferencesActions from '../actions/preferences'; -import * as UserActions from '../../User/actions'; -import * as ToastActions from '../actions/toast'; -import * as ConsoleActions from '../actions/console'; +import MediaQuery from 'react-responsive'; +import '../../../../utils/htmlmixed'; +import '../../../../utils/p5-javascript'; +import { metaKey } from '../../../../utils/metaKey'; +import '../show-hint'; +import * as hinter from '../../../../utils/p5-hinter'; +import '../../../../utils/codemirror-search'; + +import beepUrl from '../../../../sounds/audioAlert.mp3'; +import RightArrowIcon from '../../../../images/right-arrow.svg'; +import LeftArrowIcon from '../../../../images/left-arrow.svg'; +import { getHTMLFile } from '../../reducers/files'; +import { selectActiveFile } from '../../selectors/files'; + +import * as FileActions from '../../actions/files'; +import * as IDEActions from '../../actions/ide'; +import * as ProjectActions from '../../actions/project'; +import * as EditorAccessibilityActions from '../../actions/editorAccessibility'; +import * as PreferencesActions from '../../actions/preferences'; +import * as UserActions from '../../../User/actions'; +import * as ConsoleActions from '../../actions/console'; + +import AssetPreview from '../AssetPreview'; +import Timer from '../Timer'; +import EditorAccessibility from '../EditorAccessibility'; +import UnsavedChangesIndicator from '../UnsavedChangesIndicator'; +import { EditorContainer, EditorHolder } from './MobileEditor'; +import { FolderIcon } from '../../../../common/icons'; +import IconButton from '../../../../components/mobile/IconButton'; emmet(CodeMirror); @@ -98,7 +103,7 @@ class Editor extends React.Component { componentDidMount() { this.beep = new Audio(beepUrl); - this.widgets = []; + // this.widgets = []; this._cm = CodeMirror(this.codemirrorContainer, { theme: `p5-${this.props.theme}`, lineNumbers: this.props.lineNumbers, @@ -306,6 +311,13 @@ class Editor extends React.Component { this._cm.removeLineClass(i, 'background', 'line-runtime-error'); } } + + this.props.provideController({ + tidyCode: this.tidyCode, + showFind: this.showFind, + showReplace: this.showReplace, + getContent: this.getContent + }); } componentWillUnmount() { @@ -496,52 +508,80 @@ class Editor extends React.Component { }); return ( -
-
- - -
- - {this.props.file.name} - - {this.props.unsavedChanges ? ( - + {(matches) => + matches ? ( +
+
+ + +
+ + {this.props.file.name} + + + +
+
+
{ + this.codemirrorContainer = element; + }} + className={editorHolderClass} + /> + {this.props.file.url ? ( + + ) : null} + +
+ ) : ( + +
+ + + {this.props.file.name} + + +
+
+ { + this.codemirrorContainer = element; + }} + /> + {this.props.file.url ? ( + ) : null} - - - -
- -
{ - this.codemirrorContainer = element; - }} - className={editorHolderClass} - /> - {this.props.file.url ? ( - - ) : null} - - + + + + ) + } + ); } } @@ -613,7 +653,6 @@ function mapStateToProps(state) { editorAccessibility: state.editorAccessibility, user: state.user, project: state.project, - toast: state.toast, consoleEvents: state.console, ...state.preferences, @@ -634,7 +673,6 @@ function mapDispatchToProps(dispatch) { IDEActions, PreferencesActions, UserActions, - ToastActions, ConsoleActions ), dispatch diff --git a/client/modules/IDE/components/Header/MobileNav.jsx b/client/modules/IDE/components/Header/MobileNav.jsx index 5de520a5ae..3c91f1e699 100644 --- a/client/modules/IDE/components/Header/MobileNav.jsx +++ b/client/modules/IDE/components/Header/MobileNav.jsx @@ -224,7 +224,7 @@ const MobileNav = () => { } } - const title = useMemo(resolveTitle, [pageName]); + const title = useMemo(resolveTitle, [pageName, project.name]); const Logo = AsteriskIcon; return ( @@ -261,7 +261,7 @@ const MobileNav = () => { )} - {title === project.name ? ( + {pageName === 'home' ? ( ) : (
diff --git a/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap b/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap index 00aea970f4..3b55bfae67 100644 --- a/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap +++ b/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap @@ -320,7 +320,7 @@ exports[`Nav renders dashboard version for mobile 1`] = ` >