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
This commit is contained in:
@ -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;
|
||||
|
@ -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<any>,
|
||||
@ -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`
|
||||
|
@ -157,6 +157,7 @@ const SwatchInset = styled(Flex)`
|
||||
|
||||
const StyledOutline = styled(Outline)`
|
||||
padding: 5px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
|
@ -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)`
|
||||
|
@ -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`
|
||||
|
@ -94,6 +94,7 @@ const Caption = styled.input`
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
background: none;
|
||||
|
||||
&::placeholder {
|
||||
color: ${color.slate};
|
||||
|
12
app/components/Icon/MenuIcon.js
Normal file
12
app/components/Icon/MenuIcon.js
Normal file
@ -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 (
|
||||
<Icon {...props}>
|
||||
<path d="M4.99992841,5.99992841 L19.0000716,5.99992841 C19.5523168,5.99992841 20,6.4476116 20,6.99985681 C20,7.55210202 19.5523168,7.99978522 19.0000716,7.99978522 L4.99992841,7.99978522 C4.4476832,7.99978522 4,7.55210202 4,6.99985681 C4,6.4476116 4.4476832,5.99992841 4.99992841,5.99992841 Z M4.99992841,15.9992125 L19.0000716,15.9992125 C19.5523168,15.9992125 20,16.4468957 20,16.9991409 C20,17.5513861 19.5523168,17.9990693 19.0000716,17.9990693 L4.99992841,17.9990693 C4.4476832,17.9990693 4,17.5513861 4,16.9991409 C4,16.4468957 4.4476832,15.9992125 4.99992841,15.9992125 Z M4.99992841,10.9995704 L19.0000716,10.9995704 C19.5523168,10.9995704 20,11.4472536 20,11.9994988 C20,12.5517441 19.5523168,12.9994273 19.0000716,12.9994273 L4.99992841,12.9994273 C4.4476832,12.9994273 4,12.5517441 4,11.9994988 C4,11.4472536 4.4476832,10.9995704 4.99992841,10.9995704 Z" />
|
||||
</Icon>
|
||||
);
|
||||
}
|
@ -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));
|
||||
|
@ -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);
|
||||
|
83
app/components/Sidebar/Main.js
Normal file
83
app/components/Sidebar/Main.js
Normal file
@ -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 (
|
||||
<Sidebar>
|
||||
<AccountMenu
|
||||
label={
|
||||
<HeaderBlock
|
||||
subheading={user.name}
|
||||
teamName={team.name}
|
||||
logoUrl={team.avatarUrl}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Flex auto column>
|
||||
<Scrollable>
|
||||
<Section>
|
||||
<SidebarLink to="/dashboard" icon={<HomeIcon />}>
|
||||
Home
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/search" icon={<SearchIcon />}>
|
||||
Search
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/starred" icon={<StarredIcon />}>
|
||||
Starred
|
||||
</SidebarLink>
|
||||
</Section>
|
||||
<Section>
|
||||
<Collections
|
||||
history={this.props.history}
|
||||
location={this.props.location}
|
||||
onCreateCollection={this.handleCreateCollection}
|
||||
/>
|
||||
</Section>
|
||||
</Scrollable>
|
||||
</Flex>
|
||||
</Sidebar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(inject('user', 'auth', 'ui')(MainSidebar));
|
@ -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 (
|
||||
<Container column>
|
||||
<Sidebar>
|
||||
<HeaderBlock
|
||||
subheading="◄ Return to Dashboard"
|
||||
teamName={team.name}
|
||||
@ -69,26 +65,9 @@ class Sidebar extends Component {
|
||||
</Section>
|
||||
</Scrollable>
|
||||
</Flex>
|
||||
</Container>
|
||||
</Sidebar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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<any>,
|
||||
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 (
|
||||
<Container column editMode={ui.editMode}>
|
||||
<AccountMenu
|
||||
label={
|
||||
<HeaderBlock
|
||||
subheading={user.name}
|
||||
teamName={team.name}
|
||||
logoUrl={team.avatarUrl}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Flex auto column>
|
||||
<Scrollable>
|
||||
<Section>
|
||||
<SidebarLink to="/dashboard" icon={<HomeIcon />}>
|
||||
Home
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/search" icon={<SearchIcon />}>
|
||||
Search
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/starred" icon={<StarredIcon />}>
|
||||
Starred
|
||||
</SidebarLink>
|
||||
</Section>
|
||||
<Section>
|
||||
<Collections
|
||||
history={this.props.history}
|
||||
location={this.props.location}
|
||||
onCreateCollection={this.handleCreateCollection}
|
||||
/>
|
||||
</Section>
|
||||
</Scrollable>
|
||||
</Flex>
|
||||
<Container
|
||||
editMode={ui.editMode}
|
||||
mobileSidebarVisible={ui.mobileSidebarVisible}
|
||||
column
|
||||
>
|
||||
<Toggle
|
||||
onClick={this.toggleSidebar}
|
||||
mobileSidebarVisible={ui.mobileSidebarVisible}
|
||||
>
|
||||
{ui.mobileSidebarVisible ? <CloseIcon /> : <MenuIcon />}
|
||||
</Toggle>
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@ -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));
|
||||
|
@ -1,3 +1,3 @@
|
||||
// @flow
|
||||
import Sidebar from './Sidebar';
|
||||
import Sidebar from './Main';
|
||||
export default Sidebar;
|
||||
|
@ -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 (
|
||||
<Container onClick={onRequestClose} type={type}>
|
||||
|
@ -41,7 +41,7 @@ class Members extends Component {
|
||||
{this.props.settings.members && (
|
||||
<MemberList column>
|
||||
{this.props.settings.members.map(member => (
|
||||
<Member auto justify="space-between">
|
||||
<Member key={member.id} justify="space-between" auto>
|
||||
<Flex>
|
||||
<Avatar src={member.avatarUrl} />
|
||||
<UserName>
|
||||
|
@ -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;
|
||||
|
@ -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)`
|
||||
|
Reference in New Issue
Block a user