Skip to content

Implement Mobile version of Files tab / sidebar #1539

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

Merged
merged 22 commits into from
Aug 17, 2020
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8b9dd90
:construction: make <Sidebar /> component
ghalestrilo Aug 6, 2020
1680c0c
:construction: add background overlay to modals
ghalestrilo Aug 6, 2020
6d12149
:construction: improve useAsModal rendering
ghalestrilo Aug 6, 2020
d1b4d8d
:broom: remove eslint-disable-line
ghalestrilo Aug 7, 2020
dfedc81
:sparkles: add files tab
ghalestrilo Aug 7, 2020
34be0e7
:sparkles: add visibility padding to the bottom of the editor wrapper
ghalestrilo Aug 7, 2020
81ad78b
:sparkles: add floating button to open files tab
ghalestrilo Aug 7, 2020
6fa1b23
:lipstick: update floating nav button color
ghalestrilo Aug 7, 2020
faaa6d2
:sparkles: make sidebar close via hook toggle
ghalestrilo Aug 7, 2020
df5ac3f
:sparkles: make sidebar close on file click
ghalestrilo Aug 7, 2020
797bf9f
:twisted_rightwards_arrows: merge from develop
ghalestrilo Aug 11, 2020
71bd25d
:twisted_rightwards_arrows: merge from mobile-examples
ghalestrilo Aug 11, 2020
778aa4d
:lipstick: update some styles to position: fixed
ghalestrilo Aug 11, 2020
b88a403
Merge branch 'feature/mobile-examples' of https://github.com/ghalestr…
ghalestrilo Aug 11, 2020
d667b4b
:ok_hand: move files button to bottom bar
ghalestrilo Aug 11, 2020
5b12572
:twisted_rightwards_arrows: merge
ghalestrilo Aug 11, 2020
c50ca2a
:ok_hand: restore sidebar
ghalestrilo Aug 11, 2020
41ecf10
Merge branch 'feature/mobile-examples' of https://github.com/ghalestr…
ghalestrilo Aug 11, 2020
352783a
Merge branch 'feature/mobile-examples' of https://github.com/ghalestr…
ghalestrilo Aug 12, 2020
7c1e6f3
Merge branch 'feature/mobile-examples' of https://github.com/ghalestr…
ghalestrilo Aug 13, 2020
ac737a7
:twisted_rightwards_arrows: merge from mobile-examples
ghalestrilo Aug 17, 2020
9ced702
Merge branch 'develop' into feature/mobile-files-tab
ghalestrilo Aug 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions client/common/icons.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import More from '../images/more.svg';
import Code from '../images/code.svg';
import Terminal from '../images/terminal.svg';

import Folder from '../images/folder-padded.svg';

import CircleTerminal from '../images/circle-terminal.svg';
import CircleFolder from '../images/circle-folder.svg';
import CircleInfo from '../images/circle-info.svg';


// HOC that adds the right web accessibility props
// https://www.scottohara.me/blog/2019/05/22/contextual-images-svgs-and-a11y.html
Expand Down Expand Up @@ -81,3 +87,9 @@ export const PlayIcon = withLabel(Play);
export const MoreIcon = withLabel(More);
export const TerminalIcon = withLabel(Terminal);
export const CodeIcon = withLabel(Code);

export const FolderIcon = withLabel(Folder);

export const CircleTerminalIcon = withLabel(CircleTerminal);
export const CircleFolderIcon = withLabel(CircleFolder);
export const CircleInfoIcon = withLabel(CircleInfo);
2 changes: 1 addition & 1 deletion client/components/Dropdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const DropdownWrapper = styled.ul`
display: flex;
flex-direction: column;
height: auto;
z-index: 9999;
z-index: 2;
border-radius: ${remSize(6)};

& li:first-child { border-radius: ${remSize(5)} ${remSize(5)} 0 0; }
Expand Down
57 changes: 44 additions & 13 deletions client/components/mobile/ActionStrip.jsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,66 @@
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { useDispatch, useSelector } from 'react-redux';
import { remSize } from '../../theme';
import { remSize, prop } from '../../theme';
import IconButton from './IconButton';
import { TerminalIcon } from '../../common/icons';
import { TerminalIcon, FolderIcon } from '../../common/icons';
import * as IDEActions from '../../modules/IDE/actions/ide';

const BottomBarContent = styled.h2`
const BottomBarContent = styled.div`
padding: ${remSize(8)};

display: flex;

svg {
max-height: ${remSize(32)};

}

path { fill: ${prop('primaryTextColor')} !important }

.inverted {
path { fill: ${prop('backgroundColor')} !important }
rect { fill: ${prop('primaryTextColor')} !important }
}
`;

