import { useEffect, useState } from "react"; import { v4 as uuid } from "uuid"; import SelectionDragOverlay from "../components/selection/SelectionDragOverlay"; import SelectionMenu from "../components/selection/SelectionMenu"; import SelectTool from "../components/tools/SelectTool"; import { useBlur, useKeyboard } from "../contexts/KeyboardContext"; import { useUserId } from "../contexts/UserIdContext"; import shortcuts from "../shortcuts"; import { SelectionItemsChangeEventHandler, SelectionItemsCreateEventHandler, SelectionItemsRemoveEventHandler, } from "../types/Events"; import { Map, MapToolId } from "../types/Map"; import { MapState } from "../types/MapState"; import { Note } from "../types/Note"; import { Selection } from "../types/Select"; import { SelectToolSettings } from "../types/Select"; import { TokenState } from "../types/TokenState"; function useMapSelection( map: Map | null, mapState: MapState | null, onSelectionItemsChange: SelectionItemsChangeEventHandler, onSelectionItemsRemove: SelectionItemsRemoveEventHandler, onSelectionItemsCreate: SelectionItemsCreateEventHandler, selectedToolId: MapToolId, settings: SelectToolSettings ) { const userId = useUserId(); const disabledTokens: Record = {}; const disabledNotes: Record = {}; if (mapState && map && map.owner !== userId) { if (!mapState.editFlags.includes("tokens")) { for (let token of Object.values(mapState.tokens)) { if (token.owner !== userId) { disabledTokens[token.id] = true; } } } if (!mapState.editFlags.includes("notes")) { for (let note of Object.values(mapState.notes)) { disabledNotes[note.id] = true; } } } const [isSelectionMenuOpen, setIsSelectionMenuOpen] = useState(false); const [isSelectionDragging, setIsSelectionDragging] = useState(false); const [selection, setSelection] = useState(null); function handleSelectionMenuOpen(open: boolean) { setIsSelectionMenuOpen(open); } const active = selectedToolId === "select"; // Remove selection when changing tools useEffect(() => { if (!active) { setSelection(null); setIsSelectionMenuOpen(false); } }, [active]); // Remove selection when changing maps useEffect(() => { setSelection(null); setIsSelectionMenuOpen(false); }, [map]); function handleSelectionDragStart() { if (duplicateSelection && selection) { const tokenStates: TokenState[] = []; const notes: Note[] = []; for (let item of selection.items) { if (item.type === "token") { const token = mapState?.tokens[item.id]; if (token && !token.locked) { tokenStates.push({ ...token, id: uuid() }); } } else { const note = mapState?.notes[item.id]; if (note && !note.locked) { notes.push({ ...note, id: uuid() }); } } } onSelectionItemsCreate(tokenStates, notes); } setIsSelectionDragging(true); } function handleSelectionDragEnd() { setIsSelectionDragging(false); } function handleSelectionItemsRemove( tokenStateIds: string[], noteIds: string[] ) { setSelection(null); onSelectionItemsRemove(tokenStateIds, noteIds); } const [duplicateSelection, setDuplicateSelection] = useState(false); function handleKeyDown(event: KeyboardEvent) { if (shortcuts.duplicate(event)) { setDuplicateSelection(true); } if (shortcuts.delete(event) && selection && active) { const tokenIds: string[] = []; const noteIds: string[] = []; for (let item of selection.items) { if (item.type === "token") { tokenIds.push(item.id); } else { noteIds.push(item.id); } } handleSelectionItemsRemove(tokenIds, noteIds); setIsSelectionMenuOpen(false); } } function handleKeyUp(event: KeyboardEvent) { if (shortcuts.duplicate(event)) { setDuplicateSelection(false); } } function handleBlur() { setDuplicateSelection(false); } useKeyboard(handleKeyDown, handleKeyUp); useBlur(handleBlur); const selectionTool = map ? ( ) : null; const selectionMenu = ( setIsSelectionMenuOpen(false)} onRequestOpen={() => setIsSelectionMenuOpen(true)} selection={selection} onSelectionChange={setSelection} onSelectionItemsChange={onSelectionItemsChange} onSelectionItemsCreate={onSelectionItemsCreate} map={map} mapState={mapState} /> ); const selectionDragOverlay = selection ? ( ) : null; return { selectionTool, selectionMenu, selectionDragOverlay }; } export default useMapSelection;