feat(Filter/Popup): add responsive popup component

This commit is contained in:
Aminejv 2021-10-28 17:15:22 +01:00
parent d3459a9919
commit 5233b5c056
5 changed files with 153 additions and 9 deletions

View File

@ -12,7 +12,7 @@ const STYLES_DATAVIEWER_WRAPPER = (theme) => css`
min-height: 100vh; min-height: 100vh;
padding: calc(20px + ${theme.sizes.filterNavbar}px) 24px 44px; padding: calc(20px + ${theme.sizes.filterNavbar}px) 24px 44px;
@media (max-width: ${theme.sizes.mobile}px) { @media (max-width: ${theme.sizes.mobile}px) {
padding: 31px 16px 44px; padding: calc(31px + ${theme.sizes.filterNavbar}px) 16px 44px;
} }
`; `;

View File

@ -1,5 +1,6 @@
import * as React from "react"; import * as React from "react";
import * as ReactDOM from "react-dom"; import * as ReactDOM from "react-dom";
import * as Styles from "~/common/styles";
import { usePortals } from "~/components/core/PortalsProvider"; import { usePortals } from "~/components/core/PortalsProvider";
import { css } from "@emotion/react"; import { css } from "@emotion/react";
@ -16,8 +17,9 @@ export function NavbarPortal({ children }) {
return filterNavbarElement return filterNavbarElement
? ReactDOM.createPortal( ? ReactDOM.createPortal(
<> <>
<Divider height="0.5px" /> <Divider height="0.5px" color="borderGrayLight" css={Styles.MOBILE_HIDDEN} />
<div css={STYLES_NAVBAR}>{children}</div> <div css={STYLES_NAVBAR}>{children}</div>
<Divider height="0.5px" color="borderGrayLight" css={Styles.MOBILE_ONLY} />
</>, </>,
filterNavbarElement filterNavbarElement
) )
@ -29,12 +31,27 @@ export function NavbarPortal({ children }) {
* -----------------------------------------------------------------------------------------------*/ * -----------------------------------------------------------------------------------------------*/
const STYLES_NAVBAR = (theme) => css` const STYLES_NAVBAR = (theme) => css`
position: relative;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
background-color: ${theme.semantic.bgWhite};
padding: 9px 24px 11px; padding: 9px 24px 11px;
box-shadow: ${theme.shadow.lightSmall}; box-shadow: ${theme.shadow.lightSmall};
@media (max-width: ${theme.sizes.mobile}px) {
padding: 9px 16px 11px;
}
`;
const STYLES_NAVBAR_BACKGROUND = (theme) => css`
position: absolute;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
z-index: -1;
background-color: ${theme.semantic.bgWhite};
@supports ((-webkit-backdrop-filter: blur(75px)) or (backdrop-filter: blur(75px))) { @supports ((-webkit-backdrop-filter: blur(75px)) or (backdrop-filter: blur(75px))) {
-webkit-backdrop-filter: blur(75px); -webkit-backdrop-filter: blur(75px);
backdrop-filter: blur(75px); backdrop-filter: blur(75px);
@ -49,5 +66,10 @@ const STYLES_NAVBAR = (theme) => css`
export function Navbar({ children }) { export function Navbar({ children }) {
const { filterNavbar } = usePortals(); const { filterNavbar } = usePortals();
const [, setFilterElement] = filterNavbar; const [, setFilterElement] = filterNavbar;
return <div ref={setFilterElement}>{children}</div>; return (
<div ref={setFilterElement}>
{children}
<div css={STYLES_NAVBAR_BACKGROUND} />
</div>
);
} }

View File

@ -0,0 +1,80 @@
import * as React from "react";
import * as SVG from "~/common/svg";
import * as Styles from "~/common/styles";
import * as Filters from "~/components/core/Filter/Filters";
import { useFilterContext } from "~/components/core/Filter/Provider";
import { motion } from "framer-motion";
import { css } from "@emotion/react";
/* -------------------------------------------------------------------------------------------------
* Popup trigger
* -----------------------------------------------------------------------------------------------*/
export function PopupTrigger({ children, isMobile, ...props }) {
const [{ sidebarState, popupState }, { hidePopup, togglePopup }] = useFilterContext();
React.useEffect(() => {
if (sidebarState.isVisible) {
hidePopup();
}
}, [sidebarState.isVisible]);
if (sidebarState.isVisible && !isMobile) return null;
return (
<button
onClick={togglePopup}
css={[Styles.BUTTON_RESET, Styles.HORIZONTAL_CONTAINER_CENTERED]}
{...props}
>
{children}
<motion.div initial={null} animate={{ rotateX: popupState.isVisible ? 180 : 0 }}>
<SVG.ChevronUp style={{ display: "block" }} />
</motion.div>
</button>
);
}
/* -------------------------------------------------------------------------------------------------
* Popup
* -----------------------------------------------------------------------------------------------*/
const STYLES_SIDEBAR_FILTER_WRAPPER = (theme) => css`
position: sticky;
top: ${theme.sizes.header + theme.sizes.filterNavbar}px;
width: 236px;
max-height: 420px;
overflow-y: auto;
overflow-x: hidden;
border-radius: 16px;
padding: 20px 16px;
box-shadow: ${theme.shadow.lightLarge};
border: 1px solid ${theme.semantic.borderGrayLight};
background-color: ${theme.semantic.bgLight};
@supports ((-webkit-backdrop-filter: blur(75px)) or (backdrop-filter: blur(75px))) {
background-color: ${theme.semantic.bgBlurWhite};
-webkit-backdrop-filter: blur(75px);
backdrop-filter: blur(75px);
}
@media (max-width: ${theme.sizes.mobile}px) {
border-radius: unset;
width: 100%;
max-height: 375px;
padding: 15px 8px;
}
`;
export function Popup({ viewer, css, ...props }) {
const [{ popupState }] = useFilterContext();
if (!popupState.isVisible) return null;
return (
<div css={[STYLES_SIDEBAR_FILTER_WRAPPER, css]} {...props}>
<Filters.Library />
<Filters.Tags viewer={viewer} style={{ marginTop: 12 }} />
</div>
);
}

