diff --git a/src/components/konva/Selection.tsx b/src/components/konva/Selection.tsx index bd33d4c..130e369 100644 --- a/src/components/konva/Selection.tsx +++ b/src/components/konva/Selection.tsx @@ -30,6 +30,8 @@ type SelectionProps = { onSelectionChange: (selection: SelectionType | null) => void; onSelectionItemsChange: SelectionItemsChangeEventHandler; onPreventSelectionChange: (preventSelection: boolean) => void; + onSelectionDragStart: () => void; + onSelectionDragEnd: () => void; } & Konva.ShapeConfig; function Selection({ @@ -37,6 +39,8 @@ function Selection({ onSelectionChange, onSelectionItemsChange, onPreventSelectionChange, + onSelectionDragStart, + onSelectionDragEnd, ...props }: SelectionProps) { const userId = useUserId(); @@ -75,6 +79,7 @@ function Selection({ } } } + onSelectionDragStart(); } function handleDragMove(event: Konva.KonvaEventObject) { @@ -119,6 +124,7 @@ function Selection({ }); intersectingNodesRef.current = []; onPreventSelectionChange(false); + onSelectionDragEnd(); } function handlePointerDown() { diff --git a/src/components/map/Map.tsx b/src/components/map/Map.tsx index 2b6d16b..3ef2fd1 100644 --- a/src/components/map/Map.tsx +++ b/src/components/map/Map.tsx @@ -38,6 +38,7 @@ import { TokenStateChangeEventHandler, NoteCreateEventHander, SelectionItemsChangeEventHandler, + SelectionItemsRemoveEventHandler, } from "../../types/Events"; import useMapTokens from "../../hooks/useMapTokens"; @@ -52,6 +53,7 @@ type MapProps = { onMapTokenStateChange: TokenStateChangeEventHandler; onMapTokenStateRemove: TokenStateRemoveHandler; onSelectionItemsChange: SelectionItemsChangeEventHandler; + onSelectionItemsRemove: SelectionItemsRemoveEventHandler; onMapChange: MapChangeEventHandler; onMapReset: MapResetEventHandler; onMapDraw: (action: Action) => void; @@ -72,6 +74,7 @@ function Map({ onMapTokenStateChange, onMapTokenStateRemove, onSelectionItemsChange, + onSelectionItemsRemove, onMapChange, onMapReset, onMapDraw, @@ -149,13 +152,15 @@ function Map({ !!(map?.owner === userId || mapState?.editFlags.includes("notes")) ); - const { selectionTool, selectionMenu } = useMapSelection( - map, - mapState, - onSelectionItemsChange, - selectedToolId, - settings.select - ); + const { selectionTool, selectionMenu, selectionDragOverlay } = + useMapSelection( + map, + mapState, + onSelectionItemsChange, + onSelectionItemsRemove, + selectedToolId, + settings.select + ); return ( @@ -184,6 +189,7 @@ function Map({ {selectionMenu} {tokenDragOverlay} {noteDragOverlay} + {selectionDragOverlay} } selectedToolId={selectedToolId} diff --git a/src/components/note/NoteDragOverlay.tsx b/src/components/note/NoteDragOverlay.tsx index aaa0916..fa55499 100644 --- a/src/components/note/NoteDragOverlay.tsx +++ b/src/components/note/NoteDragOverlay.tsx @@ -1,4 +1,3 @@ -import Konva from "konva"; import { NoteRemoveEventHander } from "../../types/Events"; import DragOverlay from "../map/DragOverlay"; @@ -6,27 +5,19 @@ import DragOverlay from "../map/DragOverlay"; type NoteDragOverlayProps = { onNoteRemove: NoteRemoveEventHander; noteId: string; - noteGroup: Konva.Node; dragging: boolean; }; function NoteDragOverlay({ onNoteRemove, noteId, - noteGroup, dragging, }: NoteDragOverlayProps) { function handleNoteRemove() { onNoteRemove([noteId]); } - return ( - - ); + return ; } export default NoteDragOverlay; diff --git a/src/components/selection/SelectionDragOverlay.tsx b/src/components/selection/SelectionDragOverlay.tsx new file mode 100644 index 0000000..947b234 --- /dev/null +++ b/src/components/selection/SelectionDragOverlay.tsx @@ -0,0 +1,33 @@ +import { SelectionItemsRemoveEventHandler } from "../../types/Events"; +import { Selection } from "../../types/Select"; + +import DragOverlay from "../map/DragOverlay"; + +type NoteDragOverlayProps = { + onSelectionItemsRemove: SelectionItemsRemoveEventHandler; + selection: Selection; + dragging: boolean; +}; + +function NoteDragOverlay({ + onSelectionItemsRemove, + selection, + dragging, +}: NoteDragOverlayProps) { + function handleNoteRemove() { + 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); + } + } + onSelectionItemsRemove(tokenIds, noteIds); + } + + return ; +} + +export default NoteDragOverlay; diff --git a/src/components/tools/SelectTool.tsx b/src/components/tools/SelectTool.tsx index 3c7c576..04d4876 100644 --- a/src/components/tools/SelectTool.tsx +++ b/src/components/tools/SelectTool.tsx @@ -40,6 +40,8 @@ type MapSelectProps = { selection: SelectionType | null; onSelectionChange: React.Dispatch>; onSelectionMenuOpen: (open: boolean) => void; + onSelectionDragStart: () => void; + onSelectionDragEnd: () => void; }; function SelectTool({ @@ -49,6 +51,8 @@ function SelectTool({ selection, onSelectionChange, onSelectionMenuOpen, + onSelectionDragStart, + onSelectionDragEnd, }: MapSelectProps) { const stageScale = useDebouncedStageScale(); const mapWidth = useMapWidth(); @@ -255,6 +259,8 @@ function SelectTool({ onPreventSelectionChange={(prevent: boolean) => (preventSelectionRef.current = prevent) } + onSelectionDragStart={onSelectionDragStart} + onSelectionDragEnd={onSelectionDragEnd} /> )} diff --git a/src/hooks/useMapSelection.tsx b/src/hooks/useMapSelection.tsx index 33e2abf..4d04e80 100644 --- a/src/hooks/useMapSelection.tsx +++ b/src/hooks/useMapSelection.tsx @@ -1,7 +1,11 @@ import { useEffect, useState } from "react"; +import SelectionDragOverlay from "../components/selection/SelectionDragOverlay"; import SelectionMenu from "../components/selection/SelectionMenu"; import SelectTool from "../components/tools/SelectTool"; -import { SelectionItemsChangeEventHandler } from "../types/Events"; +import { + SelectionItemsChangeEventHandler, + SelectionItemsRemoveEventHandler, +} from "../types/Events"; import { Map, MapToolId } from "../types/Map"; import { MapState } from "../types/MapState"; import { Selection } from "../types/Select"; @@ -11,11 +15,13 @@ function useMapSelection( map: Map | null, mapState: MapState | null, onSelectionItemsChange: SelectionItemsChangeEventHandler, + onSelectionItemsRemove: SelectionItemsRemoveEventHandler, selectedToolId: MapToolId, settings: SelectToolSettings ) { const [isSelectionMenuOpen, setIsSelectionMenuOpen] = useState(false); + const [isSelectionDragging, setIsSelectionDragging] = useState(false); const [selection, setSelection] = useState(null); function handleSelectionMenuOpen(open: boolean) { @@ -31,6 +37,22 @@ function useMapSelection( } }, [active]); + function handleSelectionDragStart() { + setIsSelectionDragging(true); + } + + function handleSelectionDragEnd() { + setIsSelectionDragging(false); + } + + function handleSelectionItemsRemove( + tokenStateIds: string[], + noteIds: string[] + ) { + setSelection(null); + onSelectionItemsRemove(tokenStateIds, noteIds); + } + const selectionTool = ( ); @@ -53,7 +77,15 @@ function useMapSelection( /> ); - return { selectionTool, selectionMenu }; + const selectionDragOverlay = selection ? ( + + ) : null; + + return { selectionTool, selectionMenu, selectionDragOverlay }; } export default useMapSelection; diff --git a/src/network/NetworkedMapAndTokens.tsx b/src/network/NetworkedMapAndTokens.tsx index 702a0ec..9657c9c 100644 --- a/src/network/NetworkedMapAndTokens.tsx +++ b/src/network/NetworkedMapAndTokens.tsx @@ -330,6 +330,18 @@ function NetworkedMapAndTokens({ session }: { session: Session }) { ]); } + function handleSelectionItemsRemove( + tokenStateIds: string[], + noteIds: string[] + ) { + const tokenAction = new RemoveStatesAction(tokenStateIds); + const noteAction = new RemoveStatesAction(noteIds); + addActions([ + { type: "tokens", action: tokenAction }, + { type: "notes", action: noteAction }, + ]); + } + useEffect(() => { async function handlePeerData({ id, data, reply }: PeerDataEvent) { if (id === "assetRequest") { @@ -394,6 +406,7 @@ function NetworkedMapAndTokens({ session }: { session: Session }) { onMapTokenStateChange={handleMapTokenStateChange} onMapTokenStateRemove={handleMapTokenStateRemove} onSelectionItemsChange={handleSelectionItemsChange} + onSelectionItemsRemove={handleSelectionItemsRemove} onMapChange={handleMapChange} onMapReset={handleMapReset} onMapDraw={handleMapDraw} diff --git a/src/types/Events.ts b/src/types/Events.ts index 95661ff..8066d66 100644 --- a/src/types/Events.ts +++ b/src/types/Events.ts @@ -63,3 +63,7 @@ export type SelectionItemsChangeEventHandler = ( tokenChanges: Record>, noteChanges: Record> ) => void; +export type SelectionItemsRemoveEventHandler = ( + tokenStateIds: string[], + noteIds: string[] +) => void;