diff --git a/src/components/map/MapEditBar.js b/src/components/map/MapEditBar.js
new file mode 100644
index 0000000..2c57f7a
--- /dev/null
+++ b/src/components/map/MapEditBar.js
@@ -0,0 +1,178 @@
+import React, { useState, useEffect } from "react";
+import { Flex, Close, IconButton } from "theme-ui";
+
+import { groupsFromIds, itemsFromGroups } from "../../helpers/group";
+
+import ConfirmModal from "../../modals/ConfirmModal";
+
+import ResetMapIcon from "../../icons/ResetMapIcon";
+import RemoveMapIcon from "../../icons/RemoveMapIcon";
+
+import { useGroup } from "../../contexts/GroupContext";
+import { useMapData } from "../../contexts/MapDataContext";
+import { useKeyboard } from "../../contexts/KeyboardContext";
+
+import shortcuts from "../../shortcuts";
+
+function MapEditBar({ currentMap, disabled, onMapChange, onMapReset, onLoad }) {
+ const [hasMapState, setHasMapState] = useState(false);
+ const [hasSelectedDefaultMap, setHasSelectedDefaultMap] = useState(false);
+
+ const { maps, mapStates, removeMaps, resetMap } = useMapData();
+
+ const {
+ groups: allGroups,
+ selectedGroupIds,
+ onGroupSelect,
+ openGroupId,
+ openGroupItems,
+ } = useGroup();
+
+ const groups = openGroupId ? openGroupItems : allGroups;
+
+ useEffect(() => {
+ const selectedGroups = groupsFromIds(selectedGroupIds, groups);
+ const selectedMaps = itemsFromGroups(selectedGroups, maps);
+ const selectedMapStates = itemsFromGroups(
+ selectedGroups,
+ mapStates,
+ "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 getSelectedMaps() {
+ const selectedGroups = groupsFromIds(selectedGroupIds, groups);
+ return itemsFromGroups(selectedGroups, maps);
+ }
+
+ const [isMapsRemoveModalOpen, setIsMapsRemoveModalOpen] = useState(false);
+ async function handleMapsRemove() {
+ onLoad(true);
+ setIsMapsRemoveModalOpen(false);
+ const selectedMaps = getSelectedMaps();
+ const selectedMapIds = selectedMaps.map((map) => map.id);
+ await removeMaps(selectedMapIds);
+ onGroupSelect();
+ // Removed the map from the map screen if needed
+ if (currentMap && selectedMapIds.includes(currentMap.id)) {
+ onMapChange(null, null);
+ }
+ onLoad(false);
+ }
+
+ const [isMapsResetModalOpen, setIsMapsResetModalOpen] = useState(false);
+ async function handleMapsReset() {
+ onLoad(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
+ if (currentMap && currentMap.id === id) {
+ onMapReset(newState);
+ }
+ }
+ onLoad(false);
+ }
+
+ /**
+ * Shortcuts
+ */
+ function handleKeyDown(event) {
+ if (disabled) {
+ return;
+ }
+ if (shortcuts.delete(event)) {
+ const selectedMaps = getSelectedMaps();
+ // Selected maps and none are default
+ if (
+ selectedMaps.length > 0 &&
+ !selectedMaps.some((map) => map.type === "default")
+ ) {
+ setIsMapsResetModalOpen(false);
+ setIsMapsRemoveModalOpen(true);
+ }
+ }
+ }
+
+ useKeyboard(handleKeyDown);
+
+ if (selectedGroupIds.length === 0) {
+ return null;
+ }
+
+ return (
+
+ onGroupSelect()}
+ />
+
+ setIsMapsResetModalOpen(true)}
+ disabled={!hasMapState}
+ >
+
+
+ setIsMapsRemoveModalOpen(true)}
+ disabled={hasSelectedDefaultMap}
+ >
+
+
+
+ setIsMapsResetModalOpen(false)}
+ onConfirm={handleMapsReset}
+ confirmText="Reset"
+ label="Reset Selected Map(s)"
+ description="This will remove all fog, drawings and tokens from the selected maps."
+ />
+ setIsMapsRemoveModalOpen(false)}
+ onConfirm={handleMapsRemove}
+ confirmText="Remove"
+ label="Remove Selected Map(s)"
+ description="This operation cannot be undone."
+ />
+
+ );
+}
+
+export default MapEditBar;
diff --git a/src/components/map/SelectMapSelectButton.js b/src/components/map/SelectMapSelectButton.js
new file mode 100644
index 0000000..80823e5
--- /dev/null
+++ b/src/components/map/SelectMapSelectButton.js
@@ -0,0 +1,39 @@
+import React from "react";
+import { Button } from "theme-ui";
+
+import { useGroup } from "../../contexts/GroupContext";
+
+import { findGroup } from "../../helpers/group";
+
+function SelectMapSelectButton({ onMapSelect, disabled }) {
+ const {
+ groups: allGroups,
+ selectedGroupIds,
+ openGroupId,
+ openGroupItems,
+ } = useGroup();
+
+ const groups = openGroupId ? openGroupItems : allGroups;
+
+ function handleSelectClick() {
+ if (selectedGroupIds.length === 1) {
+ const group = findGroup(groups, selectedGroupIds[0]);
+ if (group && group.type === "item") {
+ onMapSelect(group.id);
+ }
+ }
+ }
+
+ return (
+
+ );
+}
+
+export default SelectMapSelectButton;
diff --git a/src/contexts/TileDragContext.js b/src/contexts/TileDragContext.js
index a5143c6..c8c687d 100644
--- a/src/contexts/TileDragContext.js
+++ b/src/contexts/TileDragContext.js
@@ -85,6 +85,7 @@ export function TileDragProvider({ onDragAdd, children }) {
selectedIndices = selectedIndices.sort((a, b) => a - b);
if (over.id.startsWith(GROUP_ID_PREFIX)) {
+ onGroupSelect();
// Handle tile group
const overId = over.id.slice(9);
if (overId === active.id) {
@@ -96,8 +97,8 @@ export function TileDragProvider({ onDragAdd, children }) {
moveGroupsInto(groups, overGroupIndex, selectedIndices),
openGroupId
);
- onGroupSelect();
} else if (over.id.startsWith(UNGROUP_ID_PREFIX)) {
+ onGroupSelect();
// Handle tile ungroup
const newGroups = ungroup(allGroups, openGroupId, selectedIndices);
// Close group if it was removed
@@ -105,7 +106,6 @@ export function TileDragProvider({ onDragAdd, children }) {
onGroupClose();
}
onGroupsChange(newGroups);
- onGroupSelect();
} else if (over.id.startsWith(ADD_TO_MAP_ID_PREFIX)) {
onDragAdd && onDragAdd(selectedGroupIds, over.rect);
} else {
diff --git a/src/modals/EditMapModal.js b/src/modals/EditMapModal.js
index e68a5dd..632d9e4 100644
--- a/src/modals/EditMapModal.js
+++ b/src/modals/EditMapModal.js
@@ -103,6 +103,10 @@ function EditMapModal({
const layout = useResponsiveLayout();
+ if (!map) {
+ return null;
+ }
+
return (
map.id);
- await removeMaps(selectedMapIds);
- setSelectedGroupIds([]);
- // Removed the map from the map screen if needed
- if (currentMap && selectedMapIds.includes(currentMap.id)) {
- onMapChange(null, null);
- }
- setIsLoading(false);
- }
-
- const [isMapsResetModalOpen, setIsMapsResetModalOpen] = useState(false);
- 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
- if (currentMap && currentMap.id === id) {
- onMapReset(newState);
- }
- }
- setIsLoading(false);
- }
-
/**
* Modal Controls
*/
@@ -179,69 +134,44 @@ function SelectMapModal({
onDone();
}
+ /**
+ * Map Controls
+ */
async function handleMapSelect(mapId) {
if (isLoading) {
return;
}
- setIsLoading(true);
- const map = await getMap(mapId);
- const mapState = await getMapState(mapId);
- onMapChange(map, mapState);
- setIsLoading(false);
+ if (mapId) {
+ setIsLoading(true);
+ const map = await getMap(mapId);
+ const mapState = await getMapState(mapId);
+ onMapChange(map, mapState);
+ setIsLoading(false);
+ } else {
+ onMapChange(null, null);
+ }
onDone();
}
+ const [editingMapId, setEditingMapId] = useState();
+
const [canAddDraggedMap, setCanAddDraggedMap] = useState(false);
function handleGroupsSelect(groupIds) {
- setSelectedGroupIds(groupIds);
- if (groupIds.length === 1) {
+ if (!canAddDraggedMap && groupIds.length === 1) {
// Only allow adding a map from dragging if there is a single group item selected
const group = findGroup(mapGroups, groupIds[0]);
setCanAddDraggedMap(group && group.type === "item");
- } else {
+ } else if (canAddDraggedMap) {
setCanAddDraggedMap(false);
}
}
- function handleSelectClick() {
- if (isLoading) {
- return;
- }
- if (selectedGroupIds.length === 1) {
- const group = findGroup(mapGroups, selectedGroupIds[0]);
- if (group && group.type === "item") {
- handleMapSelect(group.id);
- }
- } else {
- onMapChange(null, null);
- onDone();
+ function handleDragAdd(groupIds) {
+ if (groupIds.length === 1) {
+ handleMapSelect(groupIds[0]);
}
}
- /**
- * Shortcuts
- */
- function handleKeyDown(event) {
- if (!isOpen) {
- return;
- }
- if (shortcuts.delete(event)) {
- const selectedMaps = getSelectedMaps();
- // Selected maps and none are default
- if (
- selectedMaps.length > 0 &&
- !selectedMaps.some((map) => map.type === "default")
- ) {
- // Ensure all other modals are closed
- setIsEditModalOpen(false);
- setIsMapsResetModalOpen(false);
- setIsMapsRemoveModalOpen(true);
- }
- }
- }
-
- useKeyboard(handleKeyDown);
-
const layout = useResponsiveLayout();
const [modalSize, setModalSize] = useState({ width: 0, height: 0 });
@@ -269,92 +199,70 @@ function SelectMapModal({
handleHeight
onResize={handleModalResize}
>
-
-
-
-
-
+
+
+
+
setIsEditModalOpen(true)}
+ onMapEdit={setEditingMapId}
onMapSelect={handleMapSelect}
/>
-
+
setIsEditModalOpen(true)}
+ onMapEdit={setEditingMapId}
onMapSelect={handleMapSelect}
subgroup
/>
-
-
-
-
+
+
+
+
+
{(isLoading || mapsLoading) && }
setIsEditModalOpen(false)}
- map={
- selectedGroupIds.length === 1 &&
- maps.find((map) => map.id === selectedGroupIds[0])
- }
+ isOpen={!!editingMapId}
+ onDone={() => setEditingMapId()}
+ map={editingMapId && maps.find((map) => map.id === editingMapId)}
mapState={
- selectedGroupIds.length === 1 &&
- mapStates.find((state) => state.mapId === selectedGroupIds[0])
+ editingMapId &&
+ mapStates.find((state) => state.mapId === editingMapId)
}
onUpdateMap={updateMap}
onUpdateMapState={updateMapState}
/>
- setIsMapsResetModalOpen(false)}
- onConfirm={handleMapsReset}
- confirmText="Reset"
- label={`Reset ${selectedGroupIds.length} Map${
- selectedGroupIds.length > 1 ? "s" : ""
- }`}
- description="This will remove all fog, drawings and tokens from the selected maps."
- />
- setIsMapsRemoveModalOpen(false)}
- onConfirm={handleMapsRemove}
- confirmText="Remove"
- label="Remove Map(s)"
- description="This operation cannot be undone."
- />