Simplified menu interaction prevention

This commit is contained in:
Mitchell McCaffrey 2020-05-22 13:46:52 +10:00
parent 6b675d8200
commit 9e01ad1d0e
3 changed files with 33 additions and 27 deletions

View File

@ -163,12 +163,12 @@ function Map({
/> />
)); ));
const tokenMenu = isTokenMenuOpen && ( const tokenMenu = (
<TokenMenu <TokenMenu
isOpen={isTokenMenuOpen} isOpen={isTokenMenuOpen}
onRequestClose={() => setIsTokenMenuOpen(false)} onRequestClose={() => setIsTokenMenuOpen(false)}
onTokenChange={onMapTokenStateChange} onTokenChange={onMapTokenStateChange}
tokenState={mapState.tokens[tokenMenuOptions.tokenStateId]} tokenState={mapState && mapState.tokens[tokenMenuOptions.tokenStateId]}
tokenImage={tokenMenuOptions.tokenImage} tokenImage={tokenMenuOptions.tokenImage}
/> />
); );

View File

@ -4,6 +4,8 @@ import { useThemeUI } from "theme-ui";
import MapInteractionContext from "../../contexts/MapInteractionContext"; import MapInteractionContext from "../../contexts/MapInteractionContext";
import usePrevious from "../../helpers/usePrevious";
function MapMenu({ function MapMenu({
isOpen, isOpen,
onRequestClose, onRequestClose,
@ -22,7 +24,16 @@ function MapMenu({
// callback // callback
const [modalContentNode, setModalContentNode] = useState(null); const [modalContentNode, setModalContentNode] = useState(null);
// Toggle map interaction when menu is opened
const wasOpen = usePrevious(isOpen);
const { setPreventMapInteraction } = useContext(MapInteractionContext); const { setPreventMapInteraction } = useContext(MapInteractionContext);
useEffect(() => {
if (isOpen && !wasOpen) {
setPreventMapInteraction(true);
} else if (wasOpen && !isOpen) {
setPreventMapInteraction(false);
}
}, [isOpen, setPreventMapInteraction, wasOpen]);
useEffect(() => { useEffect(() => {
// Close modal if interacting with any other element // Close modal if interacting with any other element
@ -32,31 +43,29 @@ function MapMenu({
!path.includes(modalContentNode) && !path.includes(modalContentNode) &&
!(excludeNode && path.includes(excludeNode)) !(excludeNode && path.includes(excludeNode))
) { ) {
setPreventMapInteraction(false);
onRequestClose(); onRequestClose();
document.body.removeEventListener("pointerdown", handlePointerDown); document.body.removeEventListener("pointerdown", handlePointerDown);
} }
} }
if (modalContentNode) { if (modalContentNode) {
setPreventMapInteraction(true);
document.body.addEventListener("pointerdown", handlePointerDown); document.body.addEventListener("pointerdown", handlePointerDown);
// Check for wheel event to close modal as well // Check for wheel event to close modal as well
document.body.addEventListener( document.body.addEventListener(
"wheel", "wheel",
() => { () => {
setPreventMapInteraction(false);
onRequestClose(); onRequestClose();
}, },
{ once: true } { once: true }
); );
} }
return () => { return () => {
if (modalContentNode) { if (modalContentNode) {
document.body.removeEventListener("pointerdown", handlePointerDown); document.body.removeEventListener("pointerdown", handlePointerDown);
} }
}; };
}, [modalContentNode, excludeNode, onRequestClose, setPreventMapInteraction]); }, [modalContentNode, excludeNode, onRequestClose]);
function handleModalContent(node) { function handleModalContent(node) {
setModalContentNode(node); setModalContentNode(node);

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState, useContext } from "react"; import React, { useEffect, useState } from "react";
import { Box, Input, Slider, Flex, Text } from "theme-ui"; import { Box, Input, Slider, Flex, Text } from "theme-ui";
import MapMenu from "../map/MapMenu"; import MapMenu from "../map/MapMenu";
@ -7,8 +7,6 @@ import colors, { colorOptions } from "../../helpers/colors";
import usePrevious from "../../helpers/usePrevious"; import usePrevious from "../../helpers/usePrevious";
import MapInteractionContext from "../../contexts/MapInteractionContext";
const defaultTokenMaxSize = 6; const defaultTokenMaxSize = 6;
/** /**
@ -34,7 +32,7 @@ function TokenMenu({
const [tokenMaxSize, setTokenMaxSize] = useState(defaultTokenMaxSize); const [tokenMaxSize, setTokenMaxSize] = useState(defaultTokenMaxSize);
useEffect(() => { useEffect(() => {
if (isOpen && !wasOpen) { if (isOpen && !wasOpen && tokenState) {
setTokenMaxSize(Math.max(tokenState.size, defaultTokenMaxSize)); setTokenMaxSize(Math.max(tokenState.size, defaultTokenMaxSize));
} }
}, [isOpen, tokenState, wasOpen]); }, [isOpen, tokenState, wasOpen]);
@ -99,8 +97,6 @@ function TokenMenu({
} }
} }
const { setPreventMapInteraction } = useContext(MapInteractionContext);
return ( return (
<MapMenu <MapMenu
isOpen={isOpen} isOpen={isOpen}
@ -114,7 +110,6 @@ function TokenMenu({
as="form" as="form"
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
setPreventMapInteraction(false);
onRequestClose(); onRequestClose();
}} }}
sx={{ alignItems: "center" }} sx={{ alignItems: "center" }}
@ -130,7 +125,7 @@ function TokenMenu({
<Input <Input
id="changeTokenLabel" id="changeTokenLabel"
onChange={handleLabelChange} onChange={handleLabelChange}
value={tokenState.label} value={(tokenState && tokenState.label) || ""}
sx={{ sx={{
padding: "4px", padding: "4px",
border: "none", border: "none",
@ -162,18 +157,20 @@ function TokenMenu({
onClick={() => handleStatusChange(color)} onClick={() => handleStatusChange(color)}
aria-label={`Token label Color ${color}`} aria-label={`Token label Color ${color}`}
> >
{tokenState.statuses && tokenState.statuses.includes(color) && ( {tokenState &&
<Box tokenState.statuses &&
sx={{ tokenState.statuses.includes(color) && (
width: "100%", <Box
height: "100%", sx={{
border: "2px solid white", width: "100%",
position: "absolute", height: "100%",
top: 0, border: "2px solid white",
borderRadius: "50%", position: "absolute",
}} top: 0,
/> borderRadius: "50%",
)} }}
/>
)}
</Box> </Box>
))} ))}
</Box> </Box>
@ -187,7 +184,7 @@ function TokenMenu({
Size: Size:
</Text> </Text>
<Slider <Slider
value={tokenState.size || 1} value={(tokenState && tokenState.size) || 1}
onChange={handleSizeChange} onChange={handleSizeChange}
step={1} step={1}
min={1} min={1}