From 6caba86751172fc612174d591466ae3d3df33e4e Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 10 Feb 2018 23:24:12 -0800 Subject: [PATCH] Mobile Responsive Styles (#580) * WIP: Responsive styles * Flip breakpoints, ensure doc doesn't spread * Add MenuIcon * Refactor Sidebar to share mobile responsive styles * Fix accidental find/replace * Tweak padding to take into account icon spacing --- app/components/Actions/Actions.js | 9 +- .../CenteredContent/CenteredContent.js | 7 +- app/components/ColorPicker/ColorPicker.js | 1 + app/components/Editor/Editor.js | 15 ++- app/components/Editor/components/Contents.js | 8 +- app/components/Editor/components/Image.js | 1 + app/components/Icon/MenuIcon.js | 12 +++ app/components/Layout/Layout.js | 9 +- app/components/Modal/Modal.js | 10 +- app/components/Sidebar/Main.js | 83 ++++++++++++++++ app/components/Sidebar/Settings.js | 31 +----- app/components/Sidebar/Sidebar.js | 98 +++++++++---------- app/components/Sidebar/index.js | 2 +- app/components/Toasts/components/Toast.js | 6 +- app/scenes/Settings/Members.js | 2 +- app/stores/UiStore.js | 11 +++ server/pages/Home.js | 4 - 17 files changed, 213 insertions(+), 96 deletions(-) create mode 100644 app/components/Icon/MenuIcon.js create mode 100644 app/components/Sidebar/Main.js diff --git a/app/components/Actions/Actions.js b/app/components/Actions/Actions.js index 0aebc369..66b9cff2 100644 --- a/app/components/Actions/Actions.js +++ b/app/components/Actions/Actions.js @@ -1,5 +1,6 @@ // @flow import styled from 'styled-components'; +import breakpoint from 'styled-components-breakpoint'; import Flex from 'shared/components/Flex'; import { layout, color } from 'shared/styles/constants'; @@ -25,14 +26,20 @@ const Actions = styled(Flex)` position: fixed; top: 0; right: 0; - padding: ${layout.vpadding} ${layout.hpadding} 8px 8px; + left: 0; border-radius: 3px; background: rgba(255, 255, 255, 0.9); + padding: 16px; -webkit-backdrop-filter: blur(20px); @media print { display: none; } + + ${breakpoint('tablet')` + left: auto; + padding: ${layout.vpadding} ${layout.hpadding} 8px 8px; + `}; `; export default Actions; diff --git a/app/components/CenteredContent/CenteredContent.js b/app/components/CenteredContent/CenteredContent.js index b3736f6c..8e6ae3ae 100644 --- a/app/components/CenteredContent/CenteredContent.js +++ b/app/components/CenteredContent/CenteredContent.js @@ -1,6 +1,7 @@ // @flow import React from 'react'; import styled from 'styled-components'; +import breakpoint from 'styled-components-breakpoint'; type Props = { children?: React.Element, @@ -8,7 +9,11 @@ type Props = { const Container = styled.div` width: 100%; - padding: 60px; + padding: 60px 20px; + + ${breakpoint('tablet')` + padding: 60px; + `}; `; const Content = styled.div` diff --git a/app/components/ColorPicker/ColorPicker.js b/app/components/ColorPicker/ColorPicker.js index f359312d..33b4d5ea 100644 --- a/app/components/ColorPicker/ColorPicker.js +++ b/app/components/ColorPicker/ColorPicker.js @@ -157,6 +157,7 @@ const SwatchInset = styled(Flex)` const StyledOutline = styled(Outline)` padding: 5px; + flex-wrap: wrap; strong { font-weight: 500; diff --git a/app/components/Editor/Editor.js b/app/components/Editor/Editor.js index 8cada448..be58b3ed 100644 --- a/app/components/Editor/Editor.js +++ b/app/components/Editor/Editor.js @@ -4,8 +4,10 @@ import { observable } from 'mobx'; import { observer } from 'mobx-react'; import { Value, Change } from 'slate'; import { Editor } from 'slate-react'; -import type { SlateNodeProps, Plugin } from './types'; +import styled from 'styled-components'; +import breakpoint from 'styled-components-breakpoint'; import keydown from 'react-keydown'; +import type { SlateNodeProps, Plugin } from './types'; import getDataTransferFiles from 'utils/getDataTransferFiles'; import Flex from 'shared/components/Flex'; import ClickablePadding from './components/ClickablePadding'; @@ -20,7 +22,6 @@ import renderMark from './marks'; import createRenderNode from './nodes'; import schema from './schema'; import { isModKey } from './utils'; -import styled from 'styled-components'; type Props = { text: string, @@ -227,9 +228,15 @@ class MarkdownEditor extends Component { } const MaxWidth = styled(Flex)` - margin: 0 60px; - max-width: 46em; + padding: 0 20px; + max-width: 100vw; height: 100%; + + ${breakpoint('tablet')` + padding: 0; + margin: 0 60px; + max-width: 46em; + `}; `; const Header = styled(Flex)` diff --git a/app/components/Editor/components/Contents.js b/app/components/Editor/components/Contents.js index 0215bb7f..87f01ae9 100644 --- a/app/components/Editor/components/Contents.js +++ b/app/components/Editor/components/Contents.js @@ -1,5 +1,7 @@ // @flow import React, { Component } from 'react'; +import styled from 'styled-components'; +import breakpoint from 'styled-components-breakpoint'; import { observable } from 'mobx'; import { observer } from 'mobx-react'; import { Editor } from 'slate-react'; @@ -7,7 +9,6 @@ import { Block } from 'slate'; import { List } from 'immutable'; import { color } from 'shared/styles/constants'; import headingToSlug from '../headingToSlug'; -import styled from 'styled-components'; type Props = { editor: Editor, @@ -91,6 +92,7 @@ class Contents extends Component { } const Wrapper = styled.div` + display: none; position: fixed; right: 0; top: 150px; @@ -99,6 +101,10 @@ const Wrapper = styled.div` @media print { display: none; } + + ${breakpoint('tablet')` + display: block; + `}; `; const Anchor = styled.a` diff --git a/app/components/Editor/components/Image.js b/app/components/Editor/components/Image.js index 0a6d1c83..a53e4b89 100644 --- a/app/components/Editor/components/Image.js +++ b/app/components/Editor/components/Image.js @@ -94,6 +94,7 @@ const Caption = styled.input` text-align: center; width: 100%; outline: none; + background: none; &::placeholder { color: ${color.slate}; diff --git a/app/components/Icon/MenuIcon.js b/app/components/Icon/MenuIcon.js new file mode 100644 index 00000000..642c7e52 --- /dev/null +++ b/app/components/Icon/MenuIcon.js @@ -0,0 +1,12 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function MenuIcon(props: Props) { + return ( + + + + ); +} diff --git a/app/components/Layout/Layout.js b/app/components/Layout/Layout.js index b5d755d0..f01f7212 100644 --- a/app/components/Layout/Layout.js +++ b/app/components/Layout/Layout.js @@ -4,6 +4,7 @@ import { Switch, Route, withRouter } from 'react-router-dom'; import type { Location } from 'react-router-dom'; import { Helmet } from 'react-helmet'; import styled from 'styled-components'; +import breakpoint from 'styled-components-breakpoint'; import { observer, inject } from 'mobx-react'; import keydown from 'react-keydown'; import Analytics from 'shared/components/Analytics'; @@ -122,12 +123,16 @@ const Container = styled(Flex)` `; const Content = styled(Flex)` - margin-left: ${props => (props.editMode ? 0 : layout.sidebarWidth)}; + margin: 0; transition: margin-left 200ms ease-in-out; @media print { - margin-left: 0; + margin: 0; } + + ${breakpoint('tablet')` + margin-left: ${props => (props.editMode ? 0 : layout.sidebarWidth)}; + `}; `; export default withRouter(inject('user', 'auth', 'ui', 'documents')(Layout)); diff --git a/app/components/Modal/Modal.js b/app/components/Modal/Modal.js index 29790785..0a7c017b 100644 --- a/app/components/Modal/Modal.js +++ b/app/components/Modal/Modal.js @@ -2,6 +2,7 @@ import React from 'react'; import { observer } from 'mobx-react'; import styled, { injectGlobal } from 'styled-components'; +import breakpoint from 'styled-components-breakpoint'; import ReactModal from 'react-modal'; import { color } from 'shared/styles/constants'; import { fadeAndScaleIn } from 'shared/styles/animations'; @@ -80,14 +81,19 @@ const StyledModal = styled(ReactModal)` const Close = styled.a` position: fixed; - top: 3rem; - right: 3rem; + top: 16px; + right: 16px; opacity: 0.5; color: ${color.text}; &:hover { opacity: 1; } + + ${breakpoint('tablet')` + top: 3rem; + right: 3rem; + `}; `; export default observer(Modal); diff --git a/app/components/Sidebar/Main.js b/app/components/Sidebar/Main.js new file mode 100644 index 00000000..50f80d28 --- /dev/null +++ b/app/components/Sidebar/Main.js @@ -0,0 +1,83 @@ +// @flow +import React, { Component } from 'react'; +import { withRouter } from 'react-router-dom'; +import type { Location } from 'react-router-dom'; +import { observer, inject } from 'mobx-react'; +import Flex from 'shared/components/Flex'; + +import AccountMenu from 'menus/AccountMenu'; +import Sidebar, { Section } from './Sidebar'; +import Scrollable from 'components/Scrollable'; +import HomeIcon from 'components/Icon/HomeIcon'; +import SearchIcon from 'components/Icon/SearchIcon'; +import StarredIcon from 'components/Icon/StarredIcon'; +import Collections from './components/Collections'; +import SidebarLink from './components/SidebarLink'; +import HeaderBlock from './components/HeaderBlock'; + +import AuthStore from 'stores/AuthStore'; +import UiStore from 'stores/UiStore'; + +type Props = { + history: Object, + location: Location, + auth: AuthStore, + ui: UiStore, +}; + +@observer +class MainSidebar extends Component { + props: Props; + + handleCreateCollection = () => { + this.props.ui.setActiveModal('collection-new'); + }; + + handleEditCollection = () => { + this.props.ui.setActiveModal('collection-edit'); + }; + + render() { + const { auth } = this.props; + const { user, team } = auth; + if (!user || !team) return; + + return ( + + + } + /> + + +
+ }> + Home + + }> + Search + + }> + Starred + +
+
+ +
+
+
+
+ ); + } +} + +export default withRouter(inject('user', 'auth', 'ui')(MainSidebar)); diff --git a/app/components/Sidebar/Settings.js b/app/components/Sidebar/Settings.js index b8ab1e7c..90995ffc 100644 --- a/app/components/Sidebar/Settings.js +++ b/app/components/Sidebar/Settings.js @@ -1,12 +1,9 @@ // @flow import React, { Component } from 'react'; -import { withRouter } from 'react-router-dom'; -import type { Location } from 'react-router-dom'; -import styled from 'styled-components'; import { observer, inject } from 'mobx-react'; import Flex from 'shared/components/Flex'; -import { color, layout } from 'shared/styles/constants'; +import Sidebar, { Section } from './Sidebar'; import Scrollable from 'components/Scrollable'; import ProfileIcon from 'components/Icon/ProfileIcon'; import SettingsIcon from 'components/Icon/SettingsIcon'; @@ -19,12 +16,11 @@ import AuthStore from 'stores/AuthStore'; type Props = { history: Object, - location: Location, auth: AuthStore, }; @observer -class Sidebar extends Component { +class SettingsSidebar extends Component { props: Props; returnToDashboard = () => { @@ -36,7 +32,7 @@ class Sidebar extends Component { if (!team) return; return ( - + - + ); } } -const Container = styled(Flex)` - position: fixed; - top: 0; - bottom: 0; - left: 0; - width: ${layout.sidebarWidth}; - background: ${color.smoke}; - transition: left 200ms ease-in-out; -`; - -const Section = styled(Flex)` - flex-direction: column; - margin: 24px 0; - padding: 0 24px; - position: relative; -`; - -export default withRouter(inject('auth')(Sidebar)); +export default inject('auth')(SettingsSidebar); diff --git a/app/components/Sidebar/Sidebar.js b/app/components/Sidebar/Sidebar.js index d337d422..01749fe5 100644 --- a/app/components/Sidebar/Sidebar.js +++ b/app/components/Sidebar/Sidebar.js @@ -3,23 +3,18 @@ import React, { Component } from 'react'; import { withRouter } from 'react-router-dom'; import type { Location } from 'react-router-dom'; import styled from 'styled-components'; +import breakpoint from 'styled-components-breakpoint'; import { observer, inject } from 'mobx-react'; import Flex from 'shared/components/Flex'; import { color, layout } from 'shared/styles/constants'; -import AccountMenu from 'menus/AccountMenu'; -import Scrollable from 'components/Scrollable'; -import HomeIcon from 'components/Icon/HomeIcon'; -import SearchIcon from 'components/Icon/SearchIcon'; -import StarredIcon from 'components/Icon/StarredIcon'; -import Collections from './components/Collections'; -import SidebarLink from './components/SidebarLink'; -import HeaderBlock from './components/HeaderBlock'; - +import CloseIcon from 'components/Icon/CloseIcon'; +import MenuIcon from 'components/Icon/MenuIcon'; import AuthStore from 'stores/AuthStore'; import UiStore from 'stores/UiStore'; type Props = { + children: React.Element, history: Object, location: Location, auth: AuthStore, @@ -30,53 +25,32 @@ type Props = { class Sidebar extends Component { props: Props; - handleCreateCollection = () => { - this.props.ui.setActiveModal('collection-new'); + componentWillReceiveProps = (nextProps: Props) => { + if (this.props.location !== nextProps.location) { + this.props.ui.hideMobileSidebar(); + } }; - handleEditCollection = () => { - this.props.ui.setActiveModal('collection-edit'); + toggleSidebar = () => { + this.props.ui.toggleMobileSidebar(); }; render() { - const { auth, ui } = this.props; - const { user, team } = auth; - if (!user || !team) return; + const { children, ui } = this.props; return ( - - - } - /> - - - -
- }> - Home - - }> - Search - - }> - Starred - -
-
- -
-
-
+ + + {ui.mobileSidebarVisible ? : } + + {children} ); } @@ -87,21 +61,41 @@ const Container = styled(Flex)` top: 0; bottom: 0; left: ${props => (props.editMode ? `-${layout.sidebarWidth}` : 0)}; - width: ${layout.sidebarWidth}; + width: 100%; background: ${color.smoke}; transition: left 200ms ease-in-out; + margin-left: ${props => (props.mobileSidebarVisible ? 0 : '-100%')}; + z-index: 1; @media print { display: none; left: 0; } + + ${breakpoint('tablet')` + width: ${layout.sidebarWidth}; + margin: 0; + `}; `; -const Section = styled(Flex)` +export const Section = styled(Flex)` flex-direction: column; margin: 24px 0; padding: 0 24px; position: relative; `; -export default withRouter(inject('user', 'auth', 'ui')(Sidebar)); +const Toggle = styled.a` + position: fixed; + top: 0; + left: ${props => (props.mobileSidebarVisible ? 'auto' : 0)}; + right: ${props => (props.mobileSidebarVisible ? 0 : 'auto')}; + z-index: 1; + margin: 16px; + + ${breakpoint('tablet')` + display: none; + `}; +`; + +export default withRouter(inject('ui')(Sidebar)); diff --git a/app/components/Sidebar/index.js b/app/components/Sidebar/index.js index c1b441c2..66021b40 100644 --- a/app/components/Sidebar/index.js +++ b/app/components/Sidebar/index.js @@ -1,3 +1,3 @@ // @flow -import Sidebar from './Sidebar'; +import Sidebar from './Main'; export default Sidebar; diff --git a/app/components/Toasts/components/Toast.js b/app/components/Toasts/components/Toast.js index 4fc42580..d0eb4605 100644 --- a/app/components/Toasts/components/Toast.js +++ b/app/components/Toasts/components/Toast.js @@ -33,7 +33,11 @@ class Toast extends Component { } render() { - const { type, onRequestClose, message } = this.props; + const { type, onRequestClose } = this.props; + const message = + typeof this.props.message === 'string' + ? this.props.message + : this.props.message.toString(); return ( diff --git a/app/scenes/Settings/Members.js b/app/scenes/Settings/Members.js index 26d4073b..748b90f8 100644 --- a/app/scenes/Settings/Members.js +++ b/app/scenes/Settings/Members.js @@ -41,7 +41,7 @@ class Members extends Component { {this.props.settings.members && ( {this.props.settings.members.map(member => ( - + diff --git a/app/stores/UiStore.js b/app/stores/UiStore.js index 09e42feb..91aca089 100644 --- a/app/stores/UiStore.js +++ b/app/stores/UiStore.js @@ -10,6 +10,7 @@ class UiStore { @observable activeCollectionId: ?string; @observable progressBarVisible: boolean = false; @observable editMode: boolean = false; + @observable mobileSidebarVisible: boolean = false; /* Actions */ @action @@ -65,6 +66,16 @@ class UiStore { disableProgressBar() { this.progressBarVisible = false; } + + @action + toggleMobileSidebar() { + this.mobileSidebarVisible = !this.mobileSidebarVisible; + } + + @action + hideMobileSidebar() { + this.mobileSidebarVisible = false; + } } export default UiStore; diff --git a/server/pages/Home.js b/server/pages/Home.js index 7589c121..81749498 100644 --- a/server/pages/Home.js +++ b/server/pages/Home.js @@ -124,10 +124,6 @@ const Highlights = styled(Grid)` const Features = styled(Grid)` padding: 0 2em; overflow: hidden; - - ${breakpoint('mobile')` - flex-direction - `}; `; const Feature = styled(Grid.Unit)`