Added search and groups to token select, refactored map search and group
This commit is contained in:
@@ -2,42 +2,66 @@ import React, { useRef, useContext, useState } from "react";
|
||||
import { Flex, Label, Button } from "theme-ui";
|
||||
import shortid from "shortid";
|
||||
|
||||
import EditTokenModal from "./EditTokenModal";
|
||||
import EditGroupModal from "./EditGroupModal";
|
||||
|
||||
import Modal from "../components/Modal";
|
||||
import ImageDrop from "../components/ImageDrop";
|
||||
import TokenTiles from "../components/token/TokenTiles";
|
||||
import TokenSettings from "../components/token/TokenSettings";
|
||||
|
||||
import blobToBuffer from "../helpers/blobToBuffer";
|
||||
import useKeyboard from "../helpers/useKeyboard";
|
||||
import { useSearch, useGroup, handleItemSelect } from "../helpers/select";
|
||||
|
||||
import TokenDataContext from "../contexts/TokenDataContext";
|
||||
import AuthContext from "../contexts/AuthContext";
|
||||
import { isEmpty } from "../helpers/shared";
|
||||
|
||||
function SelectTokensModal({ isOpen, onRequestClose }) {
|
||||
const { userId } = useContext(AuthContext);
|
||||
const { ownedTokens, addToken, removeToken, updateToken } = useContext(
|
||||
const { ownedTokens, addToken, removeTokens, updateTokens } = useContext(
|
||||
TokenDataContext
|
||||
);
|
||||
const fileInputRef = useRef();
|
||||
|
||||
const [imageLoading, setImageLoading] = useState(false);
|
||||
/**
|
||||
* Search
|
||||
*/
|
||||
const [search, setSearch] = useState("");
|
||||
const [filteredTokens, filteredTokenScores] = useSearch(ownedTokens, search);
|
||||
|
||||
const [selectedTokenId, setSelectedTokenId] = useState(null);
|
||||
const selectedToken = ownedTokens.find(
|
||||
(token) => token.id === selectedTokenId
|
||||
function handleSearchChange(event) {
|
||||
setSearch(event.target.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Group
|
||||
*/
|
||||
const [isGroupModalOpen, setIsGroupModalOpen] = useState(false);
|
||||
|
||||
async function handleTokensGroup(group) {
|
||||
setIsGroupModalOpen(false);
|
||||
await updateTokens(selectedTokenIds, { group });
|
||||
}
|
||||
|
||||
const [tokensByGroup, tokenGroups] = useGroup(
|
||||
ownedTokens,
|
||||
filteredTokens,
|
||||
!!search,
|
||||
filteredTokenScores
|
||||
);
|
||||
|
||||
/**
|
||||
* Image Upload
|
||||
*/
|
||||
|
||||
const fileInputRef = useRef();
|
||||
const [imageLoading, setImageLoading] = useState(false);
|
||||
|
||||
function openImageDialog() {
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.click();
|
||||
}
|
||||
}
|
||||
|
||||
function handleTokenAdd(token) {
|
||||
addToken(token);
|
||||
setSelectedTokenId(token.id);
|
||||
}
|
||||
|
||||
async function handleImagesUpload(files) {
|
||||
for (let file of files) {
|
||||
await handleImageUpload(file);
|
||||
@@ -80,6 +104,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
||||
defaultSize: 1,
|
||||
category: "character",
|
||||
hideInSidebar: false,
|
||||
group: "",
|
||||
});
|
||||
setImageLoading(false);
|
||||
resolve();
|
||||
@@ -89,52 +114,72 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
||||
});
|
||||
}
|
||||
|
||||
async function handleTokenSelect(token) {
|
||||
await applyTokenChanges();
|
||||
setSelectedTokenId(token.id);
|
||||
/**
|
||||
* Token controls
|
||||
*/
|
||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||
const [selectedTokenIds, setSelectedTokenIds] = useState([]);
|
||||
const selectedTokens = ownedTokens.filter((token) =>
|
||||
selectedTokenIds.includes(token.id)
|
||||
);
|
||||
|
||||
function handleTokenAdd(token) {
|
||||
addToken(token);
|
||||
setSelectedTokenIds([token.id]);
|
||||
}
|
||||
|
||||
async function handleTokenRemove(id) {
|
||||
await removeToken(id);
|
||||
setSelectedTokenId(null);
|
||||
setTokenSettingChanges({});
|
||||
async function handleTokensRemove() {
|
||||
await removeTokens(selectedTokenIds);
|
||||
setSelectedTokenIds([]);
|
||||
}
|
||||
|
||||
// Either single, multiple or range
|
||||
const [selectMode, setSelectMode] = useState("single");
|
||||
|
||||
async function handleTokenSelect(token) {
|
||||
handleItemSelect(
|
||||
token,
|
||||
selectMode,
|
||||
selectedTokenIds,
|
||||
setSelectedTokenIds,
|
||||
tokensByGroup,
|
||||
tokenGroups
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Token settings
|
||||
* Shortcuts
|
||||
*/
|
||||
const [showMoreSettings, setShowMoreSettings] = useState(false);
|
||||
|
||||
const [tokenSettingChanges, setTokenSettingChanges] = useState({});
|
||||
|
||||
function handleTokenSettingsChange(key, value) {
|
||||
setTokenSettingChanges((prevChanges) => ({ ...prevChanges, [key]: value }));
|
||||
}
|
||||
|
||||
async function applyTokenChanges() {
|
||||
if (selectedTokenId && !isEmpty(tokenSettingChanges)) {
|
||||
// Ensure size value is positive
|
||||
let verifiedChanges = { ...tokenSettingChanges };
|
||||
if ("defaultSize" in verifiedChanges) {
|
||||
verifiedChanges.defaultSize = verifiedChanges.defaultSize || 1;
|
||||
}
|
||||
|
||||
await updateToken(selectedTokenId, verifiedChanges);
|
||||
setTokenSettingChanges({});
|
||||
function handleKeyDown({ key }) {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
if (key === "Shift") {
|
||||
setSelectMode("range");
|
||||
}
|
||||
if (key === "Control" || key === "Meta") {
|
||||
setSelectMode("multiple");
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRequestClose() {
|
||||
await applyTokenChanges();
|
||||
onRequestClose();
|
||||
function handleKeyUp({ key }) {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
if (key === "Shift" && selectMode === "range") {
|
||||
setSelectMode("single");
|
||||
}
|
||||
if ((key === "Control" || key === "Meta") && selectMode === "multiple") {
|
||||
setSelectMode("single");
|
||||
}
|
||||
}
|
||||
|
||||
const selectedTokenWithChanges = { ...selectedToken, ...tokenSettingChanges };
|
||||
useKeyboard(handleKeyDown, handleKeyUp);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onRequestClose={handleRequestClose}
|
||||
onRequestClose={onRequestClose}
|
||||
style={{ maxWidth: "542px", width: "calc(100% - 16px)" }}
|
||||
>
|
||||
<ImageDrop onDrop={handleImagesUpload} dropText="Drop token to upload">
|
||||
@@ -155,27 +200,48 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
||||
Edit or import a token
|
||||
</Label>
|
||||
<TokenTiles
|
||||
tokens={ownedTokens}
|
||||
tokens={tokensByGroup}
|
||||
groups={tokenGroups}
|
||||
onTokenAdd={openImageDialog}
|
||||
selectedToken={selectedTokenWithChanges}
|
||||
onTokenEdit={() => setIsEditModalOpen(true)}
|
||||
onTokensRemove={handleTokensRemove}
|
||||
selectedTokens={selectedTokens}
|
||||
onTokenSelect={handleTokenSelect}
|
||||
onTokenRemove={handleTokenRemove}
|
||||
/>
|
||||
<TokenSettings
|
||||
token={selectedTokenWithChanges}
|
||||
showMore={showMoreSettings}
|
||||
onSettingsChange={handleTokenSettingsChange}
|
||||
onShowMoreChange={setShowMoreSettings}
|
||||
selectMode={selectMode}
|
||||
onSelectModeChange={setSelectMode}
|
||||
search={search}
|
||||
onSearchChange={handleSearchChange}
|
||||
onTokensGroup={() => setIsGroupModalOpen(true)}
|
||||
/>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={imageLoading}
|
||||
onClick={handleRequestClose}
|
||||
onClick={onRequestClose}
|
||||
>
|
||||
Done
|
||||
</Button>
|
||||
</Flex>
|
||||
</ImageDrop>
|
||||
<EditTokenModal
|
||||
isOpen={isEditModalOpen}
|
||||
onDone={() => setIsEditModalOpen(false)}
|
||||
token={selectedTokens.length === 1 && selectedTokens[0]}
|
||||
/>
|
||||
<EditGroupModal
|
||||
isOpen={isGroupModalOpen}
|
||||
onChange={handleTokensGroup}
|
||||
groups={tokenGroups.filter(
|
||||
(group) => group !== "" && group !== "default"
|
||||
)}
|
||||
onRequestClose={() => setIsGroupModalOpen(false)}
|
||||
// Select the default group by testing whether all selected tokens are the same
|
||||
defaultGroup={
|
||||
selectedTokens.length > 0 &&
|
||||
selectedTokens
|
||||
.map((map) => map.group)
|
||||
.reduce((prev, curr) => (prev === curr ? curr : undefined))
|
||||
}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user