Browse Source

Add Touch and side panel

Jeremie Pardou-Piquemal 3 years ago
parent
commit
8eb7011de0

BIN
public/board.png


+ 17 - 53
src/components/AddItemButton.js

@@ -8,35 +8,8 @@ import NewItems from "./NewItems";
 
 import { AvailableItemListAtom } from "./Board/";
 
-const StyledButton = styled.div.attrs(() => ({
-  className: "button clear icon-only primary",
-}))`
-  position: fixed;
-  bottom: 4px;
-  right: 4px;
-`;
-
-const AddItemPane = styled.div`
-  position: absolute;
-  right: 0.5em;
-  top: 3.5em;
-  bottom: 0.5em;
-  background-color: var(--color-grey);
-  box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
-  width: 20%;
-  min-width: 280px;
-  padding: 0.5em;
-  text-align: center;
-  overflow-y: scroll;
-  & .tabs a {
-    cursor: pointer;
-  }
-  & .close {
-    position: absolute;
-    top: 5px;
-    right: 10px;
-  }
-`;
+import Touch from "../ui/Touch";
+import SidePanel from "../ui/SidePanel";
 
 const AvailableItemList = styled.div`
   margin-top: 2em;
@@ -53,30 +26,21 @@ const AddItemButton = () => {
 
   return (
     <>
-      <StyledButton onClick={() => setShowAddPanel((prev) => !prev)}>
-        <img
-          src={
-            showAddPanel
-              ? "https://icongr.am/feather/x-circle.svg?size=46&color=db5034"
-              : "https://icongr.am/feather/plus-circle.svg?size=46&color=db5034"
-          }
-          alt={t("Add item")}
-          title={t("Add item")}
-        />
-      </StyledButton>
+      <Touch
+        onClick={() => setShowAddPanel((prev) => !prev)}
+        alt={t("Add item")}
+        title={t("Add item")}
+        label={t("Add")}
+        icon={showAddPanel ? "cross" : "plus"}
+        style={{ flex: 1 }}
+      />
       {showAddPanel && (
-        <AddItemPane>
-          <button
-            className="button clear icon-only close"
-            onClick={() => {
-              setShowAddPanel(false);
-            }}
-          >
-            <img
-              src="https://icongr.am/feather/x.svg?size=30&color=ffffff"
-              alt={t("Close")}
-            />
-          </button>
+        <SidePanel
+          onClose={() => {
+            setShowAddPanel(false);
+          }}
+          position="right"
+        >
           <nav className="tabs">
             {
               // eslint-disable-next-line
@@ -105,7 +69,7 @@ const AddItemButton = () => {
               </AvailableItemList>
             )}
           </section>
-        </AddItemPane>
+        </SidePanel>
       )}
     </>
   );

+ 20 - 3
src/components/Board/Board.js

@@ -11,6 +11,16 @@ import styled from "styled-components";
 import { useRecoilValue } from "recoil";
 import { BoardConfigAtom } from "./game/atoms";
 
+/*
+
+  #2C3749 - #13131B
+  background: radial-gradient(circle, #2c3749, #13131b 100%), url(/board.png);
+  background-blend-mode: multiply;
+  box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px,
+    rgba(10, 37, 64, 0.35) 0px -2px 6px 0px inset;
+  
+*/
+
 const Placeholder = styled.p`
   position: fixed;
   top: 40vh;
@@ -21,14 +31,21 @@ const Placeholder = styled.p`
 
 const StyledBoard = styled.div.attrs(() => ({ className: "board" }))`
   position: relative;
