Refactor konva components and map tools
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import Konva from "konva";
|
||||
import { KonvaEventObject } from "konva/lib/Node";
|
||||
import { useState } from "react";
|
||||
import NoteDragOverlay from "../components/note/NoteDragOverlay";
|
||||
import NoteMenu from "../components/note/NoteMenu";
|
||||
import NoteTool from "../components/tools/NoteTool";
|
||||
import { NoteChangeEventHandler, NoteRemoveEventHander } from "../types/Events";
|
||||
import { Map, MapToolId } from "../types/Map";
|
||||
import { MapState } from "../types/MapState";
|
||||
import { Note, NoteDraggingOptions, NoteMenuOptions } from "../types/Note";
|
||||
|
||||
function useMapNotes(
|
||||
map: Map | null,
|
||||
mapState: MapState | null,
|
||||
onNoteChange: NoteChangeEventHandler,
|
||||
onNoteRemove: NoteRemoveEventHander,
|
||||
selectedToolId: MapToolId,
|
||||
allowNoteEditing: boolean
|
||||
) {
|
||||
const [isNoteMenuOpen, setIsNoteMenuOpen] = useState<boolean>(false);
|
||||
const [noteMenuOptions, setNoteMenuOptions] = useState<NoteMenuOptions>();
|
||||
const [noteDraggingOptions, setNoteDraggingOptions] =
|
||||
useState<NoteDraggingOptions>();
|
||||
function handleNoteMenuOpen(noteId: string, noteNode: Konva.Node) {
|
||||
setNoteMenuOptions({ noteId, noteNode });
|
||||
setIsNoteMenuOpen(true);
|
||||
}
|
||||
|
||||
function handleNoteDragStart(
|
||||
event: KonvaEventObject<DragEvent>,
|
||||
noteId: string
|
||||
) {
|
||||
setNoteDraggingOptions({ dragging: true, noteId, noteGroup: event.target });
|
||||
}
|
||||
|
||||
function handleNoteDragEnd() {
|
||||
noteDraggingOptions &&
|
||||
setNoteDraggingOptions({ ...noteDraggingOptions, dragging: false });
|
||||
}
|
||||
|
||||
function handleNoteRemove(noteId: string) {
|
||||
onNoteRemove(noteId);
|
||||
setNoteDraggingOptions(undefined);
|
||||
}
|
||||
|
||||
const notes = (
|
||||
<NoteTool
|
||||
map={map}
|
||||
active={selectedToolId === "note"}
|
||||
onNoteAdd={onNoteChange}
|
||||
onNoteChange={onNoteChange}
|
||||
notes={
|
||||
mapState
|
||||
? Object.values(mapState.notes).sort((a, b) =>
|
||||
sortNotes(a, b, noteDraggingOptions)
|
||||
)
|
||||
: []
|
||||
}
|
||||
onNoteMenuOpen={handleNoteMenuOpen}
|
||||
draggable={
|
||||
allowNoteEditing &&
|
||||
(selectedToolId === "note" || selectedToolId === "move")
|
||||
}
|
||||
onNoteDragStart={handleNoteDragStart}
|
||||
onNoteDragEnd={handleNoteDragEnd}
|
||||
fadeOnHover={selectedToolId === "drawing"}
|
||||
/>
|
||||
);
|
||||
|
||||
const noteMenu = (
|
||||
<NoteMenu
|
||||
isOpen={isNoteMenuOpen}
|
||||
onRequestClose={() => setIsNoteMenuOpen(false)}
|
||||
onNoteChange={onNoteChange}
|
||||
note={noteMenuOptions && mapState?.notes[noteMenuOptions.noteId]}
|
||||
noteNode={noteMenuOptions?.noteNode}
|
||||
map={map}
|
||||
/>
|
||||
);
|
||||
|
||||
const noteDragOverlay = noteDraggingOptions ? (
|
||||
<NoteDragOverlay
|
||||
dragging={noteDraggingOptions.dragging}
|
||||
noteGroup={noteDraggingOptions.noteGroup}
|
||||
noteId={noteDraggingOptions.noteId}
|
||||
onNoteRemove={handleNoteRemove}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return { notes, noteMenu, noteDragOverlay };
|
||||
}
|
||||
|
||||
export default useMapNotes;
|
||||
|
||||
function sortNotes(
|
||||
a: Note,
|
||||
b: Note,
|
||||
noteDraggingOptions?: NoteDraggingOptions
|
||||
) {
|
||||
if (
|
||||
noteDraggingOptions &&
|
||||
noteDraggingOptions.dragging &&
|
||||
noteDraggingOptions.noteId === a.id
|
||||
) {
|
||||
// If dragging token `a` move above
|
||||
return 1;
|
||||
} else if (
|
||||
noteDraggingOptions &&
|
||||
noteDraggingOptions.dragging &&
|
||||
noteDraggingOptions.noteId === b.id
|
||||
) {
|
||||
// If dragging token `b` move above
|
||||
return -1;
|
||||
} else {
|
||||
// Else sort so last modified is on top
|
||||
return a.lastModified - b.lastModified;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import { Group } from "react-konva";
|
||||
|
||||
import { Map, MapToolId } from "../types/Map";
|
||||
import { MapState } from "../types/MapState";
|
||||
import {
|
||||
TokenCategory,
|
||||
TokenDraggingOptions,
|
||||
TokenMenuOptions,
|
||||
} from "../types/Token";
|
||||
import { TokenState } from "../types/TokenState";
|
||||
import {
|
||||
MapTokenStateRemoveHandler,
|
||||
TokenStateChangeEventHandler,
|
||||
} from "../types/Events";
|
||||
import { useState } from "react";
|
||||
import Konva from "konva";
|
||||
import Token from "../components/konva/Token";
|
||||
import { KonvaEventObject } from "konva/lib/Node";
|
||||
import TokenMenu from "../components/token/TokenMenu";
|
||||
import TokenDragOverlay from "../components/token/TokenDragOverlay";
|
||||
|
||||
function useMapTokens(
|
||||
map: Map | null,
|
||||
mapState: MapState | null,
|
||||
onTokenStateChange: TokenStateChangeEventHandler,
|
||||
onTokenStateRemove: MapTokenStateRemoveHandler,
|
||||
selectedToolId: MapToolId,
|
||||
disabledTokens: Record<string, boolean>
|
||||
) {
|
||||
const [isTokenMenuOpen, setIsTokenMenuOpen] = useState<boolean>(false);
|
||||
const [tokenMenuOptions, setTokenMenuOptions] = useState<TokenMenuOptions>();
|
||||
const [tokenDraggingOptions, setTokenDraggingOptions] =
|
||||
useState<TokenDraggingOptions>();
|
||||
|
||||
function handleTokenMenuOpen(tokenStateId: string, tokenImage: Konva.Node) {
|
||||
setTokenMenuOptions({ tokenStateId, tokenImage });
|
||||
setIsTokenMenuOpen(true);
|
||||
}
|
||||
|
||||
function handleTokenDragStart(
|
||||
event: KonvaEventObject<DragEvent>,
|
||||
tokenStateId: string
|
||||
) {
|
||||
setTokenDraggingOptions({
|
||||
dragging: true,
|
||||
tokenStateId,
|
||||
tokenNode: event.target,
|
||||
});
|
||||
}
|
||||
|
||||
function handleTokenDragEnd() {
|
||||
tokenDraggingOptions &&
|
||||
setTokenDraggingOptions({
|
||||
...tokenDraggingOptions,
|
||||
dragging: false,
|
||||
});
|
||||
}
|
||||
|
||||
function handleTokenStateRemove(tokenState: TokenState) {
|
||||
onTokenStateRemove(tokenState);
|
||||
setTokenDraggingOptions(undefined);
|
||||
}
|
||||
|
||||
const tokens = map && mapState && (
|
||||
<Group>
|
||||
{Object.values(mapState.tokens)
|
||||
.sort((a, b) => sortMapTokenStates(a, b, tokenDraggingOptions))
|
||||
.map((tokenState) => (
|
||||
<Token
|
||||
key={tokenState.id}
|
||||
tokenState={tokenState}
|
||||
onTokenStateChange={onTokenStateChange}
|
||||
onTokenMenuOpen={handleTokenMenuOpen}
|
||||
onTokenDragStart={handleTokenDragStart}
|
||||
onTokenDragEnd={handleTokenDragEnd}
|
||||
draggable={
|
||||
selectedToolId === "move" &&
|
||||
!(tokenState.id in disabledTokens) &&
|
||||
!tokenState.locked
|
||||
}
|
||||
fadeOnHover={selectedToolId === "drawing"}
|
||||
map={map}
|
||||
/>
|
||||
))}
|
||||
</Group>
|
||||
);
|
||||
|
||||
const tokenMenu = (
|
||||
<TokenMenu
|
||||
isOpen={isTokenMenuOpen}
|
||||
onRequestClose={() => setIsTokenMenuOpen(false)}
|
||||
onTokenStateChange={onTokenStateChange}
|
||||
tokenState={
|
||||
tokenMenuOptions && mapState?.tokens[tokenMenuOptions.tokenStateId]
|
||||
}
|
||||
tokenImage={tokenMenuOptions?.tokenImage}
|
||||
map={map}
|
||||
/>
|
||||
);
|
||||
|
||||
const tokenDraggingState =
|
||||
tokenDraggingOptions && mapState?.tokens[tokenDraggingOptions.tokenStateId];
|
||||
|
||||
const tokenDragOverlay = tokenDraggingOptions && tokenDraggingState && (
|
||||
<TokenDragOverlay
|
||||
onTokenStateRemove={handleTokenStateRemove}
|
||||
tokenState={tokenDraggingState}
|
||||
tokenNode={tokenDraggingOptions.tokenNode}
|
||||
dragging={!!(tokenDraggingOptions && tokenDraggingOptions.dragging)}
|
||||
/>
|
||||
);
|
||||
|
||||
return { tokens, tokenMenu, tokenDragOverlay };
|
||||
}
|
||||
|
||||
export default useMapTokens;
|
||||
|
||||
function getMapTokenCategoryWeight(category: TokenCategory) {
|
||||
switch (category) {
|
||||
case "character":
|
||||
return 0;
|
||||
case "vehicle":
|
||||
return 1;
|
||||
case "prop":
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort so vehicles render below other tokens
|
||||
function sortMapTokenStates(
|
||||
a: TokenState,
|
||||
b: TokenState,
|
||||
tokenDraggingOptions?: TokenDraggingOptions
|
||||
) {
|
||||
// If categories are different sort in order "prop", "vehicle", "character"
|
||||
if (b.category !== a.category) {
|
||||
const aWeight = getMapTokenCategoryWeight(a.category);
|
||||
const bWeight = getMapTokenCategoryWeight(b.category);
|
||||
return bWeight - aWeight;
|
||||
} else if (
|
||||
tokenDraggingOptions &&
|
||||
tokenDraggingOptions.dragging &&
|
||||
tokenDraggingOptions.tokenStateId === a.id
|
||||
) {
|
||||
// If dragging token a move above
|
||||
return 1;
|
||||
} else if (
|
||||
tokenDraggingOptions &&
|
||||
tokenDraggingOptions.dragging &&
|
||||
tokenDraggingOptions.tokenStateId === b.id
|
||||
) {
|
||||
// If dragging token b move above
|
||||
return -1;
|
||||
} else {
|
||||
// Else sort so last modified is on top
|
||||
return a.lastModified - b.lastModified;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user