Refactor selected items and add items
This commit is contained in:
parent
bdbba57b9f
commit
f7bfee8018
11 changed files with 171 additions and 74 deletions
88
src/components/AddItemButton.js
Normal file
88
src/components/AddItemButton.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
import React from "react";
|
||||
import { useRecoilValue } from "recoil";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
|
||||
import AvailableItems from "./AvailableItems";
|
||||
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: 7em;
|
||||
bottom: 4em;
|
||||
background-color: var(--bg-secondary-color);
|
||||
width: 20%;
|
||||
padding: 0.5em;
|
||||
text-align: center;
|
||||
overflow-y: scroll;
|
||||
& .tabs a {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const AvailableItemList = styled.div`
|
||||
margin-top: 2em;
|
||||
background-color: black;
|
||||
color: white;
|
||||
list-type: none;
|
||||
`;
|
||||
|
||||
const Title = styled.h3``;
|
||||
|
||||
const AddItemButton = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const availableItemList = useRecoilValue(AvailableItemListAtom);
|
||||
const [showAddPanel, setShowAddPanel] = React.useState(false);
|
||||
const [tab, setTab] = React.useState("standard");
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledButton onClick={() => setShowAddPanel((prev) => !prev)}>
|
||||
<img src="https://icongr.am/feather/plus-circle.svg?size=46&color=db5034" />
|
||||
</StyledButton>
|
||||
{showAddPanel && (
|
||||
<AddItemPane>
|
||||
<nav className="tabs">
|
||||
<a
|
||||
onClick={() => setTab("standard")}
|
||||
className={tab === "standard" ? "active" : ""}
|
||||
>
|
||||
Standard
|
||||
</a>
|
||||
{availableItemList && availableItemList.length > 0 && (
|
||||
<a
|
||||
onClick={() => setTab("other")}
|
||||
className={tab === "other" ? "active" : ""}
|
||||
>
|
||||
Other
|
||||
</a>
|
||||
)}
|
||||
</nav>
|
||||
<section className="content">
|
||||
{tab === "standard" && <NewItems />}
|
||||
{tab === "other" && (
|
||||
<AvailableItemList>
|
||||
<Title>{t("Box Content")}</Title>
|
||||
<AvailableItems />
|
||||
</AvailableItemList>
|
||||
)}
|
||||
</section>
|
||||
</AddItemPane>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddItemButton;
|
|
@ -45,7 +45,6 @@ const AvailableItems = () => {
|
|||
.filter((item) => item.groupId === groupId)
|
||||
.map((item) => (
|
||||
<li key={item.id}>
|
||||
{item.id}
|
||||
<AvailableItem data={item} />
|
||||
</li>
|
||||
))}
|
||||
|
|
|
@ -14,7 +14,7 @@ const LeftPane = styled.div`
|
|||
left: 0.5em;
|
||||
top: 4em;
|
||||
bottom: 0.5em;
|
||||
background-color: #ffffff10;
|
||||
background-color: var(--bg-secondary-color);
|
||||
width: 20%;
|
||||
padding: 0.5em;
|
||||
text-align: center;
|
||||
|
|
|
@ -21,38 +21,55 @@ import "react-confirm-alert/src/react-confirm-alert.css";
|
|||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const SelectedPane = styled.div.attrs(() => ({ className: "card" }))`
|
||||
const SelectedPane = styled.div`
|
||||
position: absolute;
|
||||
right: 0.5em;
|
||||
left: 0.5em;
|
||||
bottom: 0.5em;
|
||||
background-color: #ffffffe0;
|
||||
padding: 0.5em;
|
||||
max-height: 66%;
|
||||
top: 4.5em;
|
||||
background-color: transparent;
|
||||
overflow-y: scroll;
|
||||
transform: scaleX(-1);
|
||||
& > div {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
`;
|
||||
|
||||
const ActionPane = styled.div.attrs(({ top, left }) => ({
|
||||
style: {
|
||||
top: `${top - 50}px`,
|
||||
top: `${top - 55}px`,
|
||||
left: `${left}px`,
|
||||
},
|
||||
}))`
|
||||
position: absolute;
|
||||
display: flex;
|
||||
background-color: transparent;
|
||||
background-color: #333333ff;
|
||||
justify-content: center;
|
||||
//z-index: 1;
|
||||
border-radius: 4px;
|
||||
padding: 0.5em;
|
||||
box-shadow: rgba(0, 0, 0, 0.15) 0px 3px 3px 0px;
|
||||
& button{
|
||||
margin 0 6px;
|
||||
padding: 0.4em;
|
||||
height: 40px
|
||||
}
|
||||
& .count{
|
||||
color: var(--color-secondary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
line-height: 0.8em;
|
||||
}
|
||||
& .number{
|
||||
font-size: 1.5em;
|
||||
line-height: 1em;
|
||||
}
|
||||
`;
|
||||
|
||||
const CardContent = styled.div.attrs(() => ({ className: "content" }))`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.5em;
|
||||
background-color: var(--bg-secondary-color);
|
||||
`;
|
||||
|
||||
const BoundingBoxZone = styled.div.attrs(({ top, left, height, width }) => ({
|
||||
|
@ -71,11 +88,12 @@ const BoundingBoxZone = styled.div.attrs(({ top, left, height, width }) => ({
|
|||
pointer-events: none;
|
||||
`;
|
||||
|
||||
export const SelectedItems = ({ edit }) => {
|
||||
export const SelectedItems = () => {
|
||||
const { updateItem } = useItems();
|
||||
|
||||
const { availableActions, actionMap } = useItemActions();
|
||||
const [showAction, setShowAction] = React.useState(false);
|
||||
const [showEdit, setShowEdit] = React.useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
@ -100,6 +118,8 @@ export const SelectedItems = ({ edit }) => {
|
|||
selectedItems.forEach((itemId) => {
|
||||
const elem = document.getElementById(itemId);
|
||||
|
||||
if (!elem) return;
|
||||
|
||||
const {
|
||||
right: x2,
|
||||
bottom: y2,
|
||||
|
@ -159,6 +179,7 @@ export const SelectedItems = ({ edit }) => {
|
|||
React.useEffect(() => {
|
||||
// Show on selection
|
||||
showActionDelay(true);
|
||||
setShowEdit(false);
|
||||
}, [selectedItems, showActionDelay]);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -173,7 +194,7 @@ export const SelectedItems = ({ edit }) => {
|
|||
if (["INPUT", "TEXTAREA"].includes(e.target.tagName)) return;
|
||||
Object.values(actionMap).forEach(
|
||||
({ shortcut, action, edit: whileEdit }) => {
|
||||
if (e.key === shortcut && edit === !!whileEdit) {
|
||||
if (e.key === shortcut && showEdit === !!whileEdit) {
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +204,7 @@ export const SelectedItems = ({ edit }) => {
|
|||
return () => {
|
||||
document.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
}, [actionMap, edit]);
|
||||
}, [actionMap, showEdit]);
|
||||
|
||||
const onSubmitHandler = React.useCallback(
|
||||
(formValues) => {
|
||||
|
@ -246,7 +267,7 @@ export const SelectedItems = ({ edit }) => {
|
|||
});
|
||||
};*/
|
||||
|
||||
const showEditPane = selectedItems.length === 1 && edit;
|
||||
const showEditPane = selectedItems.length === 1 && showEdit;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -269,7 +290,12 @@ export const SelectedItems = ({ edit }) => {
|
|||
)}
|
||||
{showAction && (
|
||||
<ActionPane {...boundingBoxLast}>
|
||||
<h3>#{selectedItems.length}</h3>
|
||||
{selectedItems.length > 1 && (
|
||||
<div className="count">
|
||||
<span className="number">{selectedItems.length}</span>
|
||||
<span>Items</span>
|
||||
</div>
|
||||
)}
|
||||
{availableActions.map((action) => {
|
||||
const {
|
||||
label,
|
||||
|
@ -279,9 +305,14 @@ export const SelectedItems = ({ edit }) => {
|
|||
icon,
|
||||
} = actionMap[action];
|
||||
if (multiple && selectedItems.length < 2) return null;
|
||||
if (onlyEdit && !edit) return null;
|
||||
if (onlyEdit && !showEdit) return null;
|
||||
return (
|
||||
<button key={action} onClick={handler} title={label}>
|
||||
<button
|
||||
className="icon-only"
|
||||
key={action}
|
||||
onClick={handler}
|
||||
title={label}
|
||||
>
|
||||
<img
|
||||
src={icon}
|
||||
style={{ width: "25px", height: "24px" }}
|
||||
|
@ -290,6 +321,17 @@ export const SelectedItems = ({ edit }) => {
|
|||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
<button
|
||||
className="icon-only"
|
||||
onClick={() => setShowEdit((prev) => !prev)}
|
||||
title="Edit"
|
||||
>
|
||||
<img
|
||||
src="https://icongr.am/feather/edit.svg?size=25&color=000000"
|
||||
alt="Edit"
|
||||
/>
|
||||
</button>
|
||||
</ActionPane>
|
||||
)}
|
||||
{boundingBoxLast && <BoundingBoxZone {...boundingBoxLast} />}
|
||||
|
|
|
@ -319,7 +319,7 @@ export const useItemActions = () => {
|
|||
remove: {
|
||||
action: removeSelectedItems,
|
||||
label: t("Remove all"),
|
||||
shortcut: "r",
|
||||
shortcut: "Delete",
|
||||
edit: true,
|
||||
disableDblclick: true,
|
||||
icon: deleteIcon,
|
||||
|
|
|
@ -5,11 +5,12 @@ body {
|
|||
|
||||
:root {
|
||||
--bg-color: #2a2a2aff;
|
||||
--bg-secondary-color: #00a698ff;
|
||||
--bg-secondary-color: #404040ff;
|
||||
--font-color: #f9fbfaff;
|
||||
--color-grey: #404040ff;
|
||||
--color-darkGrey: #777;
|
||||
--color-primary: #db5034ff;
|
||||
--color-secondary: #00a698ff;
|
||||
--color-lightGrey: #9a9a9aff;
|
||||
--color-error: #d43939;
|
||||
--color-success: #28bd14;
|
||||
|
|
|
@ -12,6 +12,7 @@ const StyledModal = styled.div`
|
|||
overflow: auto;
|
||||
background-color: rgb(0, 0, 0);
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
border-radius: 5px;
|
||||
.modal-content {
|
||||
max-width: 50%;
|
||||
position: relative;
|
||||
|
@ -19,6 +20,8 @@ const StyledModal = styled.div`
|
|||
padding: 8px 8px 8px 8px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-secondary-color);
|
||||
box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px,
|
||||
rgba(0, 0, 0, 0.23) 0px 6px 6px;
|
||||
}
|
||||
.close {
|
||||
position: absolute;
|
||||
|
|
|
@ -8,7 +8,7 @@ const Overlay = styled.div`
|
|||
height: 100vh;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: hsla(227, 20%, 20%, 0.9);
|
||||
background-color: var(--bg-color);
|
||||
color: #606984;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
|
|
@ -2,21 +2,17 @@ import React from "react";
|
|||
import styled from "styled-components";
|
||||
|
||||
import { SHOW_WELCOME } from "../utils/settings";
|
||||
import BoardMenu from "../components/BoardMenu";
|
||||
import BoardMenuEdit from "../components/BoardMenuEdit";
|
||||
import GameController from "../components/GameController";
|
||||
import { Board } from "../components/Board";
|
||||
import SelectedItemsPane from "../components/SelectedItemsPane";
|
||||
import { useUsers, SubscribeUserEvents, UserList } from "../components/users";
|
||||
import LoadGameModal from "../components/LoadGameModal";
|
||||
import HelpModal from "./HelpModal";
|
||||
|
||||
import WelcomeModal from "./WelcomeModal";
|
||||
import InfoModal from "./InfoModal";
|
||||
import NavBar from "./NavBar";
|
||||
import AutoSave from "../components/AutoSave";
|
||||
import ImageDropNPaste from "../components/ImageDropNPaste";
|
||||
import { getComponent } from "../components/boardComponents";
|
||||
import { useGame } from "../hooks/useGame";
|
||||
import AddItemButton from "../components/AddItemButton";
|
||||
|
||||
const StyledBoardView = styled.div`
|
||||
width: 100vw;
|
||||
|
@ -29,55 +25,22 @@ const BoardContainer = styled.div`
|
|||
height: 100%;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
background-color: #202b38;
|
||||
background-color: var(--bg-color);
|
||||
`;
|
||||
|
||||
export const BoardView = ({ namespace, edit: editMode = false }) => {
|
||||
const { currentUser, users } = useUsers();
|
||||
const [showLoadGameModal, setShowLoadGameModal] = React.useState(false);
|
||||
const [showHelpModal, setShowHelpModal] = React.useState(false);
|
||||
const [showInfoModal, setShowInfoModal] = React.useState(false);
|
||||
const [showWelcomeModal, setShowWelcomeModal] = React.useState(
|
||||
SHOW_WELCOME && !editMode
|
||||
);
|
||||
const [menuOpen, setMenuOpen] = React.useState(false);
|
||||
const [edit, setEdit] = React.useState(editMode);
|
||||
|
||||
const { gameLoaded } = useGame();
|
||||
|
||||
return (
|
||||
<StyledBoardView>
|
||||
<NavBar
|
||||
setMenuOpen={setMenuOpen}
|
||||
setShowHelpModal={setShowHelpModal}
|
||||
setShowInfoModal={setShowInfoModal}
|
||||
setEditMode={setEdit}
|
||||
edit={edit}
|
||||
/>
|
||||
{!editMode && (
|
||||
<BoardMenu
|
||||
isOpen={menuOpen}
|
||||
setMenuOpen={setMenuOpen}
|
||||
setShowLoadGameModal={setShowLoadGameModal}
|
||||
edit={edit}
|
||||
/>
|
||||
)}
|
||||
{editMode && (
|
||||
<BoardMenuEdit
|
||||
isOpen={menuOpen}
|
||||
setMenuOpen={setMenuOpen}
|
||||
setShowLoadGameModal={setShowLoadGameModal}
|
||||
edit={edit}
|
||||
/>
|
||||
)}
|
||||
<HelpModal show={showHelpModal} setShow={setShowHelpModal} />
|
||||
<InfoModal show={showInfoModal} setShow={setShowInfoModal} />
|
||||
<NavBar setEditMode={setEdit} edit={edit} />
|
||||
<WelcomeModal show={showWelcomeModal} setShow={setShowWelcomeModal} />
|
||||
<LoadGameModal
|
||||
showModal={showLoadGameModal}
|
||||
setShowModal={setShowLoadGameModal}
|
||||
/>
|
||||
|
||||
<SubscribeUserEvents />
|
||||
<AutoSave />
|
||||
{gameLoaded && (
|
||||
|
@ -91,9 +54,9 @@ export const BoardView = ({ namespace, edit: editMode = false }) => {
|
|||
/>
|
||||
</ImageDropNPaste>
|
||||
<SelectedItemsPane edit={edit} />
|
||||
{edit && <GameController />}
|
||||
</BoardContainer>
|
||||
)}
|
||||
<AddItemButton />
|
||||
</StyledBoardView>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -35,7 +35,7 @@ const GameList = styled.ul`
|
|||
|
||||
const Game = styled.li`
|
||||
width: 100%;
|
||||
background-color: hsl(210, 26%, 19%);
|
||||
background-color: var(--bg-secondary-color);
|
||||
color: hsl(210, 14%, 75%);
|
||||
position: relative;
|
||||
min-width: 300px;
|
||||
|
|
|
@ -3,23 +3,24 @@ import { Link } from "react-router-dom";
|
|||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useC2C } from "../hooks/useC2C";
|
||||
|
||||
import styled from "styled-components";
|
||||
|
||||
import HelpModal from "../views/HelpModal";
|
||||
import InfoModal from "../views/InfoModal";
|
||||
import LoadSaveModal from "../views/LoadSaveModal";
|
||||
|
||||
import logo from "../images/logo.png";
|
||||
|
||||
const StyledNavBar = styled.div.attrs(() => ({ className: "nav" }))`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
background-color: var(--color-grey);
|
||||
background-color: var(--bg-secondary-color);
|
||||
z-index: 10;
|
||||
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
|
||||
`;
|
||||
|
||||
const NavBar = ({}) => {
|
||||
const NavBar = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [showLoadGameModal, setShowLoadGameModal] = React.useState(false);
|
||||
|
@ -31,32 +32,32 @@ const NavBar = ({}) => {
|
|||
<StyledNavBar>
|
||||
<div className="nav-left">
|
||||
<Link to="/games/" className="brand">
|
||||
<span>{t("Home")}</span>
|
||||
<img src={logo} />
|
||||
</Link>
|
||||
<a
|
||||
className="button outline"
|
||||
className="button clear icon-only"
|
||||
onClick={() => setShowLoadGameModal((prev) => !prev)}
|
||||
>
|
||||
{t("Load/Save")}
|
||||
<img src="https://icongr.am/feather/save.svg?size=50&color=ffffff" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="nav-center">
|
||||
<h3>Air Board Game</h3>
|
||||
<a
|
||||
className="button outline"
|
||||
className="button clear icon-only"
|
||||
onClick={() => setShowInfoModal((prev) => !prev)}
|
||||
>
|
||||
{t("Info")}
|
||||
<img src="https://icongr.am/feather/info.svg?size=50&color=ffffff" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="nav-right">
|
||||
<a
|
||||
className="button outline"
|
||||
className="button clear icon-only"
|
||||
onClick={() => setShowHelpModal((prev) => !prev)}
|
||||
>
|
||||
{t("Help")}
|
||||
<img src="https://icongr.am/feather/help-circle.svg?size=50&color=ffffff" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Reference in a new issue