mobile menu: uses new tooltip wrapper

This commit is contained in:
jimmylee 2020-08-14 01:29:29 -07:00
parent 607c4b91ea
commit 8465d8a481
6 changed files with 127 additions and 168 deletions

View File

@ -2,8 +2,8 @@ import * as React from "react";
import * as Constants from "~/common/constants";
import * as OldSVG from "~/components/system/svg";
import { TooltipWrapper, dispatchCustomEvent, PopoverNavigation } from "~/components/system";
import { css } from "@emotion/react";
import { Tooltip } from "react-tippy";
import Dismissible from "~/components/core/Dismissible";
import CircleButtonLight from "~/components/core/CircleButtonLight";
@ -12,34 +12,79 @@ const STYLES_ANCHOR = css`
position: relative;
`;
const APPLICATION_CONTROL_MENU_ID = "application-control-menu";
export default class ApplicationControlMenu extends React.Component {
state = {};
state = { visible: false };
_handleClick = (e) => {
if (this.props.popover) {
this.setState({ visible: !this.state.visible });
}
this.setState({ visible: !this.state.visible });
if (this.props.onClick) {
this.props.onClick(e);
}
dispatchCustomEvent({
name: "show-tooltip",
detail: {
id: APPLICATION_CONTROL_MENU_ID,
},
});
};
_handleHide = () => {
this.setState({ visible: false });
return dispatchCustomEvent({
name: "hide-tooltip",
detail: {
id: APPLICATION_CONTROL_MENU_ID,
},
});
};
_handleNavigateTo = (data) => {
this._handleHide();
return this.props.onNavigateTo(data);
};
_handleAction = (data) => {
this._handleHide();
return this.props.onAction(data);
};
_handleSignOut = (data) => {
this._handleHide();
return this.props.onSignOut(data);
};
render() {
const title = this.state.visible ? "Close menu" : "Open settings menu";
return (
<Dismissible
css={STYLES_ANCHOR}
captureResize={false}
captureScroll={true}
enabled={this.state.visible}
onOutsideRectEvent={this._handleHide}
style={this.props.style}>
<TooltipWrapper
id={APPLICATION_CONTROL_MENU_ID}
type="navigation"
horizontal="right"
vertical="below"
content={
<Dismissible
css={STYLES_ANCHOR}
captureResize={true}
captureScroll={false}
enabled
onOutsideRectEvent={this._handleHide}
style={this.props.style}>
<PopoverNavigation
style={{
left: 0,
top: "24px",
cursor: "pointer",
}}
onNavigateTo={this._handleNavigateTo}
onAction={this._handleAction}
onSignOut={this._handleSignOut}
navigation={[
{ text: "Profile & account settings", value: 13 },
{ text: "Filecoin settings", value: 14 },
{ text: "Sign out", value: 0, action: "SIGN_OUT" },
]}
/>
</Dismissible>
}>
<CircleButtonLight
onClick={this._handleClick}
style={{
@ -48,9 +93,7 @@ export default class ApplicationControlMenu extends React.Component {
}}>
<OldSVG.ChevronDown height="20px" />
</CircleButtonLight>
{this.state.visible ? this.props.popover : null}
</Dismissible>
</TooltipWrapper>
);
}
}

View File

@ -21,6 +21,23 @@ const STYLES_SCROLL = css`
}
`;
const STYLES_NO_VISIBLE_SCROLL = css`
-webkit-overflow-scrolling: touch;
overflow-y: scroll;
scrollbar-width: none;
-ms-overflow-style: -ms-autohiding-scrollbar;
::-webkit-scrollbar {
width: 0px;
display: none;
}
::-webkit-scrollbar-track {
background: ${Constants.system.foreground};
}
::-webkit-scrollbar-thumb {
background: ${Constants.system.darkGray};
}
`;
const STYLES_LAYOUT = css`
display: flex;
align-items: flex-start;
@ -74,12 +91,13 @@ const STYLES_BODY_MOBILE = css`
`;
const STYLES_NAVIGATION = css`
z-index: 1;
flex-shrink: 0;
height: 100vh;
z-index: ${Constants.zindex.navigation};
width: ${Constants.sizes.navigation}px;
border-right: 1px solid ${Constants.system.border};
${STYLES_SCROLL}
${STYLES_NO_VISIBLE_SCROLL}
@media (max-width: ${Constants.sizes.mobile}px) {
width: auto;
}
@ -130,16 +148,13 @@ export default class ApplicationLayout extends React.Component {
if (this.props.sidebar) {
sidebarElements = (
<React.Fragment>
<GlobalTooltip elementRef={this._sidebar} allowedTypes={["sidebar"]} />
<div css={STYLES_SIDEBAR_HEADER}>
<div css={STYLES_BLOCK} onClick={this.props.onDismissSidebar}>
<SVG.Dismiss height="24px" />
</div>
</div>
<div css={STYLES_SIDEBAR_CONTENT}>{this.props.sidebar}</div>
<GlobalTooltip
elementRef={this._sidebar}
allowedTypes={["sidebar"]}
/>
</React.Fragment>
);
}
@ -149,36 +164,28 @@ export default class ApplicationLayout extends React.Component {
css={STYLES_NAVIGATION}
ref={(c) => {
this._navigation = c;
}}
>
}}>
<GlobalTooltip elementRef={this._navigation} allowedTypes={["navigation"]} />
{this.props.navigation}
<GlobalTooltip
elementRef={this._navigation}
allowedTypes={["navigation"]}
/>
</div>
<div css={STYLES_CONTENT}>
<GlobalTooltip elementRef={this._body} allowedTypes={["body"]} />
<div css={STYLES_HEADER}>{this.props.header}</div>
<div
css={STYLES_BODY_WEB}
ref={(c) => {
this._body = c;
}}
>
}}>
{this.props.children}
</div>
<div css={STYLES_BODY_MOBILE}>
{this.props.sidebar ? sidebarElements : this.props.children}
</div>
<GlobalTooltip elementRef={this._body} allowedTypes={["body"]} />
<div css={STYLES_BODY_MOBILE}>{this.props.sidebar ? sidebarElements : this.props.children}</div>
</div>
{this.props.sidebar ? (
<div
css={STYLES_SIDEBAR_WEB}
ref={(c) => {
this._sidebar = c;
}}
>
}}>
{sidebarElements}
</div>
) : null}

