slate/components/core/ApplicationHeader.js

279 lines
7.2 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
import { css } from "@emotion/react";
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";
import { Match, Switch } from "~/components/utility/Switch";
import { Show } from "~/components/utility/Show";
import { useForm, useMediaQuery } from "~/common/hooks";
import { Input } from "~/components/system";
import { AnimatePresence, motion } from "framer-motion";
2020-11-14 02:43:59 +03:00
const STYLES_SEARCH_COMPONENT = (theme) => css`
background-color: transparent;
border-radius: 8px;
box-shadow: none;
height: 100%;
input {
height: 100%;
padding: 0px;
}
&::placeholder {
color: ${theme.semantic.textGray};
2021-05-06 03:08:14 +03:00
}
2020-12-19 08:25:50 +03:00
`;
const STYLES_DISMISS_BUTTON = (theme) => css`
2021-05-06 03:08:14 +03:00
display: block;
${Styles.BUTTON_RESET};
color: ${theme.semantic.textGray};
`;
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`
${Styles.HORIZONTAL_CONTAINER_CENTERED};
2021-07-07 23:50:57 +03:00
height: ${Constants.sizes.header}px;
padding: 0px 24px;
@media (max-width: ${Constants.sizes.mobile}px) {
padding: 0px 16px;
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`
flex-grow: 1;
height: 100%;
padding: 0 12px;
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;
background-color: ${Constants.semantic.bgBlurDark};
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`
${Styles.CONTAINER_CENTERED};
${Styles.BUTTON_RESET};
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;
`;
export default function ApplicationHeader({ viewer, onAction }) {
const [state, setState] = React.useState({
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
const _handleTogglePopup = (value) => {
if (!value || state.popup === value) {
setState((prev) => ({ ...prev, popup: null }));
} else {
setState((prev) => ({ ...prev, popup: value, showDropdown: false }));
2020-10-29 01:36:38 +03:00
}
};
const handleUpload = React.useCallback(() => {
onAction({ type: "SIDEBAR", value: "SIDEBAR_ADD_FILE_TO_BUCKET" });
}, [onAction]);
2020-10-29 01:36:38 +03:00
const handleCreateSearch = () => {
setState((prev) => ({ ...prev, showDropdown: false }));
Events.dispatchCustomEvent({
2020-11-10 00:20:38 +03:00
name: "show-search",
detail: {},
2020-09-06 04:57:39 +03:00
});
};
const {
values: { search: searchQuery },
getFieldProps,
} = useForm({
initialValues: { search: "" },
onSubmit: handleCreateSearch,
});
const { mobile } = useMediaQuery();
const isSignedOut = !viewer;
const isSearching = searchQuery.length !== 0;
return (
<header css={STYLES_APPLICATION_HEADER_CONTAINER}>
<div css={STYLES_APPLICATION_HEADER}>
<div css={STYLES_LEFT}>
<Show
when={viewer}
fallback={
<Link onAction={onAction} href="/_/data" style={{ pointerEvents: "auto" }}>
2021-07-07 23:50:57 +03:00
<DarkSymbol style={{ height: 24, display: "block" }} />
</Link>
}
>
<ApplicationUserControls
popup={mobile ? false : state.popup}
onTogglePopup={_handleTogglePopup}
viewer={viewer}
onAction={onAction}
/>
</Show>
</div>
<div css={STYLES_MIDDLE}>
{/**TODO: update Search component */}
<Input
containerStyle={{ height: "100%" }}
full
placeholder={`Search ${!viewer ? "slate.host" : ""}`}
inputCss={STYLES_SEARCH_COMPONENT}
onSubmit={handleCreateSearch}
{...getFieldProps("search")}
/>
</div>
<div css={STYLES_RIGHT}>
<Actions
isSearching={isSearching}
isSignedOut={isSignedOut}
onAction={onAction}
onUpload={handleUpload}
/>
</div>
</div>
<Show when={mobile && state.popup === "profile"}>
2021-05-06 03:08:14 +03:00
<ApplicationUserControlsPopup
popup={state.popup}
onTogglePopup={_handleTogglePopup}
viewer={viewer}
onAction={onAction}
style={{ pointerEvents: "auto" }}
2021-05-06 03:08:14 +03:00
/>
<div css={STYLES_BACKGROUND} />
</Show>
</header>
);
}
2021-05-06 03:08:14 +03:00
const Actions = ({ isSignedOut, isSearching, onAction, onUpload, onDismissSearch }) => {
const authActions = React.useMemo(
() => (
2021-05-06 03:08:14 +03:00
<>
<Link href="/_/auth?tab=signin" onAction={onAction} style={{ pointerEvents: "auto" }}>
2021-05-06 03:08:14 +03:00
<span css={Styles.MOBILE_HIDDEN}>
<ButtonTertiary
style={{
padding: "0px 12px",
minHeight: "30px",
fontFamily: Constants.font.text,
marginRight: 8,
}}
>
Sign in
</ButtonTertiary>
2020-09-16 23:39:35 +03:00
</span>
</Link>
<Link href="/_/auth?tab=signup" onAction={onAction} style={{ pointerEvents: "auto" }}>
<ButtonPrimary
style={{ padding: "0px 12px", minHeight: "30px", fontFamily: Constants.font.text }}
>
Sign up
</ButtonPrimary>
</Link>
2021-05-06 03:08:14 +03:00
</>
),
[onAction]
);
const uploadAction = React.useMemo(
() => (
<button css={STYLES_UPLOAD_BUTTON} onClick={onUpload}>
<SVG.Plus height="16px" />
</button>
),
[onUpload]
);
return (
<AnimatePresence>
<Switch
fallback={
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ y: 10, opacity: 0 }}
>
{uploadAction}
</motion.div>
}
>
<Match when={isSignedOut}>{authActions}</Match>
<Match when={isSearching}>
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ y: -10, opacity: 0 }}
>
<button
onClick={onDismissSearch}
style={{ marginRight: 4 }}
css={STYLES_DISMISS_BUTTON}
>
<SVG.Dismiss style={{ display: "block" }} height={16} width={16} />
</button>
</motion.div>
</Match>
</Switch>
</AnimatePresence>
);
};