import { useState, Fragment, useEffect, useMemo } from "react"; import { IconButton, Flex, Box } from "theme-ui"; import RadioIconButton from "../RadioIconButton"; import Divider from "../Divider"; import SelectMapButton from "./SelectMapButton"; import FogToolSettings from "../controls/FogToolSettings"; import DrawingToolSettings from "../controls/DrawingToolSettings"; import PointerToolSettings from "../controls/PointerToolSettings"; import SelectToolSettings from "../controls/SelectToolSettings"; import MoveToolIcon from "../../icons/MoveToolIcon"; import FogToolIcon from "../../icons/FogToolIcon"; import BrushToolIcon from "../../icons/BrushToolIcon"; import MeasureToolIcon from "../../icons/MeasureToolIcon"; import ExpandMoreIcon from "../../icons/ExpandMoreIcon"; import PointerToolIcon from "../../icons/PointerToolIcon"; import FullScreenIcon from "../../icons/FullScreenIcon"; import FullScreenExitIcon from "../../icons/FullScreenExitIcon"; import NoteToolIcon from "../../icons/NoteToolIcon"; import SelectToolIcon from "../../icons/SelectToolIcon"; import UndoButton from "../controls/shared/UndoButton"; import RedoButton from "../controls/shared/RedoButton"; import useSetting from "../../hooks/useSetting"; import { Map, MapTool, MapToolId } from "../../types/Map"; import { MapState } from "../../types/MapState"; import { MapChangeEventHandler, MapResetEventHandler, } from "../../types/Events"; import { Settings } from "../../types/Settings"; import { useKeyboard } from "../../contexts/KeyboardContext"; import shortcuts from "../../shortcuts"; import { useUserId } from "../../contexts/UserIdContext"; import { isEmpty } from "../../helpers/shared"; import { MapActions } from "../../hooks/useMapActions"; type MapControlsProps = { onMapChange: MapChangeEventHandler; onMapReset: MapResetEventHandler; map: Map | null; mapState: MapState | null; mapActions: MapActions; allowMapChange: boolean; selectedToolId: MapToolId; onSelectedToolChange: (toolId: MapToolId) => void; toolSettings: Settings; onToolSettingChange: (change: Partial) => void; onToolAction: (actionId: string) => void; onUndo: () => void; onRedo: () => void; }; function MapContols({ onMapChange, onMapReset, map, mapState, mapActions, allowMapChange, selectedToolId, onSelectedToolChange, toolSettings, onToolSettingChange, onToolAction, onUndo, onRedo, }: MapControlsProps) { const [isExpanded, setIsExpanded] = useState(true); const [fullScreen, setFullScreen] = useSetting("map.fullScreen"); const userId = useUserId(); const disabledControls = useMemo(() => { const isOwner = map && map.owner === userId; const allowMapDrawing = isOwner || mapState?.editFlags.includes("drawing"); const allowFogDrawing = isOwner || mapState?.editFlags.includes("fog"); const allowNoteEditing = isOwner || mapState?.editFlags.includes("notes"); const disabled: MapToolId[] = []; if (!allowMapChange) { disabled.push("map"); } if (!map) { disabled.push("move"); disabled.push("measure"); disabled.push("pointer"); disabled.push("select"); } if (!map || !allowMapDrawing) { disabled.push("drawing"); } if (!map || !allowFogDrawing) { disabled.push("fog"); } if (!map || !allowNoteEditing) { disabled.push("note"); } if (!map || mapActions.actionIndex < 0) { disabled.push("undo"); } if (!map || mapActions.actionIndex === mapActions.actions.length - 1) { disabled.push("redo"); } return disabled; }, [map, mapState, mapActions, allowMapChange, userId]); // Change back to move tool if selected tool becomes disabled useEffect(() => { if (disabledControls.includes(selectedToolId)) { onSelectedToolChange("move"); } }, [selectedToolId, disabledControls, onSelectedToolChange]); const disabledSettings = useMemo(() => { const disabled: Partial> = { drawing: [], }; if (mapState && isEmpty(mapState.drawings)) { disabled.drawing?.push("erase"); } return disabled; }, [mapState]); const toolsById: Record = { move: { id: "move", icon: , title: "Move Tool (W)", }, select: { id: "select", icon: , title: "Select Tool (S)", SettingsComponent: SelectToolSettings, }, fog: { id: "fog", icon: , title: "Fog Tool (F)", SettingsComponent: FogToolSettings, }, drawing: { id: "drawing", icon: , title: "Drawing Tool (D)", SettingsComponent: DrawingToolSettings, }, measure: { id: "measure", icon: , title: "Measure Tool (M)", }, pointer: { id: "pointer", icon: , title: "Pointer Tool (Q)", SettingsComponent: PointerToolSettings, }, note: { id: "note", icon: , title: "Note Tool (N)", }, }; const tools: MapToolId[] = [ "move", "select", "fog", "drawing", "measure", "pointer", "note", ]; const sections = [ { id: "map", component: ( ), }, { id: "tools", component: tools.map((tool) => ( onSelectedToolChange(tool)} isSelected={selectedToolId === tool} disabled={disabledControls.includes(tool)} > {toolsById[tool].icon} )), }, { id: "history", component: ( <> ), }, ]; let controls = null; if (sections.length === 1 && sections[0].id === "map") { controls = ( {sections[0].component} ); } else if (sections.length > 0) { controls = ( <> setIsExpanded(!isExpanded)} sx={{ transform: `rotate(${isExpanded ? "0" : "180deg"})`, display: "block", backgroundColor: "overlay", borderRadius: "50%", }} m={2} > {sections.map((section, index) => ( {section.component} {index !== sections.length - 1 && } ))} ); } function getToolSettings() { const Settings = toolsById[selectedToolId].SettingsComponent; if ( !Settings || (selectedToolId !== "fog" && selectedToolId !== "drawing" && selectedToolId !== "pointer" && selectedToolId !== "select") ) { return null; } return ( ) => onToolSettingChange({ [selectedToolId]: { ...toolSettings[selectedToolId], ...change, }, }) } onToolAction={onToolAction} disabledActions={disabledSettings[selectedToolId]} /> ); } function handleKeyDown(event: KeyboardEvent) { if (shortcuts.moveTool(event) && !disabledControls.includes("move")) { onSelectedToolChange("move"); } if (shortcuts.selectTool(event) && !disabledControls.includes("select")) { onSelectedToolChange("select"); } if (shortcuts.drawingTool(event) && !disabledControls.includes("drawing")) { onSelectedToolChange("drawing"); } if (shortcuts.fogTool(event) && !disabledControls.includes("fog")) { onSelectedToolChange("fog"); } if (shortcuts.measureTool(event) && !disabledControls.includes("measure")) { onSelectedToolChange("measure"); } if (shortcuts.pointerTool(event) && !disabledControls.includes("pointer")) { onSelectedToolChange("pointer"); } if (shortcuts.noteTool(event) && !disabledControls.includes("note")) { onSelectedToolChange("note"); } if (shortcuts.redo(event) && !disabledControls.includes("redo")) { onRedo(); } if (shortcuts.undo(event) && !disabledControls.includes("undo")) { onUndo(); } } useKeyboard(handleKeyDown); return ( <> {controls} {getToolSettings()} setFullScreen(!fullScreen)} aria-label={fullScreen ? "Exit Full Screen" : "Enter Full Screen"} title={fullScreen ? "Exit Full Screen" : "Enter Full Screen"} > {fullScreen ? : } ); } export default MapContols;