Replace Modal by Side panel

This commit is contained in:
Jeremie Pardou-Piquemal 2021-03-19 21:40:22 +01:00 committed by Jérémie Pardou-Piquemal
parent f5be632cb5
commit a28d2d8e4e
9 changed files with 203 additions and 224 deletions

View file

@ -72,7 +72,12 @@ const Account = ({ disabled, ...props }) => {
)}
</div>
{loginInProgress && <Waiter message={t("In progress...")} />}
<Modal show={showLogin} setShow={setShowLogin} title={t("Login")}>
<Modal
show={showLogin}
setShow={setShowLogin}
title={t("Login")}
width="33%"
>
{!emailSent && (
<>
<input
@ -88,7 +93,7 @@ const Account = ({ disabled, ...props }) => {
marginTop: "2em",
}}
>
<button onClick={handleSubmit} className="button primary">
<button onClick={handleSubmit} className="button success">
{t("Ask authentication link")}
</button>
</div>
@ -107,7 +112,7 @@ const Account = ({ disabled, ...props }) => {
}}
>
<button
className="button primary"
className="button success"
onClick={() => setShowLogin(false)}
>
Ok

View file

@ -40,6 +40,7 @@ const AddItemButton = () => {
setShowAddPanel(false);
}}
position="right"
width="33%"
>
<nav className="tabs">
{
@ -47,6 +48,7 @@ const AddItemButton = () => {
<a
onClick={() => setTab("standard")}
className={tab === "standard" ? "active" : ""}
style={{ cursor: "pointer" }}
>
{t("Standard")}
</a>
@ -56,6 +58,7 @@ const AddItemButton = () => {
<a
onClick={() => setTab("other")}
className={tab === "other" ? "active" : ""}
style={{ cursor: "pointer" }}
>
{t("Other")}
</a>

View file

@ -58,11 +58,6 @@ const Message = ({
);
};
const StyledMessageList = styled.div`
height: 100%;
overflow: auto;
`;
const computeMessageGroup = (messages, userMap, maxTimeDiff = 30000) => {
if (!messages || messages.length === 0) return [];
@ -105,6 +100,11 @@ const computeMessageGroup = (messages, userMap, maxTimeDiff = 30000) => {
return messageGroups;
};
const StyledMessageList = styled.div`
height: 100%;
overflow: auto;
`;
const MessageList = ({ messages }) => {
const messageList = React.useRef(null);
const { users } = useUsers();
@ -216,9 +216,10 @@ const MessageButton = () => {
}}
position="left"
noMargin
title={t("Chat")}
width="25%"
>
<StyledChat>
<h3>{t("Chat")}</h3>
<MessageList messages={messages} />
<Composer sendMessage={sendMessage} />
</StyledChat>

View file

@ -286,26 +286,31 @@ export const SelectedItemsPane = ({ hideMenu = false }) => {
});
};*/
let title = "";
if (selectedItems.length === 1) {
title = t("Edit item");
}
if (selectedItems.length > 1) {
title = t("Edit all items");
}
return (
<>
{showEdit && !boardState.selecting && (
<SidePanel
key={selectedItems[0]}
onClose={() => {
setShowEdit(false);
}}
>
<div>
<header>
{selectedItems.length === 1 && <h3>{t("Edit item")}</h3>}
{selectedItems.length > 1 && <h3>{t("Edit all items")}</h3>}
</header>
<CardContent>
<ItemFormFactory />
</CardContent>
</div>
</SidePanel>
)}
<SidePanel
key={selectedItems[0]}
open={showEdit && !boardState.selecting}
onClose={() => {
setShowEdit(false);
}}
title={title}
width="25%"
>
<div>
<CardContent>
<ItemFormFactory />
</CardContent>
</div>
</SidePanel>
{selectedItems.length && !hideMenu && (
<ActionPane
{...boundingBoxLast}

View file

@ -1,107 +1,15 @@
import React from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import { hasClass } from "../utils";
const StyledModal = styled.div.attrs(() => ({ className: "overlay" }))`
position: fixed;
z-index: 20;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 5px;
.modal-content {
max-width: 500px;
min-height: 100%;
position: absolute;
top: 0px;
right: 0px;
padding: 8px 8px 8px 8px;
padding: 2em;
border-radius: 2px;
background-color: var(--color-darkGrey);
box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px,
rgba(0, 0, 0, 0.23) 0px 6px 6px;
}
& .title {
font-weight: 700;
}
& .content {
& header {
padding: 0.5em;
margin-top: 2em;
background-color: var(--color-blueGrey);
border-radius: 0.5em 0.5em 0em 0em;
& h3 {
margin: 0.2em 0.2em 0.2em;
font-weight: 300;
}
}
& section {
border-radius: 0em 0em 0.5em 0.5em;
padding: 2em;
background-color: var(--color-darkBlueGrey);
}
}
& .close {
position: fixed;
top: 1em;
right: 1em;
padding: 0.5rem;
margin: 0;
}
& footer {
margin-top: 1em;
}
@media screen and (max-width: 640px) {
& .modal-content {
max-width: 90%;
}
}
`;
export const Modal = ({ setShow, show, children, footer, title }) => {
const { t } = useTranslation();
if (!show) {
return null;
}
import SidePanel from "./SidePanel";
export const Modal = ({ setShow, show, ...rest }) => {
return (
<StyledModal
onClick={(e) => {
if (hasClass(e.target, "overlay")) setShow(false);
}}
>
<div className="modal-content">
<article>
<header>
<h2 className="title">{title}</h2>
<button
className="button clear icon-only close"
onClick={() => {
setShow(false);
}}
>
<img
src="https://icongr.am/feather/x.svg?size=42&color=ffffff"
alt={t("Close")}
/>
</button>
</header>
<div className="content">{children}</div>
<footer>{footer}</footer>
</article>
</div>
</StyledModal>
<SidePanel
open={show}
onClose={() => setShow(false)}
position="right"
modal
{...rest}
/>
);
};

View file

@ -3,27 +3,40 @@ import styled from "styled-components";
import { useTranslation } from "react-i18next";
import usePortal from "react-useportal";
const Overlay = styled.div`
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.6);
`;
const StyledSidePanel = styled.div`
position: fixed;
${({ position }) => (position === "right" ? "right: 0;" : "left: 0;")}
top: 0;
bottom: 0em;
bottom: 0;
z-index: 22;
display: flex;
flex-direction: column;
height: 100%;
max-height: 100%;
overflow: hidden;
background-color: var(--color-blueGrey);
background-color: ${({ modal }) =>
modal ? "var(--color-darkGrey)" : "var(--color-blueGrey)"};
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
width: 25%;
min-width: 280px;
max-width: 500px;
${({ width }) => (width ? `width: ${width};` : "")}
overflow-y: auto;
& .close {
position: fixed;
top: 5px;
right: 10px;
}
transform: translateX(100%);
transition: all 500ms cubic-bezier(0.4, 0, 0.2, 1);
${({ noMargin }) => (noMargin ? "" : "padding: 1em")};
${({ open, position }) => {
let start = -100;
@ -37,6 +50,43 @@ const StyledSidePanel = styled.div`
}}
${({ open }) => (open ? "opacity: 1;" : "opacity: 0.2;")}
& .close {
position: fixed;
top: 5px;
right: 10px;
}
& .title {
font-weight: 700;
padding: 0.5em;
margin: 0;
}
& > .content {
flex: 1;
overflow: auto;
${({ noMargin }) => (noMargin ? "" : "padding: 1em")};
& header {
padding: 0.5em;
margin-top: 2em;
background-color: var(--color-blueGrey);
border-radius: 0.5em 0.5em 0em 0em;
& h3 {
margin: 0.2em 0.2em 0.2em;
font-weight: 300;
}
}
& section {
border-radius: 0em 0em 0.5em 0.5em;
padding: 2em;
background-color: var(--color-darkBlueGrey);
}
}
& footer {
margin-top: 1em;
}
`;
const SidePanel = ({
@ -44,11 +94,15 @@ const SidePanel = ({
position,
noMargin,
onClose = () => {},
title,
footer,
open,
modal = false,
width,
}) => {
const { t } = useTranslation();
const { ref, Portal, openPortal, closePortal, isOpen } = usePortal({
closeOnOutsideClick: false,
closeOnOutsideClick: modal,
});
const onAnimationEnd = React.useCallback(() => {
@ -68,23 +122,30 @@ const SidePanel = ({
return (
<>
<Portal>
{modal && isOpen && <Overlay onClick={closePortal} />}
<StyledSidePanel
position={position}
open={isOpen}
onTransitionEnd={onAnimationEnd}
noMargin={noMargin}
ref={ref}
width={width}
modal={modal}
>
<button
className="button clear icon-only close"
onClick={closePortal}
>
<img
src="https://icongr.am/feather/x.svg?size=42&color=ffffff"
alt={t("Close")}
/>
</button>
{children}
<header>
{title && <h2 className="title">{title}</h2>}
<button
className="button clear icon-only close"
onClick={closePortal}
>
<img
src="https://icongr.am/feather/x.svg?size=42&color=ffffff"
alt={t("Close")}
/>
</button>
</header>
<div className="content">{children}</div>
{footer && <footer>{footer}</footer>}
</StyledSidePanel>
</Portal>
</>

View file

@ -10,11 +10,9 @@ const InfoModal = ({ show, setShow }) => {
return (
<Modal title={t("Edit game information")} setShow={setShow} show={show}>
<>
<section>
<BoardConfig />
</section>
</>
<section>
<BoardConfig />
</section>
</Modal>
);
};

View file

@ -53,78 +53,76 @@ const InfoModal = ({ show, setShow }) => {
return (
<Modal title={t("Help & info")} setShow={setShow} show={show}>
<>
<header>
<h3>{t("Game information")}</h3>
</header>
<section>
{translation.description && (
<div
dangerouslySetInnerHTML={{
__html: info,
}}
></div>
)}
{!translation.description && <div>{t("No information")}</div>}
</section>
<header>
<h3>{t("Board interactions")}</h3>
</header>
<section>
<Trans i18nKey="helpBoard">
<ul>
<li>
Move the board with middle mouse button click. Alternatively you
can use left button with alt key.
</li>
<li>Zoom with mouse wheel.</li>
<li>
Switch to edit mode with top button to be able to edit the game.
</li>
<li>You can save and reload game by clicking the burger menu.</li>
</ul>
</Trans>{" "}
</section>
<header>
<h3>{t("Item interactions")}</h3>
</header>
<section>
<Trans i18nKey="helpItem">
<ul>
<li>Double click on any item that can be flipped to flip it.</li>
<li>
<Kbd>t</Kbd> key to tap/untap selected items.
</li>
<li>
<Kbd>f</Kbd> key to flip/unflip selected items.
</li>
<li>
<Kbd>o</Kbd> key to reveal front side of selected flipped items
ONLY ONLY to you.
</li>
<li>
<Kbd>l</Kbd> key to be able to selected previously locked item.
</li>
</ul>
</Trans>
</section>
<header>
<h3>{t("Game information")}</h3>
</header>
<section>
{translation.description && (
<div
dangerouslySetInnerHTML={{
__html: info,
}}
></div>
)}
{!translation.description && <div>{t("No information")}</div>}
</section>
<header>
<h3>{t("Board interactions")}</h3>
</header>
<section>
<Trans i18nKey="helpBoard">
<ul>
<li>
Move the board with middle mouse button click. Alternatively you
can use left button with alt key.
</li>
<li>Zoom with mouse wheel.</li>
<li>
Switch to edit mode with top button to be able to edit the game.
</li>
<li>You can save and reload game by clicking the burger menu.</li>
</ul>
</Trans>
</section>
<header>
<h3>{t("Item interactions")}</h3>
</header>
<section>
<Trans i18nKey="helpItem">
<ul>
<li>Double click on any item that can be flipped to flip it.</li>
<li>
<Kbd>t</Kbd> key to tap/untap selected items.
</li>
<li>
<Kbd>f</Kbd> key to flip/unflip selected items.
</li>
<li>
<Kbd>o</Kbd> key to reveal front side of selected flipped items
ONLY ONLY to you.
</li>
<li>
<Kbd>l</Kbd> key to be able to selected previously locked item.
</li>
</ul>
</Trans>
</section>
<header>
<h3>{t("More information")}</h3>
</header>
<header>
<h3>{t("More information")}</h3>
</header>
<section>
<Trans i18nKey="moreInformation">
<p>
For more information, visit{" "}
<a href="https://github.com/jrmi/airboardgame/">
github repository
</a>
.
</p>
</Trans>
</section>
</>
<section>
<Trans i18nKey="moreInformation">
<p>
For more information, visit{" "}
<a href="https://github.com/jrmi/airboardgame/">
github repository
</a>
.
</p>
</Trans>
</section>
</Modal>
);
};

View file

@ -10,7 +10,7 @@ import Modal from "../ui/Modal";
const LoadSessionModal = ({ show, setShow }) => {
const { t } = useTranslation();
const {c2c} = useC2C();
const { c2c } = useC2C();
const loadGame = React.useCallback(
(game) => {