export default () => {
// Maybe this component shouldn't be connected, and instead just receive the `actions` prop
const ActionStrip = ({ toggleExplorer }) => {
const { expandConsole, collapseConsole } = bindActionCreators(IDEActions, useDispatch());
const { consoleIsExpanded } = useSelector(state => state.ide);

const actions = [{ icon: TerminalIcon, aria: 'Say Something', action: consoleIsExpanded ? collapseConsole : expandConsole }];
const actions = [
{
icon: TerminalIcon, inverted: true, aria: 'Open terminal console', action: consoleIsExpanded ? collapseConsole : expandConsole
},
{ icon: FolderIcon, aria: 'Open files explorer', action: toggleExplorer }
];

return (
<BottomBarContent>
{actions.map(({ icon, aria, action }) =>
(<IconButton
icon={icon}
aria-label={aria}
key={`bottom-bar-${aria}`}
onClick={() => action()}
/>))}
{actions.map(({
icon, aria, action, inverted
}) =>
(
<IconButton
inverted={inverted}
className={inverted && 'inverted'}
icon={icon}
aria-label={aria}
key={`bottom-bar-${aria}`}
onClick={() => action()}
/>))}
</BottomBarContent>
);
};

ActionStrip.propTypes = {
toggleExplorer: PropTypes.func
};

ActionStrip.defaultProps = {
toggleExplorer: () => {}
};

export default ActionStrip;
24 changes: 24 additions & 0 deletions client/components/mobile/Explorer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import Sidebar from './Sidebar';
import ConnectedFileNode from '../../modules/IDE/components/FileNode';


const Explorer = ({ id, canEdit, onPressClose }) => (
<Sidebar title="Files" onPressClose={onPressClose}>
<ConnectedFileNode id={id} canEdit={canEdit} onClickFile={() => onPressClose()} />
</Sidebar>
);

Explorer.propTypes = {
id: PropTypes.number.isRequired,
onPressClose: PropTypes.func,
canEdit: PropTypes.bool
};
Explorer.defaultProps = {
canEdit: false,
onPressClose: () => {}
};

export default Explorer;
43 changes: 43 additions & 0 deletions client/components/mobile/FloatingNav.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { remSize, prop } from '../../theme';
import Button from '../../common/Button';
import IconButton from './IconButton';

const FloatingContainer = styled.div`
position: fixed;
right: ${remSize(16)};
top: ${remSize(80)};

text-align: right;
z-index: 3;

svg { width: ${remSize(32)}; };
svg > path { fill: ${prop('Button.default.background')} !important };
`;

const FloatingNav = ({ items }) => (
<FloatingContainer>
{ items.map(({ icon, onPress }) =>
(
<IconButton
onClick={onPress}
icon={icon}
/>
))}
</FloatingContainer>
);

FloatingNav.propTypes = {
items: PropTypes.arrayOf(PropTypes.shape({
icon: PropTypes.element,
onPress: PropTypes.func
}))
};

FloatingNav.defaultProps = {
items: []
};

export default FloatingNav;
10 changes: 6 additions & 4 deletions client/components/mobile/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const textColor = ({ transparent, inverted }) => prop((transparent === false &&


const HeaderDiv = styled.div`
position: fixed;
${props => props.fixed && 'position: fixed;'}
width: 100%;
background: ${props => background(props)};
color: ${textColor};
Expand Down Expand Up @@ -57,9 +57,9 @@ const TitleContainer = styled.div`

const Header = ({
title, subtitle, leftButton, children,
transparent, inverted, slim
transparent, inverted, slim, fixed
}) => (
<HeaderDiv transparent={transparent} slim={slim} inverted={inverted}>
<HeaderDiv transparent={transparent} slim={slim} inverted={inverted} fixed={fixed}>
{leftButton}
<TitleContainer padded={subtitle === null}>
{title && <h2>{title}</h2>}
Expand All @@ -79,6 +79,7 @@ Header.propTypes = {
transparent: PropTypes.bool,
inverted: PropTypes.bool,
slim: PropTypes.bool,
fixed: PropTypes.bool,
};

Header.defaultProps = {
Expand All @@ -88,7 +89,8 @@ Header.defaultProps = {
children: [],
transparent: false,
inverted: false,
slim: false
slim: false,
fixed: true
};

export default Header;
3 changes: 3 additions & 0 deletions client/components/mobile/IDEWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import React from 'react';
import styled from 'styled-components';
import { remSize } from '../../theme';

// Applies padding to top and bottom so editor content is always visible

export default styled.div`
z-index: 0;
margin-top: ${remSize(16)};
.CodeMirror-sizer > * { padding-bottom: ${remSize(320)}; };
`;
46 changes: 46 additions & 0 deletions client/components/mobile/Sidebar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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 Header from './Header';
import IconButton from './IconButton';
import { ExitIcon } from '../../common/icons';


const SidebarWrapper = styled.div`
height: 100%;
width: ${remSize(180)};

position: fixed;
z-index: 2;
left: 0;

background: white;
box-shadow: 0 6px 6px 0 rgba(0,0,0,0.10);
`;

const Sidebar = ({ title, onPressClose, children }) => (
<SidebarWrapper>
{title &&
<Header slim title={title} fixed={false}>
<IconButton onClick={onPressClose} icon={ExitIcon} aria-label="Return to ide view" />
</Header>}
{children}
</SidebarWrapper>
);

Sidebar.propTypes = {
title: PropTypes.string,
onPressClose: PropTypes.func,
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
};

Sidebar.defaultProps = {
title: null,
children: [],
onPressClose: () => {}
};


export default Sidebar;
27 changes: 23 additions & 4 deletions client/components/useAsModal.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import React from 'react';
import styled from 'styled-components';
import { useModalBehavior } from '../utils/custom-hooks';

export default (component) => {
const [visible, trigger, setRef] = useModalBehavior();
const BackgroundOverlay = styled.div`
position: fixed;
z-index: 2;
width: 100% !important;
height: 100% !important;

background: black;
opacity: 0.3;
`;

const wrapper = () => <div ref={setRef}> {visible && component} </div>; // eslint-disable-line
export default (Element, hasOverlay = false) => {
const [visible, toggle, setRef] = useModalBehavior();

return [trigger, wrapper];
const wrapper = () => (visible &&
<div>
{hasOverlay && <BackgroundOverlay />}
<div ref={setRef}>
{ (typeof (Element) === 'function')
? Element(toggle)
: Element}
</div>
</div>);

return [toggle, wrapper];
};
5 changes: 5 additions & 0 deletions client/images/circle-folder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions client/images/circle-info.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions client/images/circle-terminal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions client/images/folder-padded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion client/modules/App/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ class App extends React.Component {

componentWillReceiveProps(nextProps) {
const locationWillChange = nextProps.location !== this.props.location;
const shouldSkipRemembering = nextProps.location.state && nextProps.location.state.skipSavingPath === true;
const shouldSkipRemembering =
nextProps.location.state &&
nextProps.location.state.skipSavingPath === true;

if (locationWillChange && !shouldSkipRemembering) {
this.props.setPreviousPath(this.props.location.pathname);
Expand Down
15 changes: 11 additions & 4 deletions client/modules/IDE/components/FileNode.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,15 @@ export class FileNode extends React.Component {
handleFileClick = (event) => {
event.stopPropagation();
const { isDeleting } = this.state;
const { id, setSelectedFile, name } = this.props;
const {
id, setSelectedFile, name, onClickFile
} = this.props;
if (name !== 'root' && !isDeleting) {
setSelectedFile(id);
}

// debugger; // eslint-disable-line
if (onClickFile) { onClickFile(); }
}

handleFileNameChange = (event) => {
Expand Down Expand Up @@ -214,7 +219,7 @@ export class FileNode extends React.Component {

renderChild = childId => (
<li key={childId}>
<ConnectedFileNode id={childId} parentId={this.props.id} canEdit={this.props.canEdit} />
<ConnectedFileNode id={childId} parentId={this.props.id} canEdit={this.props.canEdit} onClickFile={this.props.onClickFile} />
</li>
)

Expand All @@ -233,7 +238,7 @@ export class FileNode extends React.Component {
const isRoot = this.props.name === 'root';

return (
<div className={itemClass}>
<div className={itemClass} >
{ !isRoot &&
<div className="file-item__content" onContextMenu={this.toggleFileOptions}>
<span className="file-item__spacer"></span>
Expand Down Expand Up @@ -382,10 +387,12 @@ FileNode.propTypes = {
hideFolderChildren: PropTypes.func.isRequired,
canEdit: PropTypes.bool.isRequired,
openUploadFileModal: PropTypes.func.isRequired,
authenticated: PropTypes.bool.isRequired
authenticated: PropTypes.bool.isRequired,
onClickFile: PropTypes.func
};

FileNode.defaultProps = {
onClickFile: null,
parentId: '0',
isSelectedFile: false,
isFolderClosed: false,
Expand Down
Loading