Mobile menu support

This commit is contained in:
Chen-I Lim 2020-12-02 16:54:06 -08:00
parent 96428c88f3
commit a8801c0525
13 changed files with 186 additions and 63 deletions

View file

@ -46,6 +46,9 @@
}
.comment-text * {
user-select: text;
-webkit-user-select: text; /* Chrome all / Safari all */
-moz-user-select: text; /* Firefox all */
-ms-user-select: text; /* IE 10+ */
user-select: text; /* Likely future */
}
}

View file

@ -26,6 +26,11 @@
margin: 72px auto;
max-width: 975px;
height: calc(100% - 144px);
.hideOnWidescreen {
/* Hide controls (e.g. close button) on larger screens */
display: none !important;
}
}
@media screen and (max-width: 975px) {
position: fixed;
@ -64,12 +69,5 @@
> .content.fullwidth {
padding: 10px 0 10px 0;
}
.hideOnWidescreen {
/* Hide controls (e.g. close button) on larger screens */
@media not screen and (max-width: 975px) {
display: none;
}
}
}
}

View file

@ -4,12 +4,12 @@
left: -200px;
z-index: 10;
min-width: 420px;
min-width: 430px;
box-shadow: rgba(var(--main-fg), 0.1) 0px 0px 0px 1px, rgba(var(--main-fg), 0.1) 0px 2px 4px;
background-color: rgb(var(--main-bg));
padding: 10px;
@media screen and (max-width: 420px) {
@media screen and (max-width: 430px) {
position: fixed;
top: 0;
left: 0;
@ -20,7 +20,7 @@
.hideOnWidescreen {
/* Hide controls (e.g. close button) on larger screens */
@media not screen and (max-width: 420px) {
@media not screen and (max-width: 430px) {
display: none !important;
}
}

View file

@ -9,8 +9,11 @@
* {
box-sizing: border-box;
outline: 0;
user-select: none;
outline: 0;
-webkit-user-select: none; /* Chrome all / Safari all */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none; /* Likely future */
}
html, body {

View file

@ -1,5 +1,6 @@
.Button {
display: flex;
flex: 0 0 auto;
align-items: center;
text-align: center;
justify-content: center;

View file

@ -23,8 +23,8 @@ export default class ColorOption extends React.PureComponent<ColorOptionProps> {
className='MenuOption ColorOption menu-option'
onClick={this.handleOnClick}
>
{icon ?? <div className='noicon'/>}
<div className='menu-name'>{name}</div>
{icon}
<div className={`menu-colorbox ${id}`}/>
</div>
)

View file

@ -13,8 +13,9 @@ export default class LabelOption extends React.PureComponent<LabelOptionProps> {
public render(): JSX.Element {
return (
<div className='MenuOption LabelOption menu-option'>
{this.props.icon ?? <div className='noicon'/>}
<div className='menu-name'>{this.props.children}</div>
{this.props.icon}
<div className='noicon'/>
</div>
)
}

View file

@ -1,4 +1,6 @@
.Menu {
display: flex;
flex-direction: column;
position: absolute;
z-index: 15;
min-width: 180px;
@ -8,11 +10,9 @@
border-radius: 3px;
box-shadow: rgba(var(--main-fg), 0.05) 0px 0px 0px 1px, rgba(var(--main-fg), 0.1) 0px 3px 6px, rgba(var(--main-fg), 0.2) 0px 9px 24px;
&.top {
bottom: 100%;
}
&.left {
right: 0;
.menu-contents {
display: flex;
flex-direction: column;
}
.menu-options {
@ -20,47 +20,118 @@
flex-direction: column;
flex-grow: 1;
position: relative;
list-style: none;
padding: 0;
margin: 0;
color: rgb(var(--main-fg));
>.menu-option {
display: flex;
flex-direction: row;
align-items: center;
font-weight: 400;
padding: 2px 10px;
cursor: pointer;
&:hover {
background: rgba(90, 90, 90, 0.1);
}
>* {
margin-left: 5px;
}
>*:first-child {
margin-left: 0;
}
>.menu-name {
display: flex;
flex-grow: 1;
white-space: nowrap;
}
>.SubmenuTriangleIcon {
fill: rgba(var(--main-fg), 0.7);
}
>.Icon {
width: 16px;
height: 16px;
}
>.IconButton .Icon {
margin-right: 0;
}
}
}
.menu-option {
display: flex;
flex-direction: row;
align-items: center;
.menu-spacer {
height: 20px;
width: 20px;
flex: 0 0 auto;
}
}
font-weight: 400;
padding: 2px 10px;
cursor: pointer;
touch-action: none;
.Menu, .SubMenuOption .SubMenu {
@media screen and (max-width: 430px) {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
min-width: 0;
background-color: rgba(var(--main-fg), 0.5);
border-radius: 0;
padding: 10px;
&:hover {
background: rgba(90, 90, 90, 0.1);
display: block;
overflow-y: auto;
.menu-contents {
justify-content: flex-end;
min-height: 100%;
}
.menu-name {
display: flex;
flex-grow: 1;
white-space: nowrap;
margin-right: 20px;
.menu-options {
align-items: center;
border-radius: 10px;
overflow: hidden;
flex: 0 0 auto;
>.menu-option {
min-height: 44px;
width: 100%;
padding: 0 20px;
background-color: rgb(var(--main-bg));
>* {
flex: 0 0 auto;
}
>.noicon {
width: 16px;
height: 16px;
}
>.menu-name {
font-size: 16px;
justify-content: center;
}
}
}
}
@media not screen and (max-width: 430px) {
&.top {
bottom: 100%;
}
&.left {
right: 0;
}
.SubmenuTriangleIcon {
fill: rgba(var(--main-fg), 0.7);
}
.Icon {
width: 16px;
height: 16px;
margin-right: 5px;
}
.IconButton .Icon {
margin-right: 0;
.hideOnWidescreen {
/* Hide controls (e.g. close button) on larger screens */
display: none !important;
}
}
}

View file

@ -11,12 +11,12 @@ import LabelOption from './labelOption'
import './menu.scss'
type MenuProps = {
type Props = {
children: React.ReactNode
position?: 'top'|'bottom'|'left'
}
export default class Menu extends React.PureComponent<MenuProps> {
export default class Menu extends React.PureComponent<Props> {
static Color = ColorOption
static SubMenu = SubMenuOption
static Switch = SwitchOption
@ -28,10 +28,27 @@ export default class Menu extends React.PureComponent<MenuProps> {
const {position, children} = this.props
return (
<div className={'Menu noselect ' + (position || 'bottom')}>
<div className='menu-options'>
{children}
<div className='menu-contents'>
<div className='menu-options'>
{children}
</div>
<div className='menu-spacer hideOnWidescreen'/>
<div className='menu-options hideOnWidescreen'>
<Menu.Text
id='menu-cancel'
name={'Cancel'}
className='menu-cancel'
onClick={this.onCancel}
/>
</div>
</div>
</div>
)
}
private onCancel = () => {
// No need to do anything, as click bubbled up to MenuWrapper, which closes
}
}

View file

@ -3,10 +3,11 @@
.SubMenu {
position: absolute;
z-index: 15;
z-index: 16;
min-width: 180px;
background-color: rgb(var(--main-bg));
color: rgb(var(--main-fg));
margin: 0 !important;
border-radius: 3px;
box-shadow: rgba(var(--main-fg), 0.05) 0px 0px 0px 1px, rgba(var(--main-fg), 0.1) 0px 3px 6px, rgba(var(--main-fg), 0.2) 0px 9px 24px;
@ -19,7 +20,9 @@
}
}
.SubmenuTriangleIcon {
float: 'right'
@media screen and (max-width: 430px) {
.SubMenu {
background-color: rgba(var(--main-fg), 0.8) !important;
}
}
}

View file

@ -4,6 +4,8 @@ import React from 'react'
import SubmenuTriangleIcon from '../icons/submenuTriangle'
import Menu from '.'
import './subMenuOption.scss'
type SubMenuOptionProps = {
@ -23,7 +25,9 @@ export default class SubMenuOption extends React.PureComponent<SubMenuOptionProp
}
private handleMouseEnter = (): void => {
this.setState({isOpen: true})
setTimeout(() => {
this.setState({isOpen: true})
}, 50)
}
private close = (): void => {
@ -37,17 +41,34 @@ export default class SubMenuOption extends React.PureComponent<SubMenuOptionProp
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.close}
>
{this.props.icon}
{this.props.icon ?? <div className='noicon'/>}
<div className='menu-name'>{this.props.name}</div>
<SubmenuTriangleIcon/>
{this.state.isOpen &&
<div className={'SubMenu menu noselect ' + (this.props.position || 'bottom')}>
<div className='menu-options'>
{this.props.children}
<div className={'SubMenu Menu noselect ' + (this.props.position || 'bottom')}>
<div className='menu-contents'>
<div className='menu-options'>
{this.props.children}
</div>
<div className='menu-spacer hideOnWidescreen'/>
<div className='menu-options hideOnWidescreen'>
<Menu.Text
id='menu-cancel'
name={'Cancel'}
className='menu-cancel'
onClick={this.onCancel}
/>
</div>
</div>
</div>
}
</div>
)
}
private onCancel = () => {
// No need to do anything, as click bubbled up to MenuWrapper, which closes
}
}

View file

@ -24,8 +24,8 @@ export default class SwitchOption extends React.PureComponent<SwitchOptionProps>
className='MenuOption SwitchOption menu-option'
onClick={this.handleOnClick}
>
{icon ?? <div className='noicon'/>}
<div className='menu-name'>{name}</div>
{icon}
<Switch
isOn={isOn}
onChanged={() => {}}

View file

@ -7,6 +7,7 @@ import {MenuOptionProps} from './menuItem'
type TextOptionProps = MenuOptionProps & {
icon?: React.ReactNode,
rightIcon?: React.ReactNode,
className?: string
}
export default class TextOption extends React.PureComponent<TextOptionProps> {
@ -17,14 +18,18 @@ export default class TextOption extends React.PureComponent<TextOptionProps> {
public render(): JSX.Element {
const {name, icon, rightIcon} = this.props
let className = 'MenuOption TextOption menu-option'
if (this.props.className) {
className += ' ' + this.props.className
}
return (
<div
className='MenuOption TextOption menu-option'
className={className}
onClick={this.handleOnClick}
>
{icon}
{icon ?? <div className='noicon'/>}
<div className='menu-name'>{name}</div>
{rightIcon}
{rightIcon ?? <div className='noicon'/>}
</div>
)
}