View File

@ -152,34 +152,17 @@ const STYLES_ICON_ELEMENT = css`
}
`;
const Item = ({
data,
id,
activeIds,
level,
children,
showActive,
decorator,
onToggleShow,
onNavigateTo,
}) => {
const Item = ({ data, id, activeIds, level, children, showActive, decorator, onToggleShow, onNavigateTo }) => {
return (
<span
css={STYLES_NAVIGATION_ITEM}
style={{ padding: `0 0 0 ${level * 16}px` }}
>
<span css={STYLES_NAVIGATION_ITEM} style={{ padding: `0 0 0 ${level * 16}px` }}>
<span css={STYLES_EXPANDER} onClick={onToggleShow ? onToggleShow : null}>
<span
css={STYLES_ICON_ELEMENT}
style={{
color: activeIds[id] ? Constants.system.brand : null,
}}
>
}}>
{onToggleShow ? (
<SVG.ExpandArrow
height="8px"
style={showActive ? { transform: `rotate(90deg)` } : null}
/>
<SVG.ExpandArrow height="8px" style={showActive ? { transform: `rotate(90deg)` } : null} />
) : null}
</span>
</span>
@ -188,8 +171,7 @@ const Item = ({
css={STYLES_ICON_ELEMENT}
style={{
color: activeIds[id] ? Constants.system.brand : null,
}}
>
}}>
{IconMap[decorator]}
</span>
</span>
@ -198,8 +180,7 @@ const Item = ({
onClick={() => onNavigateTo({ id }, data)}
style={{
color: activeIds[id] ? Constants.system.brand : null,
}}
>
}}>
{children}
</span>
</span>
@ -216,17 +197,7 @@ class NodeReference extends React.Component {
};
render() {
const {
id,
activeId,
activeIds,
level,
children,
treeChildren,
decorator,
onNavigateTo,
data,
} = this.props;
const { id, activeId, activeIds, level, children, treeChildren, decorator, onNavigateTo, data } = this.props;
const { showTreeChildren } = this.state;
let showActive = showTreeChildren || activeIds[id];
@ -244,10 +215,7 @@ class NodeReference extends React.Component {
treeChildren={treeChildren}
onNavigateTo={onNavigateTo}
decorator={decorator}
onToggleShow={
treeChildren && treeChildren.length ? this._handleToggleShow : null
}
>
onToggleShow={treeChildren && treeChildren.length ? this._handleToggleShow : null}>
{children}
</Item>
@ -271,8 +239,7 @@ class NodeReference extends React.Component {
onNavigateTo={onNavigateTo}
level={level + 1}
treeChildren={child.children}
decorator={child.decorator}
>
decorator={child.decorator}>
{child.name}
</NodeReference>
);
@ -289,31 +256,12 @@ export default class ApplicationNavigation extends React.Component {
<nav css={STYLES_NAVIGATION}>
<div css={STYLES_NAVIGATION_HEADER}>
<ApplicationControlMenu
popover={
<System.PopoverNavigation
style={{
left: 0,
top: "48px",
cursor: "pointer",
}}
onNavigateTo={this.props.onNavigateTo}
onAction={this.props.onAction}
onSignOut={this.props.onSignOut}
navigation={[
{ text: "Profile & account settings", value: 13 },
{ text: "Filecoin settings", value: 14 },
{ text: "Sign out", value: 0, action: "SIGN_OUT" },
]}
/>
}
onNavigateTo={this.props.onNavigateTo}
onAction={this.props.onAction}
onSignOut={this.props.onSignOut}
/>
<a
css={STYLES_PROFILE}
style={{ marginLeft: 16 }}
href={`/${this.props.viewer.username}`}
target="_blank"
>
<a css={STYLES_PROFILE} style={{ marginLeft: 16 }} href={`/${this.props.viewer.username}`} target="_blank">
<span
css={STYLES_PROFILE_IMAGE}
style={{
@ -342,8 +290,7 @@ export default class ApplicationNavigation extends React.Component {
onNavigateTo={this.props.onNavigateTo}
level={0}
treeChildren={each.children}
decorator={each.decorator}
>
decorator={each.decorator}>
{each.name}
</NodeReference>
);

View File

@ -105,8 +105,7 @@ export default class WebRectBoundary extends React.PureComponent {
this._root = c;
}}
style={this.props.style}
onClick={this.props.onClick}
>
onClick={this.props.onClick}>
{this.props.children}
</div>
);

View File

@ -13,8 +13,7 @@ const STYLES_POPOVER = css`
user-select: none;
background-color: ${Constants.system.white};
color: ${Constants.system.pitchBlack};
box-shadow: inset 0 0 0 1px ${Constants.system.border},
0 1px 4px rgba(0, 0, 0, 0.07);
box-shadow: inset 0 0 0 1px ${Constants.system.border}, 0 1px 4px rgba(0, 0, 0, 0.07);
`;
const STYLES_POPOVER_ITEM = css`
@ -51,22 +50,14 @@ export class PopoverNavigation extends React.Component {
{this.props.navigation.map((each) => {
if (each.action === "SIGN_OUT") {
return (
<div
key={each.value}
css={STYLES_POPOVER_ITEM}
onClick={this.props.onSignOut}
>
<div key={each.value} css={STYLES_POPOVER_ITEM} onClick={this.props.onSignOut}>
{each.text}
</div>
);
}
return (
<div
key={each.value}
css={STYLES_POPOVER_ITEM}
onClick={() => this.props.onNavigateTo({ id: each.value })}
>
<div key={each.value} css={STYLES_POPOVER_ITEM} onClick={() => this.props.onNavigateTo({ id: each.value })}>
{each.text}
</div>
);

View File

@ -33,12 +33,8 @@ export class GlobalTooltip extends React.Component {
};
getStyle = (rect, bubbleRect, vertical, horizontal) => {
let yOffset = this.props.elementRef
? this.props.elementRef.scrollTop
: window.pageYOffset;
let xOffset = this.props.elementRef
? this.props.elementRef.scrollLeft
: window.pageXOffset;
let yOffset = this.props.elementRef ? this.props.elementRef.scrollTop : window.pageYOffset;
let xOffset = this.props.elementRef ? this.props.elementRef.scrollLeft : window.pageXOffset;
let style = { position: "absolute" };
switch (vertical) {
case "above":
@ -48,9 +44,7 @@ export class GlobalTooltip extends React.Component {
style.top = `${rect.bottom - bubbleRect.height + yOffset}px`;
break;
case "center":
style.top = `${
rect.top + 0.5 * rect.height - 0.5 * bubbleRect.height + yOffset
}px`;
style.top = `${rect.top + 0.5 * rect.height - 0.5 * bubbleRect.height + yOffset}px`;
break;
case "down":
style.top = `${rect.top + yOffset}px`;
@ -67,9 +61,7 @@ export class GlobalTooltip extends React.Component {
style.left = `${rect.right - bubbleRect.width + xOffset}px`;
break;
case "center":
style.left = `${
rect.left + 0.5 * rect.width - 0.5 * bubbleRect.width + xOffset
}px`;
style.left = `${rect.left + 0.5 * rect.width - 0.5 * bubbleRect.width + xOffset}px`;
break;
case "right":
style.left = `${rect.left + xOffset}px`;
@ -82,12 +74,8 @@ export class GlobalTooltip extends React.Component {
};
getOrientation = (rect, bubbleRect, vertical, horizontal) => {
let yOffset = this.props.elementRef
? this.props.elementRef.scrollTop
: window.pageYOffset;
let xOffset = this.props.elementRef
? this.props.elementRef.scrollLeft
: window.pageXOffset;
let yOffset = this.props.elementRef ? this.props.elementRef.scrollTop : window.pageYOffset;
let xOffset = this.props.elementRef ? this.props.elementRef.scrollLeft : window.pageXOffset;
if (!vertical) {
if (bubbleRect.height > rect.top + yOffset) {
vertical = "below";
@ -116,12 +104,12 @@ export class GlobalTooltip extends React.Component {
};
_handleAdd = (e) => {
if (
this.props.allowedTypes &&
!this.props.allowedTypes.includes(e.detail.type)
)
if (this.props.allowedTypes && !this.props.allowedTypes.includes(e.detail.type)) {
return;
}
let tooltips = this.state.tooltips;
tooltips[e.detail.id] = {
id: e.detail.id,
show: false,
@ -135,11 +123,10 @@ export class GlobalTooltip extends React.Component {
};
_handleRemove = (e) => {
if (
this.props.allowedTypes &&
!this.props.allowedTypes.includes(e.detail.type)
)
if (this.props.allowedTypes && !this.props.allowedTypes.includes(e.detail.type)) {
return;
}
if (this.state.tooltips[e.detail.id]) {
let tooltips = this.state.tooltips;
delete tooltips[e.detail.id];
@ -148,11 +135,6 @@ export class GlobalTooltip extends React.Component {
};
_handleShow = (e) => {
if (
this.props.allowedTypes &&
!this.props.allowedTypes.includes(e.detail.type)
)
return;
if (this.state.tooltips[e.detail.id]) {
let tooltips = this.state.tooltips;
if (!tooltips[e.detail.id].style) {
@ -171,11 +153,6 @@ export class GlobalTooltip extends React.Component {
};
_handleHide = (e) => {
if (
this.props.allowedTypes &&
!this.props.allowedTypes.includes(e.detail.type)
)
return;
if (this.state.tooltips[e.detail.id]) {
let tooltips = this.state.tooltips;
tooltips[e.detail.id].show = false;
@ -214,8 +191,9 @@ export class TooltipWrapper extends React.Component {
sample: true,
};
componentDidMount = () => {
componentDidMount = async () => {
let bubbleRect = this._bubble.getBoundingClientRect();
dispatchCustomEvent({
name: "add-tooltip",
detail: {
@ -247,8 +225,7 @@ export class TooltipWrapper extends React.Component {
this._bubble = c;
}}
style={{ display: "inline-flex" }}
css={STYLES_INVISIBLE}
>
css={STYLES_INVISIBLE}>
{this.props.content}
</div>
) : null}
@ -256,8 +233,7 @@ export class TooltipWrapper extends React.Component {
ref={(c) => {
this._root = c;
}}
style={{ display: "inline-flex" }}
>
style={{ display: "inline-flex" }}>
{this.props.children}
</div>
</div>
@ -328,20 +304,16 @@ export class TooltipAnchor extends React.Component {
content={content}
horizontal={this.props.horizontal}
vertical={this.props.vertical}
type={this.props.type}
>
type={this.props.type}>
<span
css={STYLES_TOOLTIP_ANCHOR}
style={this.props.anchorStyle}
onMouseEnter={this._handleMouseEnter}
onMouseLeave={this._handleMouseLeave}
>
onMouseLeave={this._handleMouseLeave}>
{this.props.children ? (
this.props.children
) : (
<SVG.Information
height={this.props.height ? this.props.height : "24px"}
/>
<SVG.Information height={this.props.height ? this.props.height : "24px"} />
)}
</span>
</TooltipWrapper>