mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-26 02:24:44 +03:00
feat(Filter/Popup): add responsive popup component
This commit is contained in:
parent
d3459a9919
commit
5233b5c056
@ -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;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
80
components/core/Filter/Popup.js
Normal file
80
components/core/Filter/Popup.js
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -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,
|
||||||
|
};
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user