slate/components/core/ApplicationHeader.js

423 lines
12 KiB
JavaScript
Raw Normal View History

2020-06-19 06:57:57 +03:00
import * as React from "react";
import * as Constants from "~/common/constants";
import * as SVG from "~/common/svg";
import * as Events from "~/common/custom-events";
2021-05-06 03:08:14 +03:00
import * as Styles from "~/common/styles";
2021-05-06 03:08:14 +03:00
import {
ApplicationUserControls,
ApplicationUserControlsPopup,
} from "~/components/core/ApplicationUserControls";
2020-09-29 07:39:05 +03:00
2020-11-30 08:24:22 +03:00
import { css, keyframes } from "@emotion/react";
2020-11-14 02:43:59 +03:00
import { Boundary } from "~/components/system/components/fragments/Boundary";
import { PopoverNavigation } from "~/components/system";
2021-07-07 23:50:57 +03:00
import { DarkSymbol } from "~/common/logo";
2021-05-06 03:08:14 +03:00
import { Link } from "~/components/core/Link";
import { ButtonPrimary, ButtonTertiary } from "~/components/system/components/Buttons";
2020-11-14 02:43:59 +03:00
2021-05-06 03:08:14 +03:00
const STYLES_NAV_LINKS = css`
2020-12-19 08:25:50 +03:00
display: flex;
2021-05-06 03:08:14 +03:00
flex-direction: row;
@media (max-width: ${Constants.sizes.mobile}px) {
flex-direction: column;
overflow: hidden;
}
2020-12-19 08:25:50 +03:00
`;
2021-05-06 03:08:14 +03:00
const STYLES_NAV_LINK = css`
2021-07-07 22:24:01 +03:00
color: ${Constants.semantic.textGray};
2021-05-06 03:08:14 +03:00
text-decoration: none;
transition: 200ms ease color;
display: block;
cursor: pointer;
2021-05-06 03:08:14 +03:00
padding: 4px 24px;
font-size: ${Constants.typescale.lvl1};
:hover {
2021-07-07 22:14:51 +03:00
color: ${Constants.system.blue};
}
2021-05-06 03:08:14 +03:00
@media (max-width: ${Constants.sizes.mobile}px) {
border-bottom: 1px solid ${Constants.system.grayLight2};
margin: 0px 24px;
padding: 12px 0px;
2021-07-07 23:50:57 +03:00
${Styles.P2};
2021-05-06 03:08:14 +03:00
}
`;
2021-05-27 11:20:34 +03:00
const STYLES_APPLICATION_HEADER_CONTAINER = (theme) => css`
2020-11-14 02:43:59 +03:00
width: 100%;
2021-05-27 11:20:34 +03:00
background-color: ${theme.system.white};
box-shadow: 0 0 0 1px ${theme.semantic.bgGrayLight};
2020-10-29 01:36:38 +03:00
@supports ((-webkit-backdrop-filter: blur(25px)) or (backdrop-filter: blur(25px))) {
-webkit-backdrop-filter: blur(25px);
backdrop-filter: blur(25px);
2021-05-06 03:08:14 +03:00
background-color: rgba(255, 255, 255, 0.7);
}
2021-05-06 03:08:14 +03:00
`;
const STYLES_APPLICATION_HEADER = css`
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
${"" /* justify-content: space-between; */}
width: 100%;
2021-07-07 23:50:57 +03:00
height: ${Constants.sizes.header}px;
2021-05-06 03:08:14 +03:00
${"" /* padding: 0 24px 0 16px; */}
padding: 0px 32px;
@media (max-width: ${Constants.sizes.mobile}px) {
2021-05-06 03:08:14 +03:00
padding: 0px 24px;
2020-09-29 07:39:05 +03:00
width: 100%;
}
`;
const STYLES_LEFT = css`
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: flex-start;
`;
const STYLES_MIDDLE = css`
min-width: 10%;
width: 100%;
2021-05-06 03:08:14 +03:00
padding: 0 24px;
display: flex;
justify-content: center;
2020-09-29 07:39:05 +03:00
`;
const STYLES_RIGHT = css`
2021-05-06 03:08:14 +03:00
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: flex-end;
`;
2021-05-06 03:08:14 +03:00
const STYLES_BACKGROUND = css`
position: absolute;
width: 100vw;
height: 100vh;
2021-07-07 22:58:14 +03:00
background-color: ${Constants.semantic.bgBlurDark6};
2021-05-06 03:08:14 +03:00
pointer-events: auto;
2020-09-06 04:57:39 +03:00
2021-05-06 03:08:14 +03:00
@keyframes fade-in {
from {
opacity: 50%;
}
to {
opacity: 100%;
}
}
animation: fade-in 200ms ease-out;
2020-09-06 04:57:39 +03:00
`;
2021-07-07 23:50:57 +03:00
const STYLES_UPLOAD_BUTTON = css`
2021-07-08 00:44:13 +03:00
background-color: ${Constants.semantic.bgGrayLight};
2021-07-07 23:50:57 +03:00
border-radius: 8px;
width: 24px;
height: 24px;
cursor: pointer;
pointer-events: auto;
${Styles.CONTAINER_CENTERED};
`;
export default class ApplicationHeader extends React.Component {
2020-10-29 01:36:38 +03:00
keysPressed = {};
searchModKey = this.props.isMac ? (
<SVG.MacCommand height="12px" style={{ display: "block", paddingLeft: 8, paddingRight: 8 }} />
) : (
<span style={{ display: "block", paddingLeft: 8, paddingRight: 8 }}>Ctrl</span>
);
2020-10-29 01:36:38 +03:00
2020-09-06 04:57:39 +03:00
state = {
2021-05-06 03:08:14 +03:00
showDropdown: false,
popup: null,
2020-09-06 04:57:39 +03:00
isRefreshing: false,
};
2020-10-29 01:36:38 +03:00
componentDidMount = () => {
window.addEventListener("keydown", this._handleKeyDown);
window.addEventListener("keyup", this._handleKeyUp);
};
_handleKeyDown = (e) => {
let prevValue = this.keysPressed[e.key];
2020-11-14 02:53:23 +03:00
if (prevValue) {
return;
}
2020-10-29 01:36:38 +03:00
this.keysPressed[e.key] = true;
2020-11-14 02:53:23 +03:00
if ((this.keysPressed["Control"] || this.keysPressed["Meta"]) && this.keysPressed["f"]) {
2020-10-29 01:36:38 +03:00
e.preventDefault();
e.stopPropagation();
this._handleCreateSearch();
}
};
_handleKeyUp = (e) => {
this.keysPressed = {};
};
2020-08-25 02:24:29 +03:00
_handleCreateSearch = (e) => {
2021-05-06 03:08:14 +03:00
this.setState({ showDropdown: false });
Events.dispatchCustomEvent({
2020-11-10 00:20:38 +03:00
name: "show-search",
detail: {},
2020-09-06 04:57:39 +03:00
});
};
_handleTogglePopup = (value) => {
if (!value || this.state.popup === value) {
this.setState({ popup: null });
} else {
2021-05-17 23:20:55 +03:00
this.setState({ popup: value, showDropdown: false });
}
};
render() {
2021-05-06 03:08:14 +03:00
const navigation = this.props.navigation.filter((item) => item.mainNav);
2021-05-06 03:08:14 +03:00
if (!this.props.viewer) {
const searchComponent = (
<div
onClick={this._handleCreateSearch}
css={Styles.HORIZONTAL_CONTAINER_CENTERED}
style={{ border: "none", pointerEvents: "auto", cursor: "pointer", paddingLeft: 12 }}
2021-05-06 03:08:14 +03:00
>
<SVG.Search
height="16px"
2021-07-07 22:24:01 +03:00
style={{ color: Constants.semantic.textGrayDark, marginRight: 8 }}
2021-05-06 03:08:14 +03:00
/>
2021-07-07 23:50:57 +03:00
<span css={Styles.P2} style={{ color: Constants.semantic.textGray }}>
2021-05-06 03:08:14 +03:00
Search Slate...
2020-09-29 07:39:05 +03:00
</span>
2021-05-06 03:08:14 +03:00
</div>
);
//NOTE(martina): signed out view
return (
<header css={STYLES_APPLICATION_HEADER_CONTAINER}>
<div css={STYLES_APPLICATION_HEADER}>
<div css={STYLES_LEFT}>
2021-07-27 15:01:44 +03:00
<Link onAction={this.props.onAction} href="/_/data" style={{ pointerEvents: "auto" }}>
2021-07-07 23:50:57 +03:00
<DarkSymbol style={{ height: 24, display: "block" }} />
</Link>
2021-05-06 03:08:14 +03:00
<div css={Styles.MOBILE_ONLY}>{searchComponent}</div>
</div>
<div css={STYLES_MIDDLE}>
<span css={Styles.MOBILE_HIDDEN}>{searchComponent}</span>
</div>
<div css={STYLES_RIGHT}>
<Link
href="/_/auth?tab=signin"
onAction={this.props.onAction}
style={{ pointerEvents: "auto" }}
>
<span css={Styles.MOBILE_HIDDEN}>
<ButtonTertiary
style={{
padding: "0px 12px",
minHeight: "30px",
fontFamily: Constants.font.text,
marginRight: 8,
}}
>
Sign in
</ButtonTertiary>
</span>
</Link>
<Link
href="/_/auth?tab=signup"
onAction={this.props.onAction}
style={{ pointerEvents: "auto" }}
>
<ButtonPrimary
style={{
padding: "0px 12px",
minHeight: "30px",
fontFamily: Constants.font.text,
}}
>
Sign up
</ButtonPrimary>
</Link>
</div>
</div>
</header>
);
}
const mobilePopup = (
// <Boundary
// captureResize={false}
// captureScroll={false}
// enabled={this.state.popup === "profile"}
// onOutsideRectEvent={(e) => {
// e.stopPropagation();
// e.preventDefault();
// this._handleTogglePopup(e);
// }}
// >
<>
<ApplicationUserControlsPopup
popup={this.state.popup}
onTogglePopup={this._handleTogglePopup}
viewer={this.props.viewer}
onAction={this.props.onAction}
style={{ pointerEvents: "auto", paddingBottom: 16 }}
/>
<div css={STYLES_BACKGROUND} />
</>
// </Boundary>
);
const mobileDropdown = (
<>
<Boundary
captureResize={false}
captureScroll={false}
enabled={this.state.showDropdown}
onOutsideRectEvent={(e) => {
e.stopPropagation();
e.preventDefault();
this.setState({ showDropdown: false });
}}
>
<div css={STYLES_NAV_LINKS} style={{ pointerEvents: "auto", paddingBottom: 16 }}>
{this.props.navigation
.filter((item) => item.mainNav)
.map((item) => (
<Link
key={item.id}
href={item.pathname}
onAction={this.props.onAction}
onClick={() => this.setState({ showDropdown: false })}
>
<div
css={STYLES_NAV_LINK}
style={{
color: this.props.activePage === item.id ? Constants.system.black : null,
}}
>
{item.name}
</div>
</Link>
))}
2020-12-19 08:25:50 +03:00
<div
2021-05-06 03:08:14 +03:00
onClick={this._handleCreateSearch}
css={STYLES_NAV_LINK}
style={{ border: "none" }}
2020-11-14 02:53:23 +03:00
>
2021-05-06 03:08:14 +03:00
Search
</div>
</div>
</Boundary>
<div css={STYLES_BACKGROUND} />
</>
);
return (
<>
<div style={{ width: "100vw", height: "100vh", position: "absolute" }} />
<header css={STYLES_APPLICATION_HEADER_CONTAINER}>
<span css={Styles.MOBILE_HIDDEN}>
<div css={STYLES_APPLICATION_HEADER}>
<div css={STYLES_LEFT}>
2021-07-07 23:50:57 +03:00
<Link
onAction={this.props.onAction}
2021-07-27 15:01:44 +03:00
href="/_/data"
2021-07-07 23:50:57 +03:00
style={{ pointerEvents: "auto" }}
>
<DarkSymbol style={{ height: 24, display: "block" }} />
</Link>
<div
css={STYLES_UPLOAD_BUTTON}
onClick={() => {
this.props.onAction({
type: "SIDEBAR",
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
});
}}
style={{ marginRight: 24, marginLeft: 24 }}
>
<SVG.Plus height="16px" />
</div>
2021-05-06 03:08:14 +03:00
</div>
<div css={STYLES_MIDDLE}>
<div css={STYLES_NAV_LINKS} style={{ pointerEvents: "auto" }}>
{navigation.map((item, i) => (
<Link key={item.id} href={item.pathname} onAction={this.props.onAction}>
<div
css={STYLES_NAV_LINK}
style={{
color: this.props.activePage === item.id ? Constants.system.black : null,
}}
>
{item.name}
</div>
</Link>
))}
<div onClick={this._handleCreateSearch} css={STYLES_NAV_LINK}>
Search
</div>
</div>
</div>
<div css={STYLES_RIGHT}>
<span style={{ pointerEvents: "auto", marginLeft: 24 }}>
<ApplicationUserControls
popup={this.state.popup}
onTogglePopup={this._handleTogglePopup}
viewer={this.props.viewer}
onAction={this.props.onAction}
/>
</span>
2020-12-19 08:25:50 +03:00
</div>
</div>
2020-10-29 11:43:08 +03:00
</span>
2021-05-06 03:08:14 +03:00
<span css={Styles.MOBILE_ONLY}>
<div css={STYLES_APPLICATION_HEADER}>
<div css={STYLES_LEFT}>
<div
css={Styles.ICON_CONTAINER}
style={{ pointerEvents: "auto" }}
2021-05-17 23:20:55 +03:00
onClick={() =>
this.setState({ showDropdown: !this.state.showDropdown, popup: null })
}
2021-05-06 03:08:14 +03:00
>
<SVG.MenuMinimal height="16px" />
</div>
</div>
<div css={STYLES_MIDDLE}>
2021-07-07 23:50:57 +03:00
<Link
onAction={this.props.onAction}
2021-07-27 15:01:44 +03:00
href="/_/data"
2021-07-07 23:50:57 +03:00
style={{ pointerEvents: "auto" }}
>
<DarkSymbol style={{ height: 24, display: "block" }} />
</Link>
2021-05-06 03:08:14 +03:00
</div>
<div css={STYLES_RIGHT}>
<span style={{ pointerEvents: "auto", marginLeft: 24 }}>
<ApplicationUserControls
popup={false}
onTogglePopup={this._handleTogglePopup}
viewer={this.props.viewer}
onAction={this.props.onAction}
/>
</span>
</div>
</div>
{this.state.popup === "profile"
? mobilePopup
: this.state.showDropdown
? mobileDropdown
: null}
2020-09-16 23:39:35 +03:00
</span>
2021-05-06 03:08:14 +03:00
</header>
</>
);
}
}