feat: Show mobile-style (slide from bottom) menus on mobile (#2025)

* feat: Show mobile-style (slide from bottom) menus at responsive viewport sizes

* More mobile improvements

* fix: Safari compatability
This commit is contained in:
Tom Moor
2021-04-13 21:43:24 -07:00
committed by GitHub
parent f37371c16e
commit 1dd97c1ddd
8 changed files with 76 additions and 21 deletions

View File

@ -3,6 +3,7 @@ import { CheckmarkIcon } from "outline-icons";
import * as React from "react"; import * as React from "react";
import { MenuItem as BaseMenuItem } from "reakit/Menu"; import { MenuItem as BaseMenuItem } from "reakit/Menu";
import styled from "styled-components"; import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
type Props = {| type Props = {|
onClick?: (SyntheticEvent<>) => void | Promise<void>, onClick?: (SyntheticEvent<>) => void | Promise<void>,
@ -72,7 +73,7 @@ export const MenuAnchor = styled.a`
display: flex; display: flex;
margin: 0; margin: 0;
border: 0; border: 0;
padding: 6px 12px; padding: 12px;
width: 100%; width: 100%;
min-height: 32px; min-height: 32px;
background: none; background: none;
@ -80,7 +81,7 @@ export const MenuAnchor = styled.a`
props.disabled ? props.theme.textTertiary : props.theme.textSecondary}; props.disabled ? props.theme.textTertiary : props.theme.textSecondary};
justify-content: left; justify-content: left;
align-items: center; align-items: center;
font-size: 15px; font-size: 16px;
cursor: default; cursor: default;
user-select: none; user-select: none;
@ -115,6 +116,11 @@ export const MenuAnchor = styled.a`
background: ${props.theme.primary}; background: ${props.theme.primary};
} }
`}; `};
${breakpoint("tablet")`
padding: 6px 12px;
font-size: 15px;
`};
`; `;
export default MenuItem; export default MenuItem;

View File

@ -1,9 +1,12 @@
// @flow // @flow
import { rgba } from "polished"; import { rgba } from "polished";
import * as React from "react"; import * as React from "react";
import { Portal } from "react-portal";
import { Menu } from "reakit/Menu"; import { Menu } from "reakit/Menu";
import styled from "styled-components"; import styled from "styled-components";
import { fadeAndScaleIn } from "shared/styles/animations"; import breakpoint from "styled-components-breakpoint";
import { fadeAndScaleIn, fadeAndSlideIn } from "shared/styles/animations";
import Fade from "components/Fade";
import usePrevious from "hooks/usePrevious"; import usePrevious from "hooks/usePrevious";
type Props = {| type Props = {|
@ -37,27 +40,60 @@ export default function ContextMenu({
}, [onOpen, onClose, previousVisible, rest.visible]); }, [onOpen, onClose, previousVisible, rest.visible]);
return ( return (
<Menu hideOnClickOutside preventBodyScroll {...rest}> <>
{(props) => ( <Menu hideOnClickOutside preventBodyScroll {...rest}>
<Position {...props}> {(props) => (
<Background> <Position {...props}>
{rest.visible || rest.animating ? children : null} <Background>
</Background> {rest.visible || rest.animating ? children : null}
</Position> </Background>
</Position>
)}
</Menu>
{(rest.visible || rest.animating) && (
<Portal>
<Fade timing="200ms">
<Backdrop />
</Fade>
</Portal>
)} )}
</Menu> </>
); );
} }
const Backdrop = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: ${(props) => props.theme.shadow};
z-index: ${(props) => props.theme.depths.menu - 1};
${breakpoint("tablet")`
display: none;
`};
`;
const Position = styled.div` const Position = styled.div`
position: absolute; position: absolute;
z-index: ${(props) => props.theme.depths.menu}; z-index: ${(props) => props.theme.depths.menu};
${breakpoint("mobile", "tablet")`
position: fixed !important;
transform: none !important;
top: auto !important;
right: 8px !important;
bottom: 8px !important;
left: 8px !important;
`};
`; `;
const Background = styled.div` const Background = styled.div`
animation: ${fadeAndScaleIn} 200ms ease; animation: ${fadeAndSlideIn} 200ms ease;
transform-origin: ${(props) => (props.left !== undefined ? "25%" : "75%")} 0; transform-origin: 50% 100%;
background: ${(props) => rgba(props.theme.menuBackground, 0.95)}; max-width: 100%;
background: ${(props) => props.theme.menuBackground};
border: ${(props) => border: ${(props) =>
props.theme.menuBorder ? `1px solid ${props.theme.menuBorder}` : "none"}; props.theme.menuBorder ? `1px solid ${props.theme.menuBorder}` : "none"};
border-radius: 6px; border-radius: 6px;
@ -66,7 +102,6 @@ const Background = styled.div`
overflow: hidden; overflow: hidden;
overflow-y: auto; overflow-y: auto;
max-height: 75vh; max-height: 75vh;
max-width: 276px;
box-shadow: ${(props) => props.theme.menuShadow}; box-shadow: ${(props) => props.theme.menuShadow};
pointer-events: all; pointer-events: all;
font-weight: normal; font-weight: normal;
@ -74,4 +109,12 @@ const Background = styled.div`
@media print { @media print {
display: none; display: none;
} }
${breakpoint("tablet")`
animation: ${fadeAndScaleIn} 200ms ease;
transform-origin: ${(props) =>
props.left !== undefined ? "25%" : "75%"} 0;
max-width: 276px;
background: ${(props) => rgba(props.theme.menuBackground, 0.95)};
`};
`; `;