-  background: radial-gradient(closest-corner, #3954848a, #0d101547 120%),
+  background: radial-gradient(
+      circle,
+      hsla(218, 30%, 40%, 0.7),
+      hsla(218, 40%, 40%, 0.05) 100%
+    ),
     url(/board.png);
 
+  border: 1px solid transparent;
+
   width: ${({ size }) => size}px;
   height: ${({ size }) => size}px;
+
   border-radius: 2px;
-  box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px,
-    rgba(10, 37, 64, 0.35) 0px -2px 6px 0px inset;
+
+  box-shadow: 0px 3px 6px #00000029;
 `;
 
 export const Board = ({ user, users, getComponent, moveFirst = true }) => {

+ 11 - 33
src/components/SelectedItemsPane.js

@@ -14,6 +14,7 @@ import {
 import debounce from "lodash.debounce";
 
 import { insideClass, hasClass } from "../utils";
+import SidePanel from "../ui/SidePanel";
 
 import ItemFormFactory from "./boardComponents/ItemFormFactory";
 
@@ -22,23 +23,6 @@ import "react-confirm-alert/src/react-confirm-alert.css";
 
 import { useTranslation } from "react-i18next";
 
-const SelectedPane = styled.div`
-  position: absolute;
-  left: 0.5em;
-  bottom: 0.5em;
-  top: 4.5em;
-  background-color: var(--color-grey);
-  padding: 0.5em;
-  overflow-y: scroll;
-  max-width: 30%;
-  z-index: 2;
-  & .close {
-    position: absolute;
-    right: 0.5em;
-    top: 0.5em;
-  }
-`;
-
 const ActionPane = styled.div.attrs(({ top, left, height }) => {
   if (top < 120) {
     return {
@@ -60,15 +44,15 @@ const ActionPane = styled.div.attrs(({ top, left, height }) => {
   touch-action: none;
   position: absolute;
   display: flex;
-  background-color: var(--color-darkGrey);
+  background-color: var(--color-blueGrey);
   justify-content: center;
   align-items: center;
   border-radius: 4px;
   padding: 0.1em 0.5em;
   transition: opacity 300ms;
-  opacity: ${({ hide }) => (hide ? 0 : 0.7)};
+  opacity: ${({ hide }) => (hide ? 0 : 0.9)};
   
-  box-shadow: 2.5px 4.33px 14.7px 0.3px rgba(0, 0, 0, 0.7);
+  box-shadow: 2px 2px 10px 0.3px rgba(0, 0, 0, 0.5);
 
   &:hover{
     opacity: 1;
@@ -306,28 +290,22 @@ export const SelectedItemsPane = () => {
   return (
     <>
       {showEdit && !boardState.selecting && (
-        <SelectedPane key={selectedItems[0]}>
+        <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>}
-              <button
-                className="button clear icon-only close"
-                onClick={() => {
-                  setShowEdit(false);
-                }}
-              >
-                <img
-                  src="https://icongr.am/feather/x.svg?size=30&color=ffffff"
-                  alt={t("Close")}
-                />
-              </button>
             </header>
             <CardContent>
               <ItemFormFactory />
             </CardContent>
           </div>
-        </SelectedPane>
+        </SidePanel>
       )}
       {selectedItems.length && (
         <ActionPane

+ 1 - 0
src/components/boardComponents/Image.js

@@ -44,6 +44,7 @@ const Wrapper = styled.div.attrs(({ flippable }) => ({
   user-select: none;
   position: relative;
   line-height: 0em;
+  //filter: drop-shadow(2px 2px 3px #2225);
 `;
 
 const FrontImage = styled.img`

+ 2 - 0
src/index.css

@@ -12,8 +12,10 @@ body {
   --font-color: #f9fbfa;
   --font-color2: #b3b3b3;
   --color-darkGrey: #121212;
+  --color-blueGrey: #19202c;
   --color-lightGrey: #90969d;
   --color-grey: #454545;
+  --color-midGrey: #2C3749;
   --color-primary: #db5034;
   --color-secondary: #00a698ff;
   --color-error: #d43939;

+ 4 - 1
src/ui/Modal.js

@@ -15,13 +15,16 @@ const StyledModal = styled.div.attrs(() => ({ className: "overlay" }))`
   overflow: auto;
   background-color: rgba(0, 0, 0, 0.4);
   border-radius: 5px;
+
   .modal-content {
     max-width: 50%;
     position: relative;
     margin: 10% auto;
     padding: 8px 8px 8px 8px;
+    padding: 2em;
     border-radius: 2px;
-    background: var(--color-grey);
+
+    background-color: var(--color-blueGrey);
     box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px,
       rgba(0, 0, 0, 0.23) 0px 6px 6px;
   }

+ 76 - 0
src/ui/SidePanel.js

@@ -0,0 +1,76 @@
+import React from "react";
+import styled from "styled-components";
+import { useTranslation } from "react-i18next";
+
+const StyledSidePanel = styled.div`
+  position: fixed;
+  z-index: 10;
+  ${({ position }) => (position === "right" ? "right: 0;" : "left: 0;")}
+  top: 4em;
+  bottom: 0em;
+
+  background-color: var(--color-blueGrey);
+
+  box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
+  width: 25%;
+  min-width: 280px;
+  overflow-y: scroll;
+  & .close {
+    position: absolute;
+    top: 5px;
+    right: 10px;
+  }
+  transform: translateX(100%);
+  transition: all 500ms cubic-bezier(0.4, 0, 0.2, 1);
+  padding: 1em;
+
+  ${({ open, position }) => {
+    let start = -100;
+    let end = 0;
+    if (position === "right") {
+      start = 100;
+    }
+    return open
+      ? `transform: translateX(${end}%);`
+      : `transform: translateX(${start}%);`;
+  }}
+
+  ${({ open }) => (open ? "opacity: 1;" : "opacity: 0.2;")}
+`;
+
+const SidePanel = ({ children, position, onClose = () => {} }) => {
+  const { t } = useTranslation();
+  const [open, setOpen] = React.useState(false);
+
+  React.useEffect(() => {
+    setOpen(true);
+  }, []);
+
+  const onCloseHandler = React.useCallback(() => {
+    setOpen(false);
+  }, []);
+
+  const onAnimationEnd = React.useCallback(() => {
+    if (!open) {
+      onClose();
+    }
+  }, [onClose, open]);
+
+  return (
+    <StyledSidePanel
+      position={position}
+      open={open}
+      onTransitionEnd={onAnimationEnd}
+    >
+      <button className="button clear icon-only close" onClick={onCloseHandler}>
+        <img
+          src="https://icongr.am/feather/x.svg?size=30&color=ffffff"
+          alt={t("Close")}
+        />
+      </button>
+      {children}
+    </StyledSidePanel>
+  );
+};
+
+export default SidePanel;

+ 58 - 0
src/ui/Touch.js

@@ -0,0 +1,58 @@
+import React from "react";
+import styled from "styled-components";
+
+const StyledWrapper = styled.div`
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+`;
+
+const StyledButton = styled.div.attrs(({ active }) => ({
+  className: active ? "active" : "",
+}))`
+  border-radius: 100%;
+  line-height: 1;
+  text-decoration: none;
+  display: inline-block;
+  cursor: pointer;
+  border-color: transparent;
+  color: var(--font-color);
+  background-color: var(--color-midGrey);
+  padding: 0.5rem;
+  width: 38px;
+  height: 38px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  &:hover,
+  &.active {
+    background-color: var(--color-primary);
+  }
+`;
+
+const Touch = ({
+  onClick = () => {},
+  icon,
+  title,
+  alt,
+  active,
+  label,
+  ...rest
+}) => {
+  // Touch icon
+  return (
+    <StyledWrapper {...rest}>
+      <StyledButton onClick={onClick} active={active}>
+        <img
+          src={`https://icongr.am/entypo/${icon}.svg?size=24&color=f9fbfa`}
+          alt={alt}
+          title={title}
+        />
+      </StyledButton>
+      {label && <span>{label}</span>}
+    </StyledWrapper>
+  );
+};
+
+export default Touch;

+ 30 - 27
src/views/BoardView.js

@@ -14,6 +14,7 @@ import ImageDropNPaste from "../components/ImageDropNPaste";
 import { getComponent } from "../components/boardComponents";
 import { useGame } from "../hooks/useGame";
 import AddItemButton from "../components/AddItemButton";
+import Touch from "../ui/Touch";
 
 import { insideClass } from "../utils";
 
@@ -32,16 +33,6 @@ const BoardContainer = styled.div`
   background-color: var(--color-darkGrey);
 `;
 
-const FirstActionSelect = styled.button`
-  position: fixed;
-  bottom: 1em;
-  left: 1em;
-  background: none;
-  border: 4px solid var(--color-primary);
-  border-radius: 100%;
-  padding: 0.5em;
-`;
-
 export const BoardView = ({ namespace, edit: editMode = false }) => {
   const { t } = useTranslation();
   const { currentUser, users } = useUsers();
@@ -87,23 +78,35 @@ export const BoardView = ({ namespace, edit: editMode = false }) => {
           <SelectedItemsPane />
         </BoardContainer>
       )}
-      <FirstActionSelect onClick={() => setMoveFirst((prev) => !prev)}>
-        {!moveFirst && (
-          <img
-            src="https://icongr.am/feather/move.svg?size=24&color=db5034"
-            alt={t("Move mode")}
-            title={t("Switch to move mode")}
-          />
-        )}
-        {moveFirst && (
-          <img
-            src="https://icongr.am/feather/mouse-pointer.svg?size=24&color=db5034"
-            alt={t("Select mode")}
-            title={t("Switch to select mode")}
-          />
-        )}
-      </FirstActionSelect>
-      <AddItemButton />
+      <div
+        style={{
+          position: "fixed",
+          bottom: "1em",
+          right: "1em",
+          display: "flex",
+          width: "20%",
+        }}
+      >
+        <Touch
+          onClick={() => setMoveFirst(false)}
+          alt={t("Select mode")}
+          label={t("Select")}
+          title={t("Switch to select mode")}
+          icon={"mouse-pointer"}
+          active={!moveFirst}
+          style={{ flex: 1 }}
+        />
+        <Touch
+          onClick={() => setMoveFirst(true)}
+          alt={t("Move mode")}
+          label={t("Move")}
+          title={t("Switch to move mode")}
+          icon={"hand"}
+          active={moveFirst}
+        />
+        <div style={{ flex: 1 }} />
+        <AddItemButton />
+      </div>
     </StyledBoardView>
   );
 };