View File

@ -4,14 +4,21 @@ import * as System from "~/components/system";
import { Navbar, NavbarPortal } from "~/components/core/Filter/Navbar"; import { Navbar, NavbarPortal } from "~/components/core/Filter/Navbar";
import { Provider } from "~/components/core/Filter/Provider"; import { Provider } from "~/components/core/Filter/Provider";
import { Sidebar, SidebarTrigger } from "~/components/core/Filter/Sidebar"; import { Sidebar, SidebarTrigger } from "~/components/core/Filter/Sidebar";
import { Popup, PopupTrigger } from "~/components/core/Filter/Popup";
import { Content } from "~/components/core/Filter/Content"; import { Content } from "~/components/core/Filter/Content";
import { useFilterContext } from "~/components/core/Filter/Provider";
/* ------------------------------------------------------------------------------------------------- /* -------------------------------------------------------------------------------------------------
* Title * Title
* -----------------------------------------------------------------------------------------------*/ * -----------------------------------------------------------------------------------------------*/
function Title() { function Title() {
return <System.H5 color="textBlack">All</System.H5>; const [{ filterState }] = useFilterContext();
return (
<System.H5 as="p" color="textBlack">
{filterState.title}
</System.H5>
);
} }
/* ------------------------------------------------------------------------------------------------- /* -------------------------------------------------------------------------------------------------
@ -21,4 +28,15 @@ function Actions() {
return <div />; return <div />;
} }
export { Title, Actions, Sidebar, SidebarTrigger, Provider, Navbar, Content, NavbarPortal }; export {
Title,
Actions,
Sidebar,
SidebarTrigger,
Popup,
PopupTrigger,
Provider,
Navbar,
Content,
NavbarPortal,
};

View File

@ -17,12 +17,24 @@ const STYLES_SCENE_PAGE = css`
`; `;
const STYLES_FILTER_TITLE_WRAPPER = css` const STYLES_FILTER_TITLE_WRAPPER = css`
${Styles.MOBILE_HIDDEN};
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
`; `;
const STYLES_FILTER_POPUP_WRAPPER = (theme) => css`
position: absolute;
top: calc(${theme.sizes.filterNavbar}px + 4px);
left: 4px;
width: 100%;
@media (max-width: ${theme.sizes.mobile}px) {
top: ${theme.sizes.filterNavbar}px;
left: 0px;
}
`;
export default function SceneFilesFolder({ viewer, page, onAction, isMobile }) { export default function SceneFilesFolder({ viewer, page, onAction, isMobile }) {
const [index, setIndex] = React.useState(-1); const [index, setIndex] = React.useState(-1);
@ -47,9 +59,21 @@ export default function SceneFilesFolder({ viewer, page, onAction, isMobile }) {
/> />
<Filter.Provider viewer={viewer}> <Filter.Provider viewer={viewer}>
<Filter.NavbarPortal> <Filter.NavbarPortal>
<div css={STYLES_FILTER_POPUP_WRAPPER}>
<Filter.Popup viewer={viewer} />
</div>
<div css={Styles.CONTAINER_CENTERED}> <div css={Styles.CONTAINER_CENTERED}>
<div css={Styles.MOBILE_HIDDEN}>
<Filter.SidebarTrigger /> <Filter.SidebarTrigger />
</div> </div>
<Filter.PopupTrigger isMobile={isMobile} style={{ marginLeft: 2 }}>
<span css={Styles.MOBILE_ONLY} style={{ marginRight: 8 }}>
<Filter.Title />
</span>
</Filter.PopupTrigger>
</div>
<div css={STYLES_FILTER_TITLE_WRAPPER}> <div css={STYLES_FILTER_TITLE_WRAPPER}>
<Filter.Title /> <Filter.Title />
</div> </div>
@ -57,7 +81,7 @@ export default function SceneFilesFolder({ viewer, page, onAction, isMobile }) {
</Filter.NavbarPortal> </Filter.NavbarPortal>
<div css={Styles.HORIZONTAL_CONTAINER}> <div css={Styles.HORIZONTAL_CONTAINER}>
<Filter.Sidebar /> <Filter.Sidebar viewer={viewer} isMobile={isMobile} />
<Filter.Content onAction={onAction} viewer={viewer} page={page} /> <Filter.Content onAction={onAction} viewer={viewer} page={page} />
</div> </div>
</Filter.Provider> </Filter.Provider>