View File

@ -77,7 +77,7 @@ const Actions = styled(Flex)`
const Wrapper = styled(Flex)` const Wrapper = styled(Flex)`
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 2; z-index: ${(props) => props.theme.depths.header};
background: ${(props) => transparentize(0.2, props.theme.background)}; background: ${(props) => transparentize(0.2, props.theme.background)};
padding: 12px; padding: 12px;
transition: all 100ms ease-out; transition: all 100ms ease-out;

View File

@ -211,7 +211,7 @@ const Background = styled.a`
right: 0; right: 0;
cursor: default; cursor: default;
z-index: ${(props) => props.theme.depths.sidebar - 1}; z-index: ${(props) => props.theme.depths.sidebar - 1};
background: rgba(0, 0, 0, 0.5); background: ${(props) => props.theme.sidebarShadow};
`; `;
const Container = styled(Flex)` const Container = styled(Flex)`

View File

@ -126,7 +126,7 @@ const Link = styled(NavLink)`
props.$isActiveDrop ? props.theme.slateDark : "inherit"}; props.$isActiveDrop ? props.theme.slateDark : "inherit"};
color: ${(props) => color: ${(props) =>
props.$isActiveDrop ? props.theme.white : props.theme.sidebarText}; props.$isActiveDrop ? props.theme.white : props.theme.sidebarText};
font-size: 15px; font-size: 16px;
cursor: pointer; cursor: pointer;
overflow: hidden; overflow: hidden;
@ -158,6 +158,7 @@ const Link = styled(NavLink)`
${breakpoint("tablet")` ${breakpoint("tablet")`
padding: 4px 16px; padding: 4px 16px;
font-size: 15px;
`} `}
`; `;

View File

@ -152,7 +152,6 @@ function CollectionScene() {
<CollectionMenu <CollectionMenu
collection={collection} collection={collection}
placement="bottom-end" placement="bottom-end"
modal={false}
label={(props) => ( label={(props) => (
<Button <Button
icon={<MoreIcon />} icon={<MoreIcon />}

View File

@ -4,6 +4,7 @@ import { observer } from "mobx-react";
import * as React from "react"; import * as React from "react";
import Textarea from "react-autosize-textarea"; import Textarea from "react-autosize-textarea";
import styled from "styled-components"; import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { MAX_TITLE_LENGTH } from "shared/constants"; import { MAX_TITLE_LENGTH } from "shared/constants";
import parseTitle from "shared/utils/parseTitle"; import parseTitle from "shared/utils/parseTitle";
import Document from "models/Document"; import Document from "models/Document";
@ -165,11 +166,9 @@ const StarButton = styled(Star)`
`; `;
const Title = styled(Textarea)` const Title = styled(Textarea)`
z-index: 1;
line-height: 1.25; line-height: 1.25;
margin-top: 1em; margin-top: 1em;
margin-bottom: 0.5em; margin-bottom: 0.5em;
margin-left: ${(props) => (props.$startsWithEmojiAndSpace ? "-1.2em" : 0)};
background: ${(props) => props.theme.background}; background: ${(props) => props.theme.background};
transition: ${(props) => props.theme.backgroundTransition}; transition: ${(props) => props.theme.backgroundTransition};
color: ${(props) => props.theme.text}; color: ${(props) => props.theme.text};
@ -186,6 +185,10 @@ const Title = styled(Textarea)`
-webkit-text-fill-color: ${(props) => props.theme.placeholder}; -webkit-text-fill-color: ${(props) => props.theme.placeholder};
} }
${breakpoint("tablet")`
margin-left: ${(props) => (props.$startsWithEmojiAndSpace ? "-1.2em" : 0)};
`};
${AnimatedStar} { ${AnimatedStar} {
opacity: ${(props) => (props.$isStarred ? "1 !important" : 0)}; opacity: ${(props) => (props.$isStarred ? "1 !important" : 0)};
} }

View File

@ -107,6 +107,7 @@ export const base = {
}, },
depths: { depths: {
header: 900,
sidebar: 1000, sidebar: 1000,
modalOverlay: 2000, modalOverlay: 2000,
modal: 3000, modal: 3000,
@ -133,6 +134,7 @@ export const light = {
sidebarBackground: colors.warmGrey, sidebarBackground: colors.warmGrey,
sidebarItemBackground: colors.black10, sidebarItemBackground: colors.black10,
sidebarText: "rgb(78, 92, 110)", sidebarText: "rgb(78, 92, 110)",
sidebarShadow: "rgba(0, 0, 0, 0.2)",
shadow: "rgba(0, 0, 0, 0.2)", shadow: "rgba(0, 0, 0, 0.2)",
menuBackground: colors.white, menuBackground: colors.white,
@ -192,6 +194,7 @@ export const dark = {
sidebarBackground: colors.veryDarkBlue, sidebarBackground: colors.veryDarkBlue,
sidebarItemBackground: colors.transparent, sidebarItemBackground: colors.transparent,
sidebarText: colors.slate, sidebarText: colors.slate,
sidebarShadow: "rgba(255, 255, 255, 0.07)",
shadow: "rgba(0, 0, 0, 0.6)", shadow: "rgba(0, 0, 0, 0.6)",
menuBorder: lighten(0.1, colors.almostBlack), menuBorder: lighten(0.1, colors.almostBlack),