diff --git a/src/components/FilterBar.js b/src/components/FilterBar.js
new file mode 100644
index 0000000..cb6aa1d
--- /dev/null
+++ b/src/components/FilterBar.js
@@ -0,0 +1,69 @@
+import React from "react";
+import { Flex, IconButton } from "theme-ui";
+
+import AddIcon from "../icons/AddIcon";
+import SelectMultipleIcon from "../icons/SelectMultipleIcon";
+import SelectSingleIcon from "../icons/SelectSingleIcon";
+
+import Search from "./Search";
+import RadioIconButton from "./RadioIconButton";
+
+function FilterBar({
+ onFocus,
+ search,
+ onSearchChange,
+ selectMode,
+ onSelectModeChange,
+ onAdd,
+ addTitle,
+}) {
+ return (
+
+
+
+ onSelectModeChange("single")}
+ isSelected={selectMode === "single"}
+ >
+
+
+ onSelectModeChange("multiple")}
+ isSelected={selectMode === "multiple" || selectMode === "range"}
+ >
+
+
+
+
+
+
+
+ );
+}
+
+export default FilterBar;
diff --git a/src/components/map/controls/RadioIconButton.js b/src/components/RadioIconButton.js
similarity index 100%
rename from src/components/map/controls/RadioIconButton.js
rename to src/components/RadioIconButton.js
diff --git a/src/components/map/MapControls.js b/src/components/map/MapControls.js
index 59c8c87..fbd5cf2 100644
--- a/src/components/map/MapControls.js
+++ b/src/components/map/MapControls.js
@@ -1,7 +1,7 @@
import React, { useState, Fragment } from "react";
import { IconButton, Flex, Box } from "theme-ui";
-import RadioIconButton from "./controls/RadioIconButton";
+import RadioIconButton from "../RadioIconButton";
import Divider from "../Divider";
import SelectMapButton from "./SelectMapButton";
diff --git a/src/components/map/MapTiles.js b/src/components/map/MapTiles.js
index 4198bc1..61915a3 100644
--- a/src/components/map/MapTiles.js
+++ b/src/components/map/MapTiles.js
@@ -4,18 +4,13 @@ import SimpleBar from "simplebar-react";
import { useMedia } from "react-media";
import Case from "case";
-import AddIcon from "../../icons/AddIcon";
import RemoveMapIcon from "../../icons/RemoveMapIcon";
import ResetMapIcon from "../../icons/ResetMapIcon";
-import SelectMultipleIcon from "../../icons/SelectMultipleIcon";
-import SelectSingleIcon from "../../icons/SelectSingleIcon";
import GroupIcon from "../../icons/GroupIcon";
-import RadioIconButton from "./controls/RadioIconButton";
-
import MapTile from "./MapTile";
import Link from "../Link";
-import Search from "../Search";
+import FilterBar from "../FilterBar";
import DatabaseContext from "../../contexts/DatabaseContext";
@@ -82,56 +77,15 @@ function MapTiles({
return (
- onMapSelect()}
- >
-
-
- onSelectModeChange("single")}
- isSelected={selectMode === "single"}
- >
-
-
- onSelectModeChange("multiple")}
- isSelected={selectMode === "multiple" || selectMode === "range"}
- >
-
-
-
-
-
-
-
+ search={search}
+ onSearchChange={onSearchChange}
+ selectMode={selectMode}
+ onSelectModeChange={onSelectModeChange}
+ onAdd={onMapAdd}
+ addTitle="Add Map"
+ />
onMapSelect()}
>
- {/* Render ungrouped maps, grouped maps then default maps */}
{groups.map((group) => (
);
}
diff --git a/src/contexts/MapDataContext.js b/src/contexts/MapDataContext.js
index fe25424..fe81c14 100644
--- a/src/contexts/MapDataContext.js
+++ b/src/contexts/MapDataContext.js
@@ -143,6 +143,22 @@ export function MapDataProvider({ children }) {
});
}
+ async function updateMaps(ids, update) {
+ await Promise.all(
+ ids.map((id) => database.table("maps").update(id, update))
+ );
+ setMaps((prevMaps) => {
+ const newMaps = [...prevMaps];
+ for (let id of ids) {
+ const i = newMaps.findIndex((map) => map.id === id);
+ if (i > -1) {
+ newMaps[i] = { ...newMaps[i], ...update };
+ }
+ }
+ return newMaps;
+ });
+ }
+
async function updateMapState(id, update) {
await database.table("states").update(id, update);
setMapStates((prevMapStates) => {
@@ -218,6 +234,7 @@ export function MapDataProvider({ children }) {
removeMaps,
resetMap,
updateMap,
+ updateMaps,
updateMapState,
putMap,
getMap,
diff --git a/src/contexts/TokenDataContext.js b/src/contexts/TokenDataContext.js
index aee9f79..286fc1e 100644
--- a/src/contexts/TokenDataContext.js
+++ b/src/contexts/TokenDataContext.js
@@ -26,6 +26,7 @@ export function TokenDataProvider({ children }) {
...defaultToken,
id: `__default-${defaultToken.name}`,
owner: userId,
+ group: "default",
});
}
return defaultTokensWithIds;
@@ -60,6 +61,14 @@ export function TokenDataProvider({ children }) {
});
}
+ async function removeTokens(ids) {
+ await database.table("tokens").bulkDelete(ids);
+ setTokens((prevTokens) => {
+ const filtered = prevTokens.filter((token) => !ids.includes(token.id));
+ return filtered;
+ });
+ }
+
async function updateToken(id, update) {
const change = { ...update, lastModified: Date.now() };
await database.table("tokens").update(id, change);
@@ -73,6 +82,23 @@ export function TokenDataProvider({ children }) {
});
}
+ async function updateTokens(ids, update) {
+ const change = { ...update, lastModified: Date.now() };
+ await Promise.all(
+ ids.map((id) => database.table("tokens").update(id, change))
+ );
+ setTokens((prevTokens) => {
+ const newTokens = [...prevTokens];
+ for (let id of ids) {
+ const i = newTokens.findIndex((token) => token.id === id);
+ if (i > -1) {
+ newTokens[i] = { ...newTokens[i], ...change };
+ }
+ }
+ return newTokens;
+ });
+ }
+
async function putToken(token) {
await database.table("tokens").put(token);
setTokens((prevTokens) => {
@@ -128,7 +154,9 @@ export function TokenDataProvider({ children }) {
ownedTokens,
addToken,
removeToken,
+ removeTokens,
updateToken,
+ updateTokens,
putToken,
getToken,
tokensById,
diff --git a/src/database.js b/src/database.js
index 91be7af..d2c92e1 100644
--- a/src/database.js
+++ b/src/database.js
@@ -217,6 +217,17 @@ function loadVersions(db) {
map.group = "";
});
});
+ // v1.6.0 - Added token grouping
+ db.version(14)
+ .stores({})
+ .upgrade((tx) => {
+ return tx
+ .table("tokens")
+ .toCollection()
+ .modify((token) => {
+ token.group = "";
+ });
+ });
}
// Get the dexie database used in DatabaseContext
diff --git a/src/helpers/select.js b/src/helpers/select.js
new file mode 100644
index 0000000..eb81b95
--- /dev/null
+++ b/src/helpers/select.js
@@ -0,0 +1,133 @@
+import { useEffect, useState } from "react";
+import Fuse from "fuse.js";
+
+import { groupBy } from "./shared";
+
+/**
+ * Helpers for the SelectMapModal and SelectTokenModal
+ */
+
+// Helper for generating search results for items
+export function useSearch(items, search) {
+ const [filteredItems, setFilteredItems] = useState([]);
+ const [filteredItemScores, setFilteredItemScores] = useState({});
+ const [fuse, setFuse] = useState();
+
+ // Update search index when items change
+ useEffect(() => {
+ setFuse(new Fuse(items, { keys: ["name", "group"], includeScore: true }));
+ }, [items]);
+
+ // Perform search when search changes
+ useEffect(() => {
+ if (search) {
+ const query = fuse.search(search);
+ setFilteredItems(query.map((result) => result.item));
+ setFilteredItemScores(
+ query.reduce(
+ (acc, value) => ({ ...acc, [value.item.id]: value.score }),
+ {}
+ )
+ );
+ }
+ }, [search, items, fuse]);
+
+ return [filteredItems, filteredItemScores];
+}
+
+// Helper for grouping items
+export function useGroup(items, filteredItems, useFiltered, filteredScores) {
+ const itemsByGroup = groupBy(useFiltered ? filteredItems : items, "group");
+ // Get the groups of the items sorting by the average score if we're filtering or the alphabetical order
+ // with "" at the start and "default" at the end if not
+ let itemGroups = Object.keys(itemsByGroup);
+ if (useFiltered) {
+ itemGroups.sort((a, b) => {
+ const aScore = itemsByGroup[a].reduce(
+ (acc, item) => (acc + filteredScores[item.id]) / 2
+ );
+ const bScore = itemsByGroup[b].reduce(
+ (acc, item) => (acc + filteredScores[item.id]) / 2
+ );
+ return aScore - bScore;
+ });
+ } else {
+ itemGroups.sort((a, b) => {
+ if (a === "" || b === "default") {
+ return -1;
+ }
+ if (b === "" || a === "default") {
+ return 1;
+ }
+ return a.localeCompare(b);
+ });
+ }
+ return [itemsByGroup, itemGroups];
+}
+
+// Helper for handling selecting items
+export function handleItemSelect(
+ item,
+ selectMode,
+ selectedIds,
+ setSelectedIds,
+ itemsByGroup,
+ itemGroups
+) {
+ if (!item) {
+ setSelectedIds([]);
+ return;
+ }
+ switch (selectMode) {
+ case "single":
+ setSelectedIds([item.id]);
+ break;
+ case "multiple":
+ setSelectedIds((prev) => {
+ if (prev.includes(item.id)) {
+ return prev.filter((id) => id !== item.id);
+ } else {
+ return [...prev, item.id];
+ }
+ });
+ break;
+ case "range":
+ // Create items array
+ let items = itemGroups.reduce(
+ (acc, group) => [...acc, ...itemsByGroup[group]],
+ []
+ );
+
+ // Add all items inbetween the previous selected item and the current selected
+ if (selectedIds.length > 0) {
+ const mapIndex = items.findIndex((m) => m.id === item.id);
+ const lastIndex = items.findIndex(
+ (m) => m.id === selectedIds[selectedIds.length - 1]
+ );
+ let idsToAdd = [];
+ let idsToRemove = [];
+ const direction = mapIndex > lastIndex ? 1 : -1;
+ for (
+ let i = lastIndex + direction;
+ direction < 0 ? i >= mapIndex : i <= mapIndex;
+ i += direction
+ ) {
+ const itemId = items[i].id;
+ if (selectedIds.includes(itemId)) {
+ idsToRemove.push(itemId);
+ } else {
+ idsToAdd.push(itemId);
+ }
+ }
+ setSelectedIds((prev) => {
+ let ids = [...prev, ...idsToAdd];
+ return ids.filter((id) => !idsToRemove.includes(id));
+ });
+ } else {
+ setSelectedIds([item.id]);
+ }
+ break;
+ default:
+ setSelectedIds([]);
+ }
+}
diff --git a/src/modals/EditMapModal.js b/src/modals/EditMapModal.js
index 7341529..6a11475 100644
--- a/src/modals/EditMapModal.js
+++ b/src/modals/EditMapModal.js
@@ -9,7 +9,7 @@ import MapDataContext from "../contexts/MapDataContext";
import { isEmpty } from "../helpers/shared";
-function SelectMapModal({ isOpen, onDone, map, mapState }) {
+function EditMapModal({ isOpen, onDone, map, mapState }) {
const { updateMap, updateMapState } = useContext(MapDataContext);
function handleClose() {
@@ -102,4 +102,4 @@ function SelectMapModal({ isOpen, onDone, map, mapState }) {
);
}
-export default SelectMapModal;
+export default EditMapModal;
diff --git a/src/modals/EditTokenModal.js b/src/modals/EditTokenModal.js
new file mode 100644
index 0000000..6bf2c37
--- /dev/null
+++ b/src/modals/EditTokenModal.js
@@ -0,0 +1,75 @@
+import React, { useState, useContext } from "react";
+import { Button, Flex, Label } from "theme-ui";
+
+import Modal from "../components/Modal";
+import TokenSettings from "../components/token/TokenSettings";
+
+import TokenDataContext from "../contexts/TokenDataContext";
+
+import { isEmpty } from "../helpers/shared";
+
+function EditTokenModal({ isOpen, onDone, token }) {
+ const { updateToken } = useContext(TokenDataContext);
+
+ function handleClose() {
+ onDone();
+ }
+
+ async function handleSave() {
+ await applyTokenChanges();
+ onDone();
+ }
+
+ const [tokenSettingChanges, setTokenSettingChanges] = useState({});
+
+ function handleTokenSettingsChange(key, value) {
+ setTokenSettingChanges((prevChanges) => ({ ...prevChanges, [key]: value }));
+ }
+
+ async function applyTokenChanges() {
+ if (token && !isEmpty(tokenSettingChanges)) {
+ // Ensure size value is positive
+ let verifiedChanges = { ...tokenSettingChanges };
+ if ("defaultSize" in verifiedChanges) {
+ verifiedChanges.defaultSize = verifiedChanges.defaultSize || 1;
+ }
+
+ await updateToken(token.id, verifiedChanges);
+ setTokenSettingChanges({});
+ }
+ }
+
+ const selectedTokenWithChanges = {
+ ...token,
+ ...tokenSettingChanges,
+ };
+
+ const [showMoreSettings, setShowMoreSettings] = useState(false);
+
+ return (
+
+
+
+ Edit token
+
+
+
+
+
+ );
+}
+
+export default EditTokenModal;
diff --git a/src/modals/SelectMapModal.js b/src/modals/SelectMapModal.js
index afdcf7d..86edd84 100644
--- a/src/modals/SelectMapModal.js
+++ b/src/modals/SelectMapModal.js
@@ -1,7 +1,6 @@
-import React, { useRef, useState, useContext, useEffect } from "react";
+import React, { useRef, useState, useContext } from "react";
import { Button, Flex, Label } from "theme-ui";
import shortid from "shortid";
-import Fuse from "fuse.js";
import EditMapModal from "./EditMapModal";
import EditGroupModal from "./EditGroupModal";
@@ -13,13 +12,12 @@ import LoadingOverlay from "../components/LoadingOverlay";
import blobToBuffer from "../helpers/blobToBuffer";
import useKeyboard from "../helpers/useKeyboard";
+import { resizeImage } from "../helpers/image";
+import { useSearch, useGroup, handleItemSelect } from "../helpers/select";
import MapDataContext from "../contexts/MapDataContext";
import AuthContext from "../contexts/AuthContext";
-import { resizeImage } from "../helpers/image";
-import { groupBy } from "../helpers/shared";
-
const defaultMapSize = 22;
const defaultMapProps = {
// Grid type
@@ -54,33 +52,14 @@ function SelectMapModal({
removeMaps,
resetMap,
updateMap,
+ updateMaps,
} = useContext(MapDataContext);
/**
* Search
*/
- const [filteredMaps, setFilteredMaps] = useState([]);
- const [filteredMapScores, setFilteredMapScores] = useState({});
- const [fuse, setFuse] = useState();
const [search, setSearch] = useState("");
-
- // Update search index when maps change
- useEffect(() => {
- setFuse(
- new Fuse(ownedMaps, { keys: ["name", "group"], includeScore: true })
- );
- }, [ownedMaps]);
-
- // Perform search when search changes
- useEffect(() => {
- if (search) {
- const query = fuse.search(search);
- setFilteredMaps(query.map((result) => result.item));
- setFilteredMapScores(
- query.reduce((acc, value) => ({ ...acc, [value.item.id]: value.score }))
- );
- }
- }, [search, ownedMaps, fuse]);
+ const [filteredMaps, filteredMapScores] = useSearch(ownedMaps, search);
function handleSearchChange(event) {
setSearch(event.target.value);
@@ -93,36 +72,15 @@ function SelectMapModal({
async function handleMapsGroup(group) {
setIsGroupModalOpen(false);
- for (let id of selectedMapIds) {
- await updateMap(id, { group });
- }
+ updateMaps(selectedMapIds, { group });
}
- const mapsByGroup = groupBy(search ? filteredMaps : ownedMaps, "group");
- // Get the groups of the maps sorting by the average score if we're filtering or the alphabetical order
- // with "" at the start and "default" at the end if not
- let mapGroups = Object.keys(mapsByGroup);
- if (search) {
- mapGroups.sort((a, b) => {
- const aScore = mapsByGroup[a].reduce(
- (acc, map) => (acc + filteredMapScores[map.id]) / 2
- );
- const bScore = mapsByGroup[b].reduce(
- (acc, map) => (acc + filteredMapScores[map.id]) / 2
- );
- return aScore - bScore;
- });
- } else {
- mapGroups.sort((a, b) => {
- if (a === "" || b === "default") {
- return -1;
- }
- if (b === "" || a === "default") {
- return 1;
- }
- return a.localeCompare(b);
- });
- }
+ const [mapsByGroup, mapGroups] = useGroup(
+ ownedMaps,
+ filteredMaps,
+ !!search,
+ filteredMapScores
+ );
/**
* Image Upload
@@ -276,63 +234,15 @@ function SelectMapModal({
// Either single, multiple or range
const [selectMode, setSelectMode] = useState("single");
- async function handleMapSelect(map) {
- if (map) {
- switch (selectMode) {
- case "single":
- setSelectedMapIds([map.id]);
- break;
- case "multiple":
- setSelectedMapIds((prev) => {
- if (prev.includes(map.id)) {
- return prev.filter((id) => id !== map.id);
- } else {
- return [...prev, map.id];
- }
- });
- break;
- case "range":
- // Create maps array
- let maps = mapGroups.reduce(
- (acc, group) => [...acc, ...mapsByGroup[group]],
- []
- );
-
- // Add all items inbetween the previous selected map and the current selected
- if (selectedMapIds.length > 0) {
- const mapIndex = maps.findIndex((m) => m.id === map.id);
- const lastIndex = maps.findIndex(
- (m) => m.id === selectedMapIds[selectedMapIds.length - 1]
- );
- let idsToAdd = [];
- let idsToRemove = [];
- const direction = mapIndex > lastIndex ? 1 : -1;
- for (
- let i = lastIndex + direction;
- direction < 0 ? i >= mapIndex : i <= mapIndex;
- i += direction
- ) {
- const mapId = maps[i].id;
- if (selectedMapIds.includes(mapId)) {
- idsToRemove.push(mapId);
- } else {
- idsToAdd.push(mapId);
- }
- }
- setSelectedMapIds((prev) => {
- let ids = [...prev, ...idsToAdd];
- return ids.filter((id) => !idsToRemove.includes(id));
- });
- } else {
- setSelectedMapIds([map.id]);
- }
- break;
- default:
- setSelectedMapIds([]);
- }
- } else {
- setSelectedMapIds([]);
- }
+ function handleMapSelect(map) {
+ handleItemSelect(
+ map,
+ selectMode,
+ selectedMapIds,
+ setSelectedMapIds,
+ mapsByGroup,
+ mapGroups
+ );
}
/**
diff --git a/src/modals/SelectTokensModal.js b/src/modals/SelectTokensModal.js
index d355f3c..82080a9 100644
--- a/src/modals/SelectTokensModal.js
+++ b/src/modals/SelectTokensModal.js
@@ -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 (
@@ -155,27 +200,48 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
Edit or import a token
setIsEditModalOpen(true)}
+ onTokensRemove={handleTokensRemove}
+ selectedTokens={selectedTokens}
onTokenSelect={handleTokenSelect}
- onTokenRemove={handleTokenRemove}
- />
- setIsGroupModalOpen(true)}
/>
+ setIsEditModalOpen(false)}
+ token={selectedTokens.length === 1 && selectedTokens[0]}
+ />
+ 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))
+ }
+ />
);
}