Make tile groups selectable

This commit is contained in:
Mitchell McCaffrey 2021-05-20 12:22:07 +10:00
parent 05968c1964
commit ef73ca0179
10 changed files with 223 additions and 148 deletions

View File

@ -6,8 +6,8 @@ import MapTileImage from "./MapTileImage";
function MapTile({
map,
isSelected,
onMapSelect,
onMapEdit,
onSelect,
onEdit,
onDone,
canEdit,
badges,
@ -16,8 +16,8 @@ function MapTile({
<Tile
title={map.name}
isSelected={isSelected}
onSelect={() => onMapSelect(map)}
onEdit={() => onMapEdit(map.id)}
onSelect={() => onSelect({ id: map.id })}
onEdit={() => onEdit(map.id)}
onDoubleClick={() => canEdit && onDone()}
canEdit={canEdit}
badges={badges}

View File

@ -3,20 +3,13 @@ import React from "react";
import Tile from "../Tile";
import MapTileImage from "./MapTileImage";
function MapTileGroup({
group,
maps,
isSelected,
onGroupSelect,
onOpen,
canOpen,
}) {
function MapTileGroup({ group, maps, isSelected, onSelect, onOpen, canOpen }) {
return (
<Tile
title={group.name}
isSelected={isSelected}
// onSelect={() => onGroupSelect(group)}
// onDoubleClick={() => canOpen && onOpen()}
onSelect={() => onSelect(group)}
onDoubleClick={() => canOpen && onOpen()}
columns="1fr 1fr"
>
{maps.slice(0, 4).map((map) => (

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { Flex, Box, Text, IconButton, Close, Grid } from "theme-ui";
import SimpleBar from "simplebar-react";
@ -13,16 +13,16 @@ import FilterBar from "../FilterBar";
import Sortable from "../drag/Sortable";
import SortableTiles from "../drag/SortableTiles";
import { useDatabase } from "../../contexts/DatabaseContext";
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
import { groupsFromIds, itemsFromGroups } from "../../helpers/select";
function MapTiles({
maps,
mapStates,
groups,
selectedMaps,
selectedMapStates,
onMapSelect,
selectedGroupIds,
onTileSelect,
onMapsRemove,
onMapsReset,
onMapAdd,
@ -33,46 +33,65 @@ function MapTiles({
search,
onSearchChange,
onMapsGroup,
databaseDisabled,
}) {
const { databaseStatus } = useDatabase();
const layout = useResponsiveLayout();
let hasMapState = false;
for (let state of selectedMapStates) {
if (
Object.values(state.tokens).length > 0 ||
Object.values(state.drawShapes).length > 0 ||
Object.values(state.fogShapes).length > 0 ||
Object.values(state.notes).length > 0
) {
hasMapState = true;
break;
}
}
const [hasMapState, setHasMapState] = useState(false);
const [hasSelectedDefaultMap, setHasSelectedDefaultMap] = useState(false);
let hasSelectedDefaultMap = selectedMaps.some(
(map) => map.type === "default"
);
useEffect(() => {
const selectedGroups = groupsFromIds(selectedGroupIds, groups);
const selectedMaps = itemsFromGroups(selectedGroups, maps);
const selectedMapStates = itemsFromGroups(
selectedGroups,
mapStates,
true,
"mapId"
);
setHasSelectedDefaultMap(
selectedMaps.some((map) => map.type === "default")
);
let _hasMapState = false;
for (let state of selectedMapStates) {
if (
Object.values(state.tokens).length > 0 ||
Object.values(state.drawShapes).length > 0 ||
Object.values(state.fogShapes).length > 0 ||
Object.values(state.notes).length > 0
) {
_hasMapState = true;
break;
}
}
setHasMapState(_hasMapState);
}, [selectedGroupIds, maps, mapStates, groups]);
function groupToMapTile(group) {
if (group.type === "item") {
const map = maps.find((map) => map.id === group.id);
const isSelected = selectedMaps.includes(map);
const isSelected = selectedGroupIds.includes(group.id);
return (
<MapTile
key={map.id}
map={map}
isSelected={isSelected}
onMapSelect={onMapSelect}
onMapEdit={onMapEdit}
onSelect={onTileSelect}
onEdit={onMapEdit}
onDone={onDone}
canEdit={
isSelected && selectMode === "single" && selectedMaps.length === 1
isSelected &&
selectMode === "single" &&
selectedGroupIds.length === 1
}
badges={[`${map.grid.size.x}x${map.grid.size.y}`]}
/>
);
} else {
const isSelected = selectedGroupIds.includes(group.id);
return (
<MapTileGroup
key={group.id}
@ -80,12 +99,14 @@ function MapTiles({
maps={group.items.map((item) =>
maps.find((map) => map.id === item.id)
)}
isSelected={isSelected}
onSelect={onTileSelect}
/>
);
}
}
const multipleSelected = selectedMaps.length > 1;
const multipleSelected = selectedGroupIds.length > 1;
return (
<SortableTiles
@ -95,7 +116,7 @@ function MapTiles({
>
<Box sx={{ position: "relative" }}>
<FilterBar
onFocus={() => onMapSelect()}
onFocus={() => onTileSelect()}
search={search}
onSearchChange={onSearchChange}
selectMode={selectMode}
@ -111,7 +132,7 @@ function MapTiles({
<Grid
p={2}
pb={4}
pt={databaseStatus === "disabled" ? 4 : 2}
pt={databaseDisabled ? 4 : 2}
bg="muted"
sx={{
borderRadius: "4px",
@ -120,7 +141,7 @@ function MapTiles({
}}
gap={2}
columns={layout.gridTemplate}
onClick={() => onMapSelect()}
onClick={() => onTileSelect()}
>
{groups.map((group) => (
<Sortable id={group.id} key={group.id}>
@ -129,7 +150,7 @@ function MapTiles({
))}
</Grid>
</SimpleBar>
{databaseStatus === "disabled" && (
{databaseDisabled && (
<Box
sx={{
position: "absolute",
@ -148,7 +169,7 @@ function MapTiles({
</Text>
</Box>
)}
{selectedMaps.length > 0 && (
{selectedGroupIds.length > 0 && (
<Flex
sx={{
position: "absolute",
@ -162,7 +183,7 @@ function MapTiles({
<Close
title="Clear Selection"
aria-label="Clear Selection"
onClick={() => onMapSelect()}
onClick={() => onTileSelect()}
/>
<Flex>
<IconButton

View File

@ -7,7 +7,7 @@ function TokenTileGroup({
group,
tokens,
isSelected,
onGroupSelect,
onSelect,
onOpen,
canOpen,
}) {
@ -15,8 +15,8 @@ function TokenTileGroup({
<Tile
title={group.name}
isSelected={isSelected}
// onSelect={() => onGroupSelect(group)}
// onDoubleClick={() => canOpen && onOpen()}
onSelect={() => onSelect(group)}
onDoubleClick={() => canOpen && onOpen()}
columns="1fr 1fr"
>
{tokens.slice(0, 4).map((token) => (

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useState, useEffect } from "react";
import { Flex, Box, Text, IconButton, Close, Grid } from "theme-ui";
import SimpleBar from "simplebar-react";
@ -14,17 +14,17 @@ import FilterBar from "../FilterBar";
import Sortable from "../drag/Sortable";
import SortableTiles from "../drag/SortableTiles";
import { useDatabase } from "../../contexts/DatabaseContext";
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
import { groupsFromIds, itemsFromGroups } from "../../helpers/select";
function TokenTiles({
tokens,
groups,
selectedGroupIds,
onTileSelect,
onTokenAdd,
onTokenEdit,
onTokenSelect,
selectedTokens,
onTokensRemove,
selectMode,
onSelectModeChange,
@ -32,36 +32,45 @@ function TokenTiles({
onSearchChange,
onTokensGroup,
onTokensHide,
databaseDisabled,
}) {
const { databaseStatus } = useDatabase();
const layout = useResponsiveLayout();
let hasSelectedDefaultToken = selectedTokens.some(
(token) => token.type === "default"
);
let allTokensVisible = selectedTokens.every((token) => !token.hideInSidebar);
const [hasSelectedDefaultToken, setHasSelectedDefaultToken] = useState(false);
const [allTokensVisible, setAllTokensVisisble] = useState(false);
useEffect(() => {
const selectedGroups = groupsFromIds(selectedGroupIds, groups);
const selectedTokens = itemsFromGroups(selectedGroups, tokens);
setHasSelectedDefaultToken(
selectedTokens.some((token) => token.type === "default")
);
setAllTokensVisisble(selectedTokens.every((token) => !token.hideInSidebar));
}, [selectedGroupIds, tokens, groups]);
function groupToTokenTile(group) {
if (group.type === "item") {
const token = tokens.find((token) => token.id === group.id);
const isSelected = selectedTokens.includes(token);
const isSelected = selectedGroupIds.includes(group.id);
return (
<TokenTile
key={token.id}
token={token}
isSelected={isSelected}
onTokenSelect={onTokenSelect}
onTokenSelect={onTileSelect}
onTokenEdit={onTokenEdit}
canEdit={
isSelected &&
token.type !== "default" &&
selectMode === "single" &&
selectedTokens.length === 1
selectedGroupIds.length === 1
}
badges={[`${token.defaultSize}x`]}
/>
);
} else {
const isSelected = selectedGroupIds.includes(group.id);
return (
<TokenTileGroup
key={group.id}
@ -69,12 +78,14 @@ function TokenTiles({
tokens={group.items.map((item) =>
tokens.find((token) => token.id === item.id)
)}
isSelected={isSelected}
onSelect={onTileSelect}
/>
);
}
}
const multipleSelected = selectedTokens.length > 1;
const multipleSelected = selectedGroupIds.length > 1;
let hideTitle = "";
if (multipleSelected) {
@ -99,7 +110,7 @@ function TokenTiles({
>
<Box sx={{ position: "relative" }}>
<FilterBar
onFocus={() => onTokenSelect()}
onFocus={() => onTileSelect()}
search={search}
onSearchChange={onSearchChange}
selectMode={selectMode}
@ -115,7 +126,7 @@ function TokenTiles({
<Grid
p={2}
pb={4}
pt={databaseStatus === "disabled" ? 4 : 2}
pt={databaseDisabled ? 4 : 2}
bg="muted"
sx={{
borderRadius: "4px",
@ -123,7 +134,7 @@ function TokenTiles({
}}
gap={2}
columns={layout.gridTemplate}
onClick={() => onTokenSelect()}
onClick={() => onTileSelect()}
>
{groups.map((group) => (
<Sortable id={group.id} key={group.id}>
@ -132,7 +143,7 @@ function TokenTiles({
))}
</Grid>
</SimpleBar>
{databaseStatus === "disabled" && (
{databaseDisabled && (
<Box
sx={{
position: "absolute",
@ -151,7 +162,7 @@ function TokenTiles({
</Text>
</Box>
)}
{selectedTokens.length > 0 && (
{selectedGroupIds.length > 0 && (
<Flex
sx={{
position: "absolute",
@ -165,7 +176,7 @@ function TokenTiles({
<Close
title="Clear Selection"
aria-label="Clear Selection"
onClick={() => onTokenSelect()}
onClick={() => onTileSelect()}
/>
<Flex>
<IconButton

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
import Fuse from "fuse.js";
import { groupBy } from "./shared";
import { groupBy, keyBy } from "./shared";
/**
* Helpers for the SelectMapModal and SelectTokenModal
@ -134,3 +134,34 @@ export function handleItemSelect(
setSelectedIds([]);
}
}
export function groupsFromIds(groupIds, groups) {
const groupsByIds = keyBy(groups, "id");
const filteredGroups = [];
for (let groupId of groupIds) {
filteredGroups.push(groupsByIds[groupId]);
}
return filteredGroups;
}
export function itemsFromGroups(
groups,
allItems,
includeGroupedItems = true,
itemKey = "id"
) {
const allItemsById = keyBy(allItems, itemKey);
const groupedItems = [];
for (let group of groups) {
if (group.type === "item") {
groupedItems.push(allItemsById[group.id]);
} else if (group.type === "group" && includeGroupedItems) {
for (let item of group.items) {
groupedItems.push(allItemsById[item.id]);
}
}
}
return groupedItems;
}

View File

@ -5,16 +5,19 @@ import Modal from "../components/Modal";
import MapSettings from "../components/map/MapSettings";
import MapEditor from "../components/map/MapEditor";
import { useMapData } from "../contexts/MapDataContext";
import { isEmpty } from "../helpers/shared";
import { getGridDefaultInset } from "../helpers/grid";
import useResponsiveLayout from "../hooks/useResponsiveLayout";
function EditMapModal({ isOpen, onDone, map, mapState }) {
const { updateMap, updateMapState } = useMapData();
function EditMapModal({
isOpen,
onDone,
map,
mapState,
onUpdateMap,
onUpdateMapState,
}) {
function handleClose() {
setMapSettingChanges({});
setMapStateSettingChanges({});
@ -79,8 +82,8 @@ function EditMapModal({ isOpen, onDone, map, mapState }) {
}
}
}
await updateMap(map.id, mapSettingChanges);
await updateMapState(map.id, mapStateSettingChanges);
await onUpdateMap(map.id, mapSettingChanges);
await onUpdateMapState(map.id, mapStateSettingChanges);
setMapSettingChanges({});
setMapStateSettingChanges({});

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import { Button, Flex, Label } from "theme-ui";
import Modal from "../components/Modal";
@ -6,31 +6,11 @@ import TokenSettings from "../components/token/TokenSettings";
import TokenPreview from "../components/token/TokenPreview";
import LoadingOverlay from "../components/LoadingOverlay";
import { useTokenData } from "../contexts/TokenDataContext";
import { isEmpty } from "../helpers/shared";
import useResponsiveLayout from "../hooks/useResponsiveLayout";
function EditTokenModal({ isOpen, onDone, tokenId }) {
const { updateToken, getToken } = useTokenData();
const [isLoading, setIsLoading] = useState(true);
const [token, setToken] = useState();
useEffect(() => {
async function loadToken() {
setIsLoading(true);
setToken(await getToken(tokenId));
setIsLoading(false);
}
if (isOpen && tokenId) {
loadToken();
} else {
setToken();
}
}, [isOpen, tokenId, getToken]);
function EditTokenModal({ isOpen, onDone, token, onUpdateToken }) {
function handleClose() {
setTokenSettingChanges({});
onDone();
@ -55,7 +35,7 @@ function EditTokenModal({ isOpen, onDone, tokenId }) {
verifiedChanges.defaultSize = verifiedChanges.defaultSize || 1;
}
await updateToken(token.id, verifiedChanges);
await onUpdateToken(token.id, verifiedChanges);
setTokenSettingChanges({});
}
}
@ -84,7 +64,7 @@ function EditTokenModal({ isOpen, onDone, tokenId }) {
<Label pt={2} pb={1}>
Edit token
</Label>
{isLoading || !token ? (
{!token ? (
<Flex
sx={{
width: "100%",

View File

@ -10,7 +10,11 @@ import MapTiles from "../components/map/MapTiles";
import ImageDrop from "../components/ImageDrop";
import LoadingOverlay from "../components/LoadingOverlay";
import { handleItemSelect } from "../helpers/select";
import {
groupsFromIds,
handleItemSelect,
itemsFromGroups,
} from "../helpers/select";
import { createMapFromFile } from "../helpers/map";
import useResponsiveLayout from "../hooks/useResponsiveLayout";
@ -19,6 +23,7 @@ import { useMapData } from "../contexts/MapDataContext";
import { useAuth } from "../contexts/AuthContext";
import { useKeyboard, useBlur } from "../contexts/KeyboardContext";
import { useAssets } from "../contexts/AssetsContext";
import { useDatabase } from "../contexts/DatabaseContext";
import shortcuts from "../shortcuts";
@ -42,8 +47,12 @@ function SelectMapModal({
resetMap,
mapsLoading,
getMapState,
getMap,
updateMapGroups,
updateMap,
updateMapState,
} = useMapData();
const { databaseStatus } = useDatabase();
const { addAssets } = useAssets();
/**
@ -126,7 +135,6 @@ function SelectMapModal({
const { map, assets } = await createMapFromFile(file, userId);
await addMap(map);
await addAssets(assets);
setSelectedMapIds([map.id]);
setIsLoading(false);
}
@ -140,20 +148,21 @@ function SelectMapModal({
* Map Controls
*/
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
// The map selected in the modal
const [selectedMapIds, setSelectedMapIds] = useState([]);
const [selectedGroupIds, setSelectedGroupIds] = useState([]);
const selectedMaps = maps.filter((map) => selectedMapIds.includes(map.id));
const selectedMapStates = mapStates.filter((state) =>
selectedMapIds.includes(state.mapId)
);
function getSelectedMaps() {
const groups = groupsFromIds(selectedGroupIds, mapGroups);
return itemsFromGroups(groups, maps);
}
const [isMapsRemoveModalOpen, setIsMapsRemoveModalOpen] = useState(false);
async function handleMapsRemove() {
setIsLoading(true);
setIsMapsRemoveModalOpen(false);
const selectedMaps = getSelectedMaps();
const selectedMapIds = selectedMaps.map((map) => map.id);
await removeMaps(selectedMapIds);
setSelectedMapIds([]);
setSelectedGroupIds([]);
// Removed the map from the map screen if needed
if (currentMap && selectedMapIds.includes(currentMap.id)) {
onMapChange(null, null);
@ -165,6 +174,8 @@ function SelectMapModal({
async function handleMapsReset() {
setIsLoading(true);
setIsMapsResetModalOpen(false);
const selectedMaps = getSelectedMaps();
const selectedMapIds = selectedMaps.map((map) => map.id);
for (let id of selectedMapIds) {
const newState = await resetMap(id);
// Reset the state of the current map if needed
@ -178,12 +189,12 @@ function SelectMapModal({
// Either single, multiple or range
const [selectMode, setSelectMode] = useState("single");
function handleMapSelect(map) {
function handleTileSelect(item) {
handleItemSelect(
map,
item,
selectMode,
selectedMapIds,
setSelectedMapIds
selectedGroupIds,
setSelectedGroupIds
// TODO: Add new group support
);
}
@ -200,10 +211,11 @@ function SelectMapModal({
if (isLoading) {
return;
}
if (selectedMapIds.length === 1) {
const groups = groupsFromIds(selectedGroupIds, mapGroups);
if (groups.length === 1 && groups[0].type === "item") {
setIsLoading(true);
const map = selectedMaps[0];
const mapState = await getMapState(map.id);
const map = await getMap(groups[0].id);
const mapState = await getMapState(groups[0].id);
onMapChange(map, mapState);
setIsLoading(false);
} else {
@ -226,9 +238,10 @@ function SelectMapModal({
setSelectMode("multiple");
}
if (shortcuts.delete(event)) {
const selectedMaps = getSelectedMaps();
// Selected maps and none are default
if (
selectedMapIds.length > 0 &&
selectedMaps.length > 0 &&
!selectedMaps.some((map) => map.type === "default")
) {
// Ensure all other modals are closed
@ -287,24 +300,25 @@ function SelectMapModal({
</Label>
<MapTiles
maps={maps}
mapStates={mapStates}
groups={mapGroups}
selectedGroupIds={selectedGroupIds}
onMapAdd={openImageDialog}
onMapEdit={() => setIsEditModalOpen(true)}
onMapsReset={() => setIsMapsResetModalOpen(true)}
onMapsRemove={() => setIsMapsRemoveModalOpen(true)}
selectedMaps={selectedMaps}
selectedMapStates={selectedMapStates}
onMapSelect={handleMapSelect}
onTileSelect={handleTileSelect}
onDone={handleDone}
selectMode={selectMode}
onSelectModeChange={setSelectMode}
search={search}
onSearchChange={handleSearchChange}
onMapsGroup={updateMapGroups}
databaseDisabled={databaseStatus === "disabled"}
/>
<Button
variant="primary"
disabled={isLoading || selectedMapIds.length > 1}
disabled={isLoading || selectedGroupIds.length > 1}
onClick={handleDone}
mt={2}
>
@ -316,16 +330,24 @@ function SelectMapModal({
<EditMapModal
isOpen={isEditModalOpen}
onDone={() => setIsEditModalOpen(false)}
map={selectedMaps.length === 1 && selectedMaps[0]}
mapState={selectedMapStates.length === 1 && selectedMapStates[0]}
map={
selectedGroupIds.length === 1 &&
maps.find((map) => map.id === selectedGroupIds[0])
}
mapState={
selectedGroupIds.length === 1 &&
mapStates.find((state) => state.mapId === selectedGroupIds[0])
}
onUpdateMap={updateMap}
onUpdateMapState={updateMapState}
/>
<ConfirmModal
isOpen={isMapsResetModalOpen}
onRequestClose={() => setIsMapsResetModalOpen(false)}
onConfirm={handleMapsReset}
confirmText="Reset"
label={`Reset ${selectedMapIds.length} Map${
selectedMapIds.length > 1 ? "s" : ""
label={`Reset ${selectedGroupIds.length} Map${
selectedGroupIds.length > 1 ? "s" : ""
}`}
description="This will remove all fog, drawings and tokens from the selected maps."
/>
@ -334,9 +356,7 @@ function SelectMapModal({
onRequestClose={() => setIsMapsRemoveModalOpen(false)}
onConfirm={handleMapsRemove}
confirmText="Remove"
label={`Remove ${selectedMapIds.length} Map${
selectedMapIds.length > 1 ? "s" : ""
}`}
label="Remove Map(s)"
description="This operation cannot be undone."
/>
<ConfirmModal

View File

@ -11,7 +11,11 @@ import ImageDrop from "../components/ImageDrop";
import TokenTiles from "../components/token/TokenTiles";
import LoadingOverlay from "../components/LoadingOverlay";
import { handleItemSelect } from "../helpers/select";
import {
groupsFromIds,
handleItemSelect,
itemsFromGroups,
} from "../helpers/select";
import { createTokenFromFile } from "../helpers/token";
import useResponsiveLayout from "../hooks/useResponsiveLayout";
@ -20,6 +24,7 @@ import { useTokenData } from "../contexts/TokenDataContext";
import { useAuth } from "../contexts/AuthContext";
import { useKeyboard, useBlur } from "../contexts/KeyboardContext";
import { useAssets } from "../contexts/AssetsContext";
import { useDatabase } from "../contexts/DatabaseContext";
import shortcuts from "../shortcuts";
@ -35,7 +40,9 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
tokensLoading,
tokenGroups,
updateTokenGroups,
updateToken,
} = useTokenData();
const { databaseStatus } = useDatabase();
const { addAssets } = useAssets();
/**
@ -123,7 +130,6 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
const { token, assets } = await createTokenFromFile(file, userId);
await addToken(token);
await addAssets(assets);
setSelectedTokenIds([token.id]);
setIsLoading(false);
}
@ -131,22 +137,28 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
* Token controls
*/
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [selectedTokenIds, setSelectedTokenIds] = useState([]);
const selectedTokens = tokens.filter((token) =>
selectedTokenIds.includes(token.id)
);
const [selectedGroupIds, setSelectedGroupIds] = useState([]);
function getSelectedTokens() {
const groups = groupsFromIds(selectedGroupIds, tokenGroups);
return itemsFromGroups(groups, tokens);
}
const [isTokensRemoveModalOpen, setIsTokensRemoveModalOpen] = useState(false);
async function handleTokensRemove() {
setIsLoading(true);
setIsTokensRemoveModalOpen(false);
const selectedTokens = getSelectedTokens();
const selectedTokenIds = selectedTokens.map((token) => token.id);
await removeTokens(selectedTokenIds);
setSelectedTokenIds([]);
setSelectedGroupIds([]);
setIsLoading(false);
}
async function handleTokensHide(hideInSidebar) {
setIsLoading(true);
const selectedTokens = getSelectedTokens();
const selectedTokenIds = selectedTokens.map((token) => token.id);
await updateTokens(selectedTokenIds, { hideInSidebar });
setIsLoading(false);
}
@ -154,12 +166,12 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
// Either single, multiple or range
const [selectMode, setSelectMode] = useState("single");
async function handleTokenSelect(token) {
async function handleTileSelect(item) {
handleItemSelect(
token,
item,
selectMode,
selectedTokenIds,
setSelectedTokenIds
selectedGroupIds,
setSelectedGroupIds
// TODO: Rework group support
);
}
@ -178,9 +190,10 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
setSelectMode("multiple");
}
if (shortcuts.delete(event)) {
const selectedTokens = getSelectedTokens();
// Selected tokens and none are default
if (
selectedTokenIds.length > 0 &&
selectedTokens.length > 0 &&
!selectedTokens.some((token) => token.type === "default")
) {
// Ensure all other modals are closed
@ -239,17 +252,18 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
<TokenTiles
tokens={tokens}
groups={tokenGroups}
selectedGroupIds={selectedGroupIds}
onTokenAdd={openImageDialog}
onTokenEdit={() => setIsEditModalOpen(true)}
onTokensRemove={() => setIsTokensRemoveModalOpen(true)}
selectedTokens={selectedTokens}
onTokenSelect={handleTokenSelect}
onTileSelect={handleTileSelect}
selectMode={selectMode}
onSelectModeChange={setSelectMode}
search={search}
onSearchChange={handleSearchChange}
onTokensGroup={updateTokenGroups}
onTokensHide={handleTokensHide}
databaseDisabled={databaseStatus === "disabled"}
/>
<Button
variant="primary"
@ -265,16 +279,18 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
<EditTokenModal
isOpen={isEditModalOpen}
onDone={() => setIsEditModalOpen(false)}
tokenId={selectedTokens.length === 1 && selectedTokens[0].id}
token={
selectedGroupIds.length === 1 &&
tokens.find((token) => token.id === selectedGroupIds[0])
}
onTokenUpdate={updateToken}
/>
<ConfirmModal
isOpen={isTokensRemoveModalOpen}
onRequestClose={() => setIsTokensRemoveModalOpen(false)}
onConfirm={handleTokensRemove}
confirmText="Remove"
label={`Remove ${selectedTokenIds.length} Token${
selectedTokenIds.length > 1 ? "s" : ""
}`}
label="Remove Token(s)"
description="This operation cannot be undone."
/>
<ConfirmModal