From ef73ca017945046924c742fa1bd8ca29eb35078a Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Thu, 20 May 2021 12:22:07 +1000 Subject: [PATCH] Make tile groups selectable --- src/components/map/MapTile.js | 8 +-- src/components/map/MapTileGroup.js | 13 +--- src/components/map/MapTiles.js | 87 ++++++++++++++++---------- src/components/token/TokenTileGroup.js | 6 +- src/components/token/TokenTiles.js | 51 +++++++++------ src/helpers/select.js | 33 +++++++++- src/modals/EditMapModal.js | 17 ++--- src/modals/EditTokenModal.js | 28 ++------- src/modals/SelectMapModal.js | 76 +++++++++++++--------- src/modals/SelectTokensModal.js | 52 +++++++++------ 10 files changed, 223 insertions(+), 148 deletions(-) diff --git a/src/components/map/MapTile.js b/src/components/map/MapTile.js index 7b01c35..520ce2a 100644 --- a/src/components/map/MapTile.js +++ b/src/components/map/MapTile.js @@ -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({ onMapSelect(map)} - onEdit={() => onMapEdit(map.id)} + onSelect={() => onSelect({ id: map.id })} + onEdit={() => onEdit(map.id)} onDoubleClick={() => canEdit && onDone()} canEdit={canEdit} badges={badges} diff --git a/src/components/map/MapTileGroup.js b/src/components/map/MapTileGroup.js index 730484a..0d1a634 100644 --- a/src/components/map/MapTileGroup.js +++ b/src/components/map/MapTileGroup.js @@ -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 ( onGroupSelect(group)} - // onDoubleClick={() => canOpen && onOpen()} + onSelect={() => onSelect(group)} + onDoubleClick={() => canOpen && onOpen()} columns="1fr 1fr" > {maps.slice(0, 4).map((map) => ( diff --git a/src/components/map/MapTiles.js b/src/components/map/MapTiles.js index 4967f2e..b361782 100644 --- a/src/components/map/MapTiles.js +++ b/src/components/map/MapTiles.js @@ -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 ( ); } else { + const isSelected = selectedGroupIds.includes(group.id); return ( maps.find((map) => map.id === item.id) )} + isSelected={isSelected} + onSelect={onTileSelect} /> ); } } - const multipleSelected = selectedMaps.length > 1; + const multipleSelected = selectedGroupIds.length > 1; return ( onMapSelect()} + onFocus={() => onTileSelect()} search={search} onSearchChange={onSearchChange} selectMode={selectMode} @@ -111,7 +132,7 @@ function MapTiles({ onMapSelect()} + onClick={() => onTileSelect()} > {groups.map((group) => ( @@ -129,7 +150,7 @@ function MapTiles({ ))} - {databaseStatus === "disabled" && ( + {databaseDisabled && ( )} - {selectedMaps.length > 0 && ( + {selectedGroupIds.length > 0 && ( onMapSelect()} + onClick={() => onTileSelect()} /> onGroupSelect(group)} - // onDoubleClick={() => canOpen && onOpen()} + onSelect={() => onSelect(group)} + onDoubleClick={() => canOpen && onOpen()} columns="1fr 1fr" > {tokens.slice(0, 4).map((token) => ( diff --git a/src/components/token/TokenTiles.js b/src/components/token/TokenTiles.js index 47332a6..29a70e4 100644 --- a/src/components/token/TokenTiles.js +++ b/src/components/token/TokenTiles.js @@ -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 ( ); } else { + const isSelected = selectedGroupIds.includes(group.id); return ( 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({ > onTokenSelect()} + onFocus={() => onTileSelect()} search={search} onSearchChange={onSearchChange} selectMode={selectMode} @@ -115,7 +126,7 @@ function TokenTiles({ onTokenSelect()} + onClick={() => onTileSelect()} > {groups.map((group) => ( @@ -132,7 +143,7 @@ function TokenTiles({ ))} - {databaseStatus === "disabled" && ( + {databaseDisabled && ( )} - {selectedTokens.length > 0 && ( + {selectedGroupIds.length > 0 && ( onTokenSelect()} + onClick={() => onTileSelect()} /> { - 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 }) { - {isLoading || !token ? ( + {!token ? ( 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({ 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"} />