+ 32 - 38
src/views/NavBar.js

@@ -8,6 +8,7 @@ import InfoEditModal from "../views/InfoEditModal";
 import LoadSaveModal from "../views/LoadSaveModal";
 import { UserList } from "../components/users";
 import { getBestTranslationFromConfig } from "../utils/api";
+import Touch from "../ui/Touch";
 
 import useBoardConfig from "../components/useBoardConfig";
 
@@ -21,7 +22,7 @@ const StyledNavBar = styled.div.attrs(() => ({ className: "nav" }))`
   width: 100%;
   z-index: 10;
 
-  background-color: #19202c;
+  background-color: #19202ce0;
   box-shadow: 0px 3px 6px #00000029;
 
   color: var(--font-color);
@@ -34,7 +35,7 @@ const StyledNavBar = styled.div.attrs(() => ({ className: "nav" }))`
       margin: 0;
       padding: 0 2em;
 
-      background-color: #19202c;
+      background-color: var(--color-blueGrey);
       box-shadow: 0px 3px 6px #00000029;
 
       line-height: 90px;
@@ -62,6 +63,12 @@ const StyledNavBar = styled.div.attrs(() => ({ className: "nav" }))`
   & .nav-right {
     justify-content: flex-end;
     padding-right: 5px;
+    gap: 1em;
+  }
+
+  & .spacer {
+    flex: 1;
+    max-width: 1em;
   }
 
   @media screen and (max-width: 640px) {
@@ -118,45 +125,32 @@ const NavBar = ({ editMode }) => {
 
         <div className="nav-right">
           {!editMode && <UserList />}
-          <button
-            className="button clear icon-only save"
+          <div className="spacer" />
+          <Touch
+            onClick={() => setShowLoadGameModal((prev) => !prev)}
+            alt={t("Info")}
+            title={t("Info")}
+            icon={"upload-to-cloud"}
+          />
+          <Touch
             onClick={() => setShowLoadGameModal((prev) => !prev)}
-          >
-            <img
-              src="https://icongr.am/feather/save.svg?size=50&color=ffffff"
-              alt={t("Save")}
-              title={t("Save")}
-            />
-          </button>
-          <button
-            className="button clear icon-only info"
+            alt={t("Save")}
+            title={t("Save session")}
+            icon={"download"}
+          />
+          <div className="spacer" />
+          <Touch
             onClick={() => setShowInfoModal((prev) => !prev)}
-          >
-            {!editMode && (
-              <img
-                src="https://icongr.am/feather/info.svg?size=50&color=ffffff"
-                alt={t("Info")}
-                title={t("Info")}
-              />
-            )}
-            {editMode && (
-              <img
-                src="https://icongr.am/feather/edit.svg?size=50&color=ffffff"
-                alt={t("Configure game")}
-                title={t("Configure game")}
-              />
-            )}
-          </button>
-          <button
-            className="button clear icon-only help"
+            alt={t("Info")}
+            title={t("Info")}
+            icon={editMode ? "tools" : "info"}
+          />
+          <Touch
             onClick={() => setShowHelpModal((prev) => !prev)}
-          >
-            <img
-              src="https://icongr.am/feather/help-circle.svg?size=50&color=ffffff"
-              alt={t("Help")}
-              title={t("Help")}
-            />
-          </button>
+            alt={"help"}
+            title={t("Help")}
+            icon={"help"}
+          />
         </div>
       </StyledNavBar>
       <HelpModal show={showHelpModal} setShow={setShowHelpModal} />