Typescript
This commit is contained in:
parent
e48d19a817
commit
fecf8090ea
@ -3,7 +3,7 @@ import Creatable from "react-select/creatable";
|
|||||||
import { useThemeUI } from "theme-ui";
|
import { useThemeUI } from "theme-ui";
|
||||||
|
|
||||||
type SelectProps = {
|
type SelectProps = {
|
||||||
creatable: boolean;
|
creatable?: boolean;
|
||||||
} & Props;
|
} & Props;
|
||||||
|
|
||||||
function Select({ creatable, ...props }: SelectProps) {
|
function Select({ creatable, ...props }: SelectProps) {
|
||||||
@ -76,4 +76,8 @@ function Select({ creatable, ...props }: SelectProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Select.defaultProps = {
|
||||||
|
creatable: false,
|
||||||
|
};
|
||||||
|
|
||||||
export default Select;
|
export default Select;
|
||||||
|
@ -72,29 +72,31 @@ function DragOverlay({ dragging, node, onRemove }: DragOverlayProps) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!dragging) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
dragging && (
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
sx={{
|
position: "absolute",
|
||||||
position: "absolute",
|
bottom: "32px",
|
||||||
bottom: "32px",
|
left: "50%",
|
||||||
left: "50%",
|
borderRadius: "50%",
|
||||||
borderRadius: "50%",
|
transform: isRemoveHovered
|
||||||
transform: isRemoveHovered
|
? "translateX(-50%) scale(2.0)"
|
||||||
? "translateX(-50%) scale(2.0)"
|
: "translateX(-50%) scale(1.5)",
|
||||||
: "translateX(-50%) scale(1.5)",
|
transition: "transform 250ms ease",
|
||||||
transition: "transform 250ms ease",
|
color: isRemoveHovered ? "primary" : "text",
|
||||||
color: isRemoveHovered ? "primary" : "text",
|
pointerEvents: "none",
|
||||||
pointerEvents: "none",
|
}}
|
||||||
}}
|
bg="overlay"
|
||||||
bg="overlay"
|
ref={removeTokenRef}
|
||||||
ref={removeTokenRef}
|
>
|
||||||
>
|
<IconButton>
|
||||||
<IconButton>
|
<RemoveTokenIcon />
|
||||||
<RemoveTokenIcon />
|
</IconButton>
|
||||||
</IconButton>
|
</Box>
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Box } from "theme-ui";
|
import { Box } from "theme-ui";
|
||||||
import { useToasts } from "react-toast-notifications";
|
import { useToasts } from "react-toast-notifications";
|
||||||
|
|
||||||
@ -27,15 +27,48 @@ import {
|
|||||||
RemoveStatesAction,
|
RemoveStatesAction,
|
||||||
} from "../../actions";
|
} from "../../actions";
|
||||||
import Session from "../../network/Session";
|
import Session from "../../network/Session";
|
||||||
import { Drawing } from "../../types/Drawing";
|
import { Drawing, DrawingState } from "../../types/Drawing";
|
||||||
import { Fog } from "../../types/Fog";
|
import { Fog, FogState } from "../../types/Fog";
|
||||||
import { Map, MapToolId } from "../../types/Map";
|
import { Map, MapActions, MapToolId } from "../../types/Map";
|
||||||
import { MapState } from "../../types/MapState";
|
import { MapState } from "../../types/MapState";
|
||||||
import { Settings } from "../../types/Settings";
|
import { Settings } from "../../types/Settings";
|
||||||
import {
|
import {
|
||||||
MapChangeEventHandler,
|
MapChangeEventHandler,
|
||||||
MapResetEventHandler,
|
MapResetEventHandler,
|
||||||
|
MapTokensStateCreateHandler,
|
||||||
|
MapTokenStateRemoveHandler,
|
||||||
|
NoteChangeEventHandler,
|
||||||
|
NoteRemoveEventHander,
|
||||||
|
TokenStateChangeEventHandler,
|
||||||
} from "../../types/Events";
|
} from "../../types/Events";
|
||||||
|
import Action from "../../actions/Action";
|
||||||
|
import Konva from "konva";
|
||||||
|
import { TokenDraggingOptions, TokenMenuOptions } from "../../types/Token";
|
||||||
|
import { Note, NoteDraggingOptions, NoteMenuOptions } from "../../types/Note";
|
||||||
|
|
||||||
|
type MapProps = {
|
||||||
|
map: Map;
|
||||||
|
mapState: MapState;
|
||||||
|
mapActions: MapActions;
|
||||||
|
onMapTokenStateChange: TokenStateChangeEventHandler;
|
||||||
|
onMapTokenStateRemove: MapTokenStateRemoveHandler;
|
||||||
|
onMapChange: MapChangeEventHandler;
|
||||||
|
onMapReset: MapResetEventHandler;
|
||||||
|
onMapDraw: (action: Action<DrawingState>) => void;
|
||||||
|
onMapDrawUndo: () => void;
|
||||||
|
onMapDrawRedo: () => void;
|
||||||
|
onFogDraw: (action: Action<FogState>) => void;
|
||||||
|
onFogDrawUndo: () => void;
|
||||||
|
onFogDrawRedo: () => void;
|
||||||
|
onMapNoteChange: NoteChangeEventHandler;
|
||||||
|
onMapNoteRemove: NoteRemoveEventHander;
|
||||||
|
allowMapDrawing: boolean;
|
||||||
|
allowFogDrawing: boolean;
|
||||||
|
allowMapChange: boolean;
|
||||||
|
allowNoteEditing: boolean;
|
||||||
|
disabledTokens: string[];
|
||||||
|
session: Session;
|
||||||
|
};
|
||||||
|
|
||||||
function Map({
|
function Map({
|
||||||
map,
|
map,
|
||||||
@ -59,29 +92,7 @@ function Map({
|
|||||||
allowNoteEditing,
|
allowNoteEditing,
|
||||||
disabledTokens,
|
disabledTokens,
|
||||||
session,
|
session,
|
||||||
}: {
|
}: MapProps) {
|
||||||
map: Map;
|
|
||||||
mapState: MapState;
|
|
||||||
mapActions: ;
|
|
||||||
onMapTokenStateChange: ;
|
|
||||||
onMapTokenStateRemove: ;
|
|
||||||
onMapChange: MapChangeEventHandler;
|
|
||||||
onMapReset: MapResetEventHandler;
|
|
||||||
onMapDraw: ;
|
|
||||||
onMapDrawUndo: ;
|
|
||||||
onMapDrawRedo: ;
|
|
||||||
onFogDraw: ;
|
|
||||||
onFogDrawUndo: ;
|
|
||||||
onFogDrawRedo: ;
|
|
||||||
onMapNoteChange: ;
|
|
||||||
onMapNoteRemove: ;
|
|
||||||
allowMapDrawing: boolean;
|
|
||||||
allowFogDrawing: boolean;
|
|
||||||
allowMapChange: boolean;
|
|
||||||
allowNoteEditing: boolean;
|
|
||||||
disabledTokens: ;
|
|
||||||
session: Session;
|
|
||||||
}) {
|
|
||||||
const { addToast } = useToasts();
|
const { addToast } = useToasts();
|
||||||
|
|
||||||
const { tokensById } = useTokenData();
|
const { tokensById } = useTokenData();
|
||||||
@ -141,7 +152,7 @@ function Map({
|
|||||||
onFogDraw(new EditStatesAction(shapes));
|
onFogDraw(new EditStatesAction(shapes));
|
||||||
}
|
}
|
||||||
|
|
||||||
const disabledControls = [];
|
const disabledControls: MapToolId[] = [];
|
||||||
if (!allowMapDrawing) {
|
if (!allowMapDrawing) {
|
||||||
disabledControls.push("drawing");
|
disabledControls.push("drawing");
|
||||||
}
|
}
|
||||||
@ -206,9 +217,10 @@ function Map({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [isTokenMenuOpen, setIsTokenMenuOpen] = useState<boolean>(false);
|
const [isTokenMenuOpen, setIsTokenMenuOpen] = useState<boolean>(false);
|
||||||
const [tokenMenuOptions, setTokenMenuOptions] = useState({});
|
const [tokenMenuOptions, setTokenMenuOptions] = useState<TokenMenuOptions>();
|
||||||
const [tokenDraggingOptions, setTokenDraggingOptions] = useState();
|
const [tokenDraggingOptions, setTokenDraggingOptions] =
|
||||||
function handleTokenMenuOpen(tokenStateId: string, tokenImage) {
|
useState<TokenDraggingOptions>();
|
||||||
|
function handleTokenMenuOpen(tokenStateId: string, tokenImage: Konva.Node) {
|
||||||
setTokenMenuOptions({ tokenStateId, tokenImage });
|
setTokenMenuOptions({ tokenStateId, tokenImage });
|
||||||
setIsTokenMenuOpen(true);
|
setIsTokenMenuOpen(true);
|
||||||
}
|
}
|
||||||
@ -220,7 +232,7 @@ function Map({
|
|||||||
tokenDraggingOptions={tokenDraggingOptions}
|
tokenDraggingOptions={tokenDraggingOptions}
|
||||||
setTokenDraggingOptions={setTokenDraggingOptions}
|
setTokenDraggingOptions={setTokenDraggingOptions}
|
||||||
onMapTokenStateChange={onMapTokenStateChange}
|
onMapTokenStateChange={onMapTokenStateChange}
|
||||||
handleTokenMenuOpen={handleTokenMenuOpen}
|
onTokenMenuOpen={handleTokenMenuOpen}
|
||||||
selectedToolId={selectedToolId}
|
selectedToolId={selectedToolId}
|
||||||
disabledTokens={disabledTokens}
|
disabledTokens={disabledTokens}
|
||||||
/>
|
/>
|
||||||
@ -231,8 +243,12 @@ function Map({
|
|||||||
isOpen={isTokenMenuOpen}
|
isOpen={isTokenMenuOpen}
|
||||||
onRequestClose={() => setIsTokenMenuOpen(false)}
|
onRequestClose={() => setIsTokenMenuOpen(false)}
|
||||||
onTokenStateChange={onMapTokenStateChange}
|
onTokenStateChange={onMapTokenStateChange}
|
||||||
tokenState={mapState && mapState.tokens[tokenMenuOptions.tokenStateId]}
|
tokenState={
|
||||||
tokenImage={tokenMenuOptions.tokenImage}
|
tokenMenuOptions &&
|
||||||
|
mapState &&
|
||||||
|
mapState.tokens[tokenMenuOptions.tokenStateId]
|
||||||
|
}
|
||||||
|
tokenImage={tokenMenuOptions && tokenMenuOptions.tokenImage}
|
||||||
map={map}
|
map={map}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -241,7 +257,7 @@ function Map({
|
|||||||
<TokenDragOverlay
|
<TokenDragOverlay
|
||||||
onTokenStateRemove={(state) => {
|
onTokenStateRemove={(state) => {
|
||||||
onMapTokenStateRemove(state);
|
onMapTokenStateRemove(state);
|
||||||
setTokenDraggingOptions(null);
|
setTokenDraggingOptions(undefined);
|
||||||
}}
|
}}
|
||||||
onTokenStateChange={onMapTokenStateChange}
|
onTokenStateChange={onMapTokenStateChange}
|
||||||
tokenState={tokenDraggingOptions && tokenDraggingOptions.tokenState}
|
tokenState={tokenDraggingOptions && tokenDraggingOptions.tokenState}
|
||||||
@ -291,14 +307,19 @@ function Map({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [isNoteMenuOpen, setIsNoteMenuOpen] = useState<boolean>(false);
|
const [isNoteMenuOpen, setIsNoteMenuOpen] = useState<boolean>(false);
|
||||||
const [noteMenuOptions, setNoteMenuOptions] = useState({});
|
const [noteMenuOptions, setNoteMenuOptions] = useState<NoteMenuOptions>();
|
||||||
const [noteDraggingOptions, setNoteDraggingOptions] = useState();
|
const [noteDraggingOptions, setNoteDraggingOptions] =
|
||||||
function handleNoteMenuOpen(noteId: string, noteNode) {
|
useState<NoteDraggingOptions>();
|
||||||
|
function handleNoteMenuOpen(noteId: string, noteNode: Konva.Node) {
|
||||||
setNoteMenuOptions({ noteId, noteNode });
|
setNoteMenuOptions({ noteId, noteNode });
|
||||||
setIsNoteMenuOpen(true);
|
setIsNoteMenuOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortNotes(a, b, noteDraggingOptions) {
|
function sortNotes(
|
||||||
|
a: Note,
|
||||||
|
b: Note,
|
||||||
|
noteDraggingOptions?: NoteDraggingOptions
|
||||||
|
) {
|
||||||
if (
|
if (
|
||||||
noteDraggingOptions &&
|
noteDraggingOptions &&
|
||||||
noteDraggingOptions.dragging &&
|
noteDraggingOptions.dragging &&
|
||||||
@ -341,6 +362,7 @@ function Map({
|
|||||||
setNoteDraggingOptions({ dragging: true, noteId, noteGroup: e.target })
|
setNoteDraggingOptions({ dragging: true, noteId, noteGroup: e.target })
|
||||||
}
|
}
|
||||||
onNoteDragEnd={() =>
|
onNoteDragEnd={() =>
|
||||||
|
noteDraggingOptions &&
|
||||||
setNoteDraggingOptions({ ...noteDraggingOptions, dragging: false })
|
setNoteDraggingOptions({ ...noteDraggingOptions, dragging: false })
|
||||||
}
|
}
|
||||||
fadeOnHover={selectedToolId === "drawing"}
|
fadeOnHover={selectedToolId === "drawing"}
|
||||||
@ -352,23 +374,25 @@ function Map({
|
|||||||
isOpen={isNoteMenuOpen}
|
isOpen={isNoteMenuOpen}
|
||||||
onRequestClose={() => setIsNoteMenuOpen(false)}
|
onRequestClose={() => setIsNoteMenuOpen(false)}
|
||||||
onNoteChange={onMapNoteChange}
|
onNoteChange={onMapNoteChange}
|
||||||
note={mapState && mapState.notes[noteMenuOptions.noteId]}
|
note={
|
||||||
noteNode={noteMenuOptions.noteNode}
|
noteMenuOptions && mapState && mapState.notes[noteMenuOptions.noteId]
|
||||||
|
}
|
||||||
|
noteNode={noteMenuOptions?.noteNode}
|
||||||
map={map}
|
map={map}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const noteDragOverlay = (
|
const noteDragOverlay = noteDraggingOptions ? (
|
||||||
<NoteDragOverlay
|
<NoteDragOverlay
|
||||||
dragging={!!(noteDraggingOptions && noteDraggingOptions.dragging)}
|
dragging={noteDraggingOptions.dragging}
|
||||||
noteGroup={noteDraggingOptions && noteDraggingOptions.noteGroup}
|
noteGroup={noteDraggingOptions.noteGroup}
|
||||||
noteId={noteDraggingOptions && noteDraggingOptions.noteId}
|
noteId={noteDraggingOptions.noteId}
|
||||||
onNoteRemove={(noteId) => {
|
onNoteRemove={(noteId) => {
|
||||||
onMapNoteRemove(noteId);
|
onMapNoteRemove(noteId);
|
||||||
setNoteDraggingOptions(null);
|
setNoteDraggingOptions(undefined);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ flexGrow: 1 }}>
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
|
@ -24,8 +24,7 @@ import MapGrid from "./MapGrid";
|
|||||||
import MapGridEditor from "./MapGridEditor";
|
import MapGridEditor from "./MapGridEditor";
|
||||||
import { Map } from "../../types/Map";
|
import { Map } from "../../types/Map";
|
||||||
import { GridInset } from "../../types/Grid";
|
import { GridInset } from "../../types/Grid";
|
||||||
|
import { MapSettingsChangeEventHandler } from "../../types/Events";
|
||||||
type MapSettingsChangeEventHandler = (change: Partial<Map>) => void;
|
|
||||||
|
|
||||||
type MapEditorProps = {
|
type MapEditorProps = {
|
||||||
map: Map;
|
map: Map;
|
||||||
|
@ -9,10 +9,10 @@ type MapMenuProps = {
|
|||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onRequestClose: RequestCloseEventHandler;
|
onRequestClose: RequestCloseEventHandler;
|
||||||
onModalContent: (instance: HTMLDivElement) => void;
|
onModalContent: (instance: HTMLDivElement) => void;
|
||||||
top: number;
|
top: number | string;
|
||||||
left: number;
|
left: number | string;
|
||||||
bottom: number;
|
bottom: number | string;
|
||||||
right: number;
|
right: number | string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
style: React.CSSProperties;
|
style: React.CSSProperties;
|
||||||
excludeNode: Node | null;
|
excludeNode: Node | null;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import shortid from "shortid";
|
import shortid from "shortid";
|
||||||
import { Group } from "react-konva";
|
import { Group } from "react-konva";
|
||||||
|
import Konva from "konva";
|
||||||
|
|
||||||
import { useInteractionEmitter } from "../../contexts/MapInteractionContext";
|
import { useInteractionEmitter } from "../../contexts/MapInteractionContext";
|
||||||
import { useMapStage } from "../../contexts/MapStageContext";
|
import { useMapStage } from "../../contexts/MapStageContext";
|
||||||
@ -13,8 +14,30 @@ import useGridSnapping from "../../hooks/useGridSnapping";
|
|||||||
|
|
||||||
import Note from "../note/Note";
|
import Note from "../note/Note";
|
||||||
|
|
||||||
|
import { Map } from "../../types/Map";
|
||||||
|
import { Note as NoteType } from "../../types/Note";
|
||||||
|
import {
|
||||||
|
NoteAddEventHander,
|
||||||
|
NoteChangeEventHandler,
|
||||||
|
NoteDragEventHandler,
|
||||||
|
NoteMenuOpenEventHandler,
|
||||||
|
} from "../../types/Events";
|
||||||
|
|
||||||
const defaultNoteSize = 2;
|
const defaultNoteSize = 2;
|
||||||
|
|
||||||
|
type MapNoteProps = {
|
||||||
|
map: Map;
|
||||||
|
active: boolean;
|
||||||
|
onNoteAdd: NoteAddEventHander;
|
||||||
|
onNoteChange: NoteChangeEventHandler;
|
||||||
|
notes: NoteType[];
|
||||||
|
onNoteMenuOpen: NoteMenuOpenEventHandler;
|
||||||
|
draggable: boolean;
|
||||||
|
onNoteDragStart: NoteDragEventHandler;
|
||||||
|
onNoteDragEnd: NoteDragEventHandler;
|
||||||
|
fadeOnHover: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
function MapNotes({
|
function MapNotes({
|
||||||
map,
|
map,
|
||||||
active,
|
active,
|
||||||
@ -26,14 +49,14 @@ function MapNotes({
|
|||||||
onNoteDragStart,
|
onNoteDragStart,
|
||||||
onNoteDragEnd,
|
onNoteDragEnd,
|
||||||
fadeOnHover,
|
fadeOnHover,
|
||||||
}) {
|
}: MapNoteProps) {
|
||||||
const interactionEmitter = useInteractionEmitter();
|
const interactionEmitter = useInteractionEmitter();
|
||||||
const userId = useUserId();
|
const userId = useUserId();
|
||||||
const mapStageRef = useMapStage();
|
const mapStageRef = useMapStage();
|
||||||
const [isBrushDown, setIsBrushDown] = useState(false);
|
const [isBrushDown, setIsBrushDown] = useState(false);
|
||||||
const [noteData, setNoteData] = useState(null);
|
const [noteData, setNoteData] = useState<NoteType | null>(null);
|
||||||
|
|
||||||
const creatingNoteRef = useRef();
|
const creatingNoteRef = useRef<Konva.Group>(null);
|
||||||
|
|
||||||
const snapPositionToGrid = useGridSnapping();
|
const snapPositionToGrid = useGridSnapping();
|
||||||
|
|
||||||
@ -44,8 +67,14 @@ function MapNotes({
|
|||||||
const mapStage = mapStageRef.current;
|
const mapStage = mapStageRef.current;
|
||||||
|
|
||||||
function getBrushPosition() {
|
function getBrushPosition() {
|
||||||
|
if (!mapStage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const mapImage = mapStage.findOne("#mapImage");
|
const mapImage = mapStage.findOne("#mapImage");
|
||||||
let position = getRelativePointerPosition(mapImage);
|
let position = getRelativePointerPosition(mapImage);
|
||||||
|
if (!position) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (map.snapToGrid) {
|
if (map.snapToGrid) {
|
||||||
position = snapPositionToGrid(position);
|
position = snapPositionToGrid(position);
|
||||||
}
|
}
|
||||||
@ -57,6 +86,9 @@ function MapNotes({
|
|||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown() {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
|
if (!brushPosition || !userId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setNoteData({
|
setNoteData({
|
||||||
x: brushPosition.x,
|
x: brushPosition.x,
|
||||||
y: brushPosition.y,
|
y: brushPosition.y,
|
||||||
@ -76,17 +108,25 @@ function MapNotes({
|
|||||||
function handleBrushMove() {
|
function handleBrushMove() {
|
||||||
if (noteData) {
|
if (noteData) {
|
||||||
const brushPosition = getBrushPosition();
|
const brushPosition = getBrushPosition();
|
||||||
setNoteData((prev) => ({
|
if (!brushPosition) {
|
||||||
...prev,
|
return;
|
||||||
x: brushPosition.x,
|
}
|
||||||
y: brushPosition.y,
|
setNoteData((prev) => {
|
||||||
}));
|
if (!prev) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
x: brushPosition.x,
|
||||||
|
y: brushPosition.y,
|
||||||
|
};
|
||||||
|
});
|
||||||
setIsBrushDown(true);
|
setIsBrushDown(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushUp() {
|
function handleBrushUp() {
|
||||||
if (noteData) {
|
if (noteData && creatingNoteRef.current) {
|
||||||
onNoteAdd(noteData);
|
onNoteAdd(noteData);
|
||||||
onNoteMenuOpen(noteData.id, creatingNoteRef.current);
|
onNoteMenuOpen(noteData.id, creatingNoteRef.current);
|
||||||
}
|
}
|
||||||
@ -94,14 +134,14 @@ function MapNotes({
|
|||||||
setIsBrushDown(false);
|
setIsBrushDown(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
interactionEmitter.on("dragStart", handleBrushDown);
|
interactionEmitter?.on("dragStart", handleBrushDown);
|
||||||
interactionEmitter.on("drag", handleBrushMove);
|
interactionEmitter?.on("drag", handleBrushMove);
|
||||||
interactionEmitter.on("dragEnd", handleBrushUp);
|
interactionEmitter?.on("dragEnd", handleBrushUp);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
interactionEmitter.off("dragStart", handleBrushDown);
|
interactionEmitter?.off("dragStart", handleBrushDown);
|
||||||
interactionEmitter.off("drag", handleBrushMove);
|
interactionEmitter?.off("drag", handleBrushMove);
|
||||||
interactionEmitter.off("dragEnd", handleBrushUp);
|
interactionEmitter?.off("dragEnd", handleBrushUp);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,9 +161,7 @@ function MapNotes({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<Group ref={creatingNoteRef}>
|
<Group ref={creatingNoteRef}>
|
||||||
{isBrushDown && noteData && (
|
{isBrushDown && noteData && <Note note={noteData} map={map} />}
|
||||||
<Note note={noteData} map={map} draggable={false} />
|
|
||||||
)}
|
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Group } from "react-konva";
|
import { Group } from "react-konva";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -15,7 +15,17 @@ import {
|
|||||||
} from "../../helpers/konva";
|
} from "../../helpers/konva";
|
||||||
import Vector2 from "../../helpers/Vector2";
|
import Vector2 from "../../helpers/Vector2";
|
||||||
|
|
||||||
import colors from "../../helpers/colors";
|
import colors, { Color } from "../../helpers/colors";
|
||||||
|
|
||||||
|
type MapPointerProps = {
|
||||||
|
active: boolean;
|
||||||
|
position: Vector2;
|
||||||
|
onPointerDown?: (position: Vector2) => void;
|
||||||
|
onPointerMove?: (position: Vector2) => void;
|
||||||
|
onPointerUp?: (position: Vector2) => void;
|
||||||
|
visible: boolean;
|
||||||
|
color: Color;
|
||||||
|
};
|
||||||
|
|
||||||
function MapPointer({
|
function MapPointer({
|
||||||
active,
|
active,
|
||||||
@ -25,7 +35,7 @@ function MapPointer({
|
|||||||
onPointerUp,
|
onPointerUp,
|
||||||
visible,
|
visible,
|
||||||
color,
|
color,
|
||||||
}) {
|
}: MapPointerProps) {
|
||||||
const mapWidth = useMapWidth();
|
const mapWidth = useMapWidth();
|
||||||
const mapHeight = useMapHeight();
|
const mapHeight = useMapHeight();
|
||||||
const interactionEmitter = useInteractionEmitter();
|
const interactionEmitter = useInteractionEmitter();
|
||||||
@ -40,30 +50,36 @@ function MapPointer({
|
|||||||
const mapStage = mapStageRef.current;
|
const mapStage = mapStageRef.current;
|
||||||
|
|
||||||
function getBrushPosition() {
|
function getBrushPosition() {
|
||||||
|
if (!mapStage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const mapImage = mapStage.findOne("#mapImage");
|
const mapImage = mapStage.findOne("#mapImage");
|
||||||
return getRelativePointerPositionNormalized(mapImage);
|
return getRelativePointerPositionNormalized(mapImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushDown() {
|
function handleBrushDown() {
|
||||||
onPointerDown && onPointerDown(getBrushPosition());
|
const brushPosition = getBrushPosition();
|
||||||
|
brushPosition && onPointerDown?.(brushPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushMove() {
|
function handleBrushMove() {
|
||||||
onPointerMove && visible && onPointerMove(getBrushPosition());
|
const brushPosition = getBrushPosition();
|
||||||
|
brushPosition && visible && onPointerMove?.(brushPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushUp() {
|
function handleBrushUp() {
|
||||||
onPointerMove && onPointerUp(getBrushPosition());
|
const brushPosition = getBrushPosition();
|
||||||
|
brushPosition && onPointerUp?.(brushPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
interactionEmitter.on("dragStart", handleBrushDown);
|
interactionEmitter?.on("dragStart", handleBrushDown);
|
||||||
interactionEmitter.on("drag", handleBrushMove);
|
interactionEmitter?.on("drag", handleBrushMove);
|
||||||
interactionEmitter.on("dragEnd", handleBrushUp);
|
interactionEmitter?.on("dragEnd", handleBrushUp);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
interactionEmitter.off("dragStart", handleBrushDown);
|
interactionEmitter?.off("dragStart", handleBrushDown);
|
||||||
interactionEmitter.off("drag", handleBrushMove);
|
interactionEmitter?.off("drag", handleBrushMove);
|
||||||
interactionEmitter.off("dragEnd", handleBrushUp);
|
interactionEmitter?.off("dragEnd", handleBrushUp);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -9,8 +9,16 @@ import { mapSources as defaultMapSources } from "../../maps";
|
|||||||
|
|
||||||
import Divider from "../Divider";
|
import Divider from "../Divider";
|
||||||
import Select from "../Select";
|
import Select from "../Select";
|
||||||
|
import { Map, MapQuality } from "../../types/Map";
|
||||||
|
import { EditFlag, MapState } from "../../types/MapState";
|
||||||
|
import {
|
||||||
|
MapSettingsChangeEventHandler,
|
||||||
|
MapStateSettingsChangeEventHandler,
|
||||||
|
} from "../../types/Events";
|
||||||
|
import { Grid, GridMeasurementType, GridType } from "../../types/Grid";
|
||||||
|
|
||||||
const qualitySettings = [
|
type QualityTypeSetting = { value: MapQuality; label: string };
|
||||||
|
const qualitySettings: QualityTypeSetting[] = [
|
||||||
{ value: "low", label: "Low" },
|
{ value: "low", label: "Low" },
|
||||||
{ value: "medium", label: "Medium" },
|
{ value: "medium", label: "Medium" },
|
||||||
{ value: "high", label: "High" },
|
{ value: "high", label: "High" },
|
||||||
@ -18,42 +26,53 @@ const qualitySettings = [
|
|||||||
{ value: "original", label: "Original" },
|
{ value: "original", label: "Original" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const gridTypeSettings = [
|
type GridTypeSetting = { value: GridType; label: string };
|
||||||
|
const gridTypeSettings: GridTypeSetting[] = [
|
||||||
{ value: "square", label: "Square" },
|
{ value: "square", label: "Square" },
|
||||||
{ value: "hexVertical", label: "Hex Vertical" },
|
{ value: "hexVertical", label: "Hex Vertical" },
|
||||||
{ value: "hexHorizontal", label: "Hex Horizontal" },
|
{ value: "hexHorizontal", label: "Hex Horizontal" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const gridSquareMeasurementTypeSettings = [
|
type GridMeasurementTypeSetting = { value: GridMeasurementType; label: string };
|
||||||
|
const gridSquareMeasurementTypeSettings: GridMeasurementTypeSetting[] = [
|
||||||
{ value: "chebyshev", label: "Chessboard (D&D 5e)" },
|
{ value: "chebyshev", label: "Chessboard (D&D 5e)" },
|
||||||
{ value: "alternating", label: "Alternating Diagonal (D&D 3.5e)" },
|
{ value: "alternating", label: "Alternating Diagonal (D&D 3.5e)" },
|
||||||
{ value: "euclidean", label: "Euclidean" },
|
{ value: "euclidean", label: "Euclidean" },
|
||||||
{ value: "manhattan", label: "Manhattan" },
|
{ value: "manhattan", label: "Manhattan" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const gridHexMeasurementTypeSettings = [
|
const gridHexMeasurementTypeSettings: GridMeasurementTypeSetting[] = [
|
||||||
{ value: "manhattan", label: "Manhattan" },
|
{ value: "manhattan", label: "Manhattan" },
|
||||||
{ value: "euclidean", label: "Euclidean" },
|
{ value: "euclidean", label: "Euclidean" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
type MapSettingsProps = {
|
||||||
|
map: Map;
|
||||||
|
mapState: MapState;
|
||||||
|
onSettingsChange: MapSettingsChangeEventHandler;
|
||||||
|
onStateSettingsChange: MapStateSettingsChangeEventHandler;
|
||||||
|
};
|
||||||
|
|
||||||
function MapSettings({
|
function MapSettings({
|
||||||
map,
|
map,
|
||||||
mapState,
|
mapState,
|
||||||
onSettingsChange,
|
onSettingsChange,
|
||||||
onStateSettingsChange,
|
onStateSettingsChange,
|
||||||
}) {
|
}: MapSettingsProps) {
|
||||||
function handleFlagChange(event, flag) {
|
function handleFlagChange(
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
flag: EditFlag
|
||||||
|
) {
|
||||||
if (event.target.checked) {
|
if (event.target.checked) {
|
||||||
onStateSettingsChange("editFlags", [...mapState.editFlags, flag]);
|
onStateSettingsChange({ editFlags: [...mapState.editFlags, flag] });
|
||||||
} else {
|
} else {
|
||||||
onStateSettingsChange(
|
onStateSettingsChange({
|
||||||
"editFlags",
|
editFlags: mapState.editFlags.filter((f) => f !== flag),
|
||||||
mapState.editFlags.filter((f) => f !== flag)
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleGridSizeXChange(event) {
|
function handleGridSizeXChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
const value = parseInt(event.target.value) || 0;
|
const value = parseInt(event.target.value) || 0;
|
||||||
let grid = {
|
let grid = {
|
||||||
...map.grid,
|
...map.grid,
|
||||||
@ -63,10 +82,10 @@ function MapSettings({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
grid.inset = getGridUpdatedInset(grid, map.width, map.height);
|
grid.inset = getGridUpdatedInset(grid, map.width, map.height);
|
||||||
onSettingsChange("grid", grid);
|
onSettingsChange({ grid });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleGridSizeYChange(event) {
|
function handleGridSizeYChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
const value = parseInt(event.target.value) || 0;
|
const value = parseInt(event.target.value) || 0;
|
||||||
let grid = {
|
let grid = {
|
||||||
...map.grid,
|
...map.grid,
|
||||||
@ -76,12 +95,15 @@ function MapSettings({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
grid.inset = getGridUpdatedInset(grid, map.width, map.height);
|
grid.inset = getGridUpdatedInset(grid, map.width, map.height);
|
||||||
onSettingsChange("grid", grid);
|
onSettingsChange({ grid });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleGridTypeChange(option) {
|
function handleGridTypeChange(option: GridTypeSetting | null) {
|
||||||
|
if (!option) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const type = option.value;
|
const type = option.value;
|
||||||
let grid = {
|
let grid: Grid = {
|
||||||
...map.grid,
|
...map.grid,
|
||||||
type,
|
type,
|
||||||
measurement: {
|
measurement: {
|
||||||
@ -90,10 +112,15 @@ function MapSettings({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
grid.inset = getGridUpdatedInset(grid, map.width, map.height);
|
grid.inset = getGridUpdatedInset(grid, map.width, map.height);
|
||||||
onSettingsChange("grid", grid);
|
onSettingsChange({ grid });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleGridMeasurementTypeChange(option) {
|
function handleGridMeasurementTypeChange(
|
||||||
|
option: GridMeasurementTypeSetting | null
|
||||||
|
) {
|
||||||
|
if (!option) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const grid = {
|
const grid = {
|
||||||
...map.grid,
|
...map.grid,
|
||||||
measurement: {
|
measurement: {
|
||||||
@ -101,10 +128,19 @@ function MapSettings({
|
|||||||
type: option.value,
|
type: option.value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
onSettingsChange("grid", grid);
|
onSettingsChange({ grid });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleGridMeasurementScaleChange(event) {
|
function handleQualityChange(option: QualityTypeSetting | null) {
|
||||||
|
if (!option) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSettingsChange({ quality: option.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleGridMeasurementScaleChange(
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
|
) {
|
||||||
const grid = {
|
const grid = {
|
||||||
...map.grid,
|
...map.grid,
|
||||||
measurement: {
|
measurement: {
|
||||||
@ -112,7 +148,7 @@ function MapSettings({
|
|||||||
scale: event.target.value,
|
scale: event.target.value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
onSettingsChange("grid", grid);
|
onSettingsChange({ grid });
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapURL = useDataURL(map, defaultMapSources);
|
const mapURL = useDataURL(map, defaultMapSources);
|
||||||
@ -124,7 +160,7 @@ function MapSettings({
|
|||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
let size = blob.size;
|
let size = blob.size;
|
||||||
size /= 1000000; // Bytes to Megabytes
|
size /= 1000000; // Bytes to Megabytes
|
||||||
setMapSize(size.toFixed(2));
|
setMapSize(parseFloat(size.toFixed(2)));
|
||||||
} else {
|
} else {
|
||||||
setMapSize(0);
|
setMapSize(0);
|
||||||
}
|
}
|
||||||
@ -168,7 +204,7 @@ function MapSettings({
|
|||||||
<Input
|
<Input
|
||||||
name="name"
|
name="name"
|
||||||
value={(map && map.name) || ""}
|
value={(map && map.name) || ""}
|
||||||
onChange={(e) => onSettingsChange("name", e.target.value)}
|
onChange={(e) => onSettingsChange({ name: e.target.value })}
|
||||||
disabled={mapEmpty}
|
disabled={mapEmpty}
|
||||||
my={1}
|
my={1}
|
||||||
/>
|
/>
|
||||||
@ -185,10 +221,11 @@ function MapSettings({
|
|||||||
isDisabled={mapEmpty}
|
isDisabled={mapEmpty}
|
||||||
options={gridTypeSettings}
|
options={gridTypeSettings}
|
||||||
value={
|
value={
|
||||||
!mapEmpty &&
|
mapEmpty
|
||||||
gridTypeSettings.find((s) => s.value === map.grid.type)
|
? undefined
|
||||||
|
: gridTypeSettings.find((s) => s.value === map.grid.type)
|
||||||
}
|
}
|
||||||
onChange={handleGridTypeChange}
|
onChange={handleGridTypeChange as any}
|
||||||
isSearchable={false}
|
isSearchable={false}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@ -197,7 +234,9 @@ function MapSettings({
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
checked={!mapEmpty && map.showGrid}
|
checked={!mapEmpty && map.showGrid}
|
||||||
disabled={mapEmpty}
|
disabled={mapEmpty}
|
||||||
onChange={(e) => onSettingsChange("showGrid", e.target.checked)}
|
onChange={(e) =>
|
||||||
|
onSettingsChange({ showGrid: e.target.checked })
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
Draw Grid
|
Draw Grid
|
||||||
</Label>
|
</Label>
|
||||||
@ -206,7 +245,7 @@ function MapSettings({
|
|||||||
checked={!mapEmpty && map.snapToGrid}
|
checked={!mapEmpty && map.snapToGrid}
|
||||||
disabled={mapEmpty}
|
disabled={mapEmpty}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
onSettingsChange("snapToGrid", e.target.checked)
|
onSettingsChange({ snapToGrid: e.target.checked })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
Snap to Grid
|
Snap to Grid
|
||||||
@ -224,12 +263,13 @@ function MapSettings({
|
|||||||
: gridHexMeasurementTypeSettings
|
: gridHexMeasurementTypeSettings
|
||||||
}
|
}
|
||||||
value={
|
value={
|
||||||
!mapEmpty &&
|
mapEmpty
|
||||||
gridSquareMeasurementTypeSettings.find(
|
? undefined
|
||||||
(s) => s.value === map.grid.measurement.type
|
: gridSquareMeasurementTypeSettings.find(
|
||||||
)
|
(s) => s.value === map.grid.measurement.type
|
||||||
|
)
|
||||||
}
|
}
|
||||||
onChange={handleGridMeasurementTypeChange}
|
onChange={handleGridMeasurementTypeChange as any}
|
||||||
isSearchable={false}
|
isSearchable={false}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@ -254,14 +294,17 @@ function MapSettings({
|
|||||||
<Select
|
<Select
|
||||||
options={qualitySettings}
|
options={qualitySettings}
|
||||||
value={
|
value={
|
||||||
!mapEmpty &&
|
mapEmpty
|
||||||
qualitySettings.find((s) => s.value === map.quality)
|
? undefined
|
||||||
|
: qualitySettings.find((s) => s.value === map.quality)
|
||||||
}
|
}
|
||||||
isDisabled={mapEmpty}
|
isDisabled={mapEmpty}
|
||||||
onChange={(option) => onSettingsChange("quality", option.value)}
|
onChange={handleQualityChange as any}
|
||||||
isOptionDisabled={(option) =>
|
isOptionDisabled={
|
||||||
mapEmpty ||
|
((option: QualityTypeSetting) =>
|
||||||
(option.value !== "original" && !map.resolutions[option.value])
|
mapEmpty ||
|
||||||
|
(option.value !== "original" &&
|
||||||
|
!map.resolutions[option.value])) as any
|
||||||
}
|
}
|
||||||
isSearchable={false}
|
isSearchable={false}
|
||||||
/>
|
/>
|
@ -1,8 +1,19 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Map } from "../../types/Map";
|
||||||
|
|
||||||
import Tile from "../tile/Tile";
|
import Tile from "../tile/Tile";
|
||||||
import MapImage from "./MapTileImage";
|
import MapImage from "./MapTileImage";
|
||||||
|
|
||||||
|
type MapTileProps = {
|
||||||
|
map: Map;
|
||||||
|
isSelected: boolean;
|
||||||
|
onSelect: (mapId: string) => void;
|
||||||
|
onEdit: (mapId: string) => void;
|
||||||
|
onDoubleClick: () => void;
|
||||||
|
canEdit: boolean;
|
||||||
|
badges: React.ReactChild[];
|
||||||
|
};
|
||||||
|
|
||||||
function MapTile({
|
function MapTile({
|
||||||
map,
|
map,
|
||||||
isSelected,
|
isSelected,
|
||||||
@ -11,7 +22,7 @@ function MapTile({
|
|||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
canEdit,
|
canEdit,
|
||||||
badges,
|
badges,
|
||||||
}) {
|
}: MapTileProps) {
|
||||||
return (
|
return (
|
||||||
<Tile
|
<Tile
|
||||||
title={map.name}
|
title={map.name}
|
@ -6,7 +6,24 @@ import MapImage from "./MapTileImage";
|
|||||||
|
|
||||||
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
||||||
|
|
||||||
function MapTileGroup({ group, maps, isSelected, onSelect, onDoubleClick }) {
|
import { Map } from "../../types/Map";
|
||||||
|
import { GroupContainer } from "../../types/Group";
|
||||||
|
|
||||||
|
type MapTileGroupProps = {
|
||||||
|
group: GroupContainer;
|
||||||
|
maps: Map[];
|
||||||
|
isSelected: boolean;
|
||||||
|
onSelect: (groupId: string) => void;
|
||||||
|
onDoubleClick: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
function MapTileGroup({
|
||||||
|
group,
|
||||||
|
maps,
|
||||||
|
isSelected,
|
||||||
|
onSelect,
|
||||||
|
onDoubleClick,
|
||||||
|
}: MapTileGroupProps) {
|
||||||
const layout = useResponsiveLayout();
|
const layout = useResponsiveLayout();
|
||||||
|
|
||||||
return (
|
return (
|
@ -10,11 +10,26 @@ import { getGroupItems } from "../../helpers/group";
|
|||||||
|
|
||||||
import { useGroup } from "../../contexts/GroupContext";
|
import { useGroup } from "../../contexts/GroupContext";
|
||||||
|
|
||||||
function MapTiles({ mapsById, onMapEdit, onMapSelect, subgroup }) {
|
import { Map } from "../../types/Map";
|
||||||
|
import { Group } from "../../types/Group";
|
||||||
|
|
||||||
|
type MapTileProps = {
|
||||||
|
mapsById: Record<string, Map>;
|
||||||
|
onMapEdit: (mapId: string) => void;
|
||||||
|
onMapSelect: (groupId: string) => void;
|
||||||
|
subgroup: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function MapTiles({
|
||||||
|
mapsById,
|
||||||
|
onMapEdit,
|
||||||
|
onMapSelect,
|
||||||
|
subgroup,
|
||||||
|
}: MapTileProps) {
|
||||||
const { selectedGroupIds, selectMode, onGroupOpen, onGroupSelect } =
|
const { selectedGroupIds, selectMode, onGroupOpen, onGroupSelect } =
|
||||||
useGroup();
|
useGroup();
|
||||||
|
|
||||||
function renderTile(group) {
|
function renderTile(group: Group) {
|
||||||
if (group.type === "item") {
|
if (group.type === "item") {
|
||||||
const map = mapsById[group.id];
|
const map = mapsById[group.id];
|
||||||
if (map) {
|
if (map) {
|
@ -31,7 +31,7 @@ import {
|
|||||||
TokenStateChangeEventHandler,
|
TokenStateChangeEventHandler,
|
||||||
} from "../../types/Events";
|
} from "../../types/Events";
|
||||||
|
|
||||||
type MapTokenStateProps = {
|
type MapTokenProps = {
|
||||||
tokenState: TokenState;
|
tokenState: TokenState;
|
||||||
onTokenStateChange: TokenStateChangeEventHandler;
|
onTokenStateChange: TokenStateChangeEventHandler;
|
||||||
onTokenMenuOpen: TokenMenuOpenChangeEventHandler;
|
onTokenMenuOpen: TokenMenuOpenChangeEventHandler;
|
||||||
@ -51,7 +51,7 @@ function MapToken({
|
|||||||
draggable,
|
draggable,
|
||||||
fadeOnHover,
|
fadeOnHover,
|
||||||
map,
|
map,
|
||||||
}: MapTokenStateProps) {
|
}: MapTokenProps) {
|
||||||
const userId = useUserId();
|
const userId = useUserId();
|
||||||
|
|
||||||
const mapWidth = useMapWidth();
|
const mapWidth = useMapWidth();
|
||||||
|
@ -1,19 +1,37 @@
|
|||||||
import React from "react";
|
|
||||||
import { Group } from "react-konva";
|
import { Group } from "react-konva";
|
||||||
|
import {
|
||||||
|
TokenMenuOpenChangeEventHandler,
|
||||||
|
TokenStateChangeEventHandler,
|
||||||
|
} from "../../types/Events";
|
||||||
|
import { Map, MapToolId } from "../../types/Map";
|
||||||
|
import { MapState } from "../../types/MapState";
|
||||||
|
import { TokenCategory, TokenDraggingOptions } from "../../types/Token";
|
||||||
|
import { TokenState } from "../../types/TokenState";
|
||||||
|
|
||||||
import MapToken from "./MapToken";
|
import MapToken from "./MapToken";
|
||||||
|
|
||||||
|
type MapTokensProps = {
|
||||||
|
map: Map;
|
||||||
|
mapState: MapState;
|
||||||
|
tokenDraggingOptions?: TokenDraggingOptions;
|
||||||
|
setTokenDraggingOptions: (options: TokenDraggingOptions) => void;
|
||||||
|
onMapTokenStateChange: TokenStateChangeEventHandler;
|
||||||
|
onTokenMenuOpen: TokenMenuOpenChangeEventHandler;
|
||||||
|
selectedToolId: MapToolId;
|
||||||
|
disabledTokens: string[];
|
||||||
|
};
|
||||||
|
|
||||||
function MapTokens({
|
function MapTokens({
|
||||||
map,
|
map,
|
||||||
mapState,
|
mapState,
|
||||||
tokenDraggingOptions,
|
tokenDraggingOptions,
|
||||||
setTokenDraggingOptions,
|
setTokenDraggingOptions,
|
||||||
onMapTokenStateChange,
|
onMapTokenStateChange,
|
||||||
handleTokenMenuOpen,
|
onTokenMenuOpen,
|
||||||
selectedToolId,
|
selectedToolId,
|
||||||
disabledTokens,
|
disabledTokens,
|
||||||
}) {
|
}: MapTokensProps) {
|
||||||
function getMapTokenCategoryWeight(category) {
|
function getMapTokenCategoryWeight(category: TokenCategory) {
|
||||||
switch (category) {
|
switch (category) {
|
||||||
case "character":
|
case "character":
|
||||||
return 0;
|
return 0;
|
||||||
@ -27,7 +45,11 @@ function MapTokens({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort so vehicles render below other tokens
|
// Sort so vehicles render below other tokens
|
||||||
function sortMapTokenStates(a, b, tokenDraggingOptions) {
|
function sortMapTokenStates(
|
||||||
|
a: TokenState,
|
||||||
|
b: TokenState,
|
||||||
|
tokenDraggingOptions?: TokenDraggingOptions
|
||||||
|
) {
|
||||||
// If categories are different sort in order "prop", "vehicle", "character"
|
// If categories are different sort in order "prop", "vehicle", "character"
|
||||||
if (b.category !== a.category) {
|
if (b.category !== a.category) {
|
||||||
const aWeight = getMapTokenCategoryWeight(a.category);
|
const aWeight = getMapTokenCategoryWeight(a.category);
|
||||||
@ -62,7 +84,7 @@ function MapTokens({
|
|||||||
key={tokenState.id}
|
key={tokenState.id}
|
||||||
tokenState={tokenState}
|
tokenState={tokenState}
|
||||||
onTokenStateChange={onMapTokenStateChange}
|
onTokenStateChange={onMapTokenStateChange}
|
||||||
onTokenMenuOpen={handleTokenMenuOpen}
|
onTokenMenuOpen={onTokenMenuOpen}
|
||||||
onTokenDragStart={(e) =>
|
onTokenDragStart={(e) =>
|
||||||
setTokenDraggingOptions({
|
setTokenDraggingOptions({
|
||||||
dragging: true,
|
dragging: true,
|
||||||
@ -71,6 +93,7 @@ function MapTokens({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
onTokenDragEnd={() =>
|
onTokenDragEnd={() =>
|
||||||
|
tokenDraggingOptions &&
|
||||||
setTokenDraggingOptions({
|
setTokenDraggingOptions({
|
||||||
...tokenDraggingOptions,
|
...tokenDraggingOptions,
|
||||||
dragging: false,
|
dragging: false,
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, useState, useRef } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
import { Rect, Text } from "react-konva";
|
import { Rect, Text } from "react-konva";
|
||||||
|
import Konva from "konva";
|
||||||
import { useSpring, animated } from "@react-spring/konva";
|
import { useSpring, animated } from "@react-spring/konva";
|
||||||
|
|
||||||
import { useUserId } from "../../contexts/UserIdContext";
|
import { useUserId } from "../../contexts/UserIdContext";
|
||||||
@ -15,8 +16,27 @@ import colors from "../../helpers/colors";
|
|||||||
import usePrevious from "../../hooks/usePrevious";
|
import usePrevious from "../../hooks/usePrevious";
|
||||||
import useGridSnapping from "../../hooks/useGridSnapping";
|
import useGridSnapping from "../../hooks/useGridSnapping";
|
||||||
|
|
||||||
|
import { Note as NoteType } from "../../types/Note";
|
||||||
|
import {
|
||||||
|
NoteChangeEventHandler,
|
||||||
|
NoteDragEventHandler,
|
||||||
|
NoteMenuOpenEventHandler,
|
||||||
|
} from "../../types/Events";
|
||||||
|
import { Map } from "../../types/Map";
|
||||||
|
|
||||||
const defaultFontSize = 16;
|
const defaultFontSize = 16;
|
||||||
|
|
||||||
|
type NoteProps = {
|
||||||
|
note: NoteType;
|
||||||
|
map: Map;
|
||||||
|
onNoteChange?: NoteChangeEventHandler;
|
||||||
|
onNoteMenuOpen?: NoteMenuOpenEventHandler;
|
||||||
|
draggable: boolean;
|
||||||
|
onNoteDragStart?: NoteDragEventHandler;
|
||||||
|
onNoteDragEnd?: NoteDragEventHandler;
|
||||||
|
fadeOnHover: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
function Note({
|
function Note({
|
||||||
note,
|
note,
|
||||||
map,
|
map,
|
||||||
@ -26,7 +46,7 @@ function Note({
|
|||||||
onNoteDragStart,
|
onNoteDragStart,
|
||||||
onNoteDragEnd,
|
onNoteDragEnd,
|
||||||
fadeOnHover,
|
fadeOnHover,
|
||||||
}) {
|
}: NoteProps) {
|
||||||
const userId = useUserId();
|
const userId = useUserId();
|
||||||
|
|
||||||
const mapWidth = useMapWidth();
|
const mapWidth = useMapWidth();
|
||||||
@ -45,11 +65,11 @@ function Note({
|
|||||||
|
|
||||||
const snapPositionToGrid = useGridSnapping();
|
const snapPositionToGrid = useGridSnapping();
|
||||||
|
|
||||||
function handleDragStart(event) {
|
function handleDragStart(event: Konva.KonvaEventObject<DragEvent>) {
|
||||||
onNoteDragStart && onNoteDragStart(event, note.id);
|
onNoteDragStart?.(event, note.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDragMove(event) {
|
function handleDragMove(event: Konva.KonvaEventObject<DragEvent>) {
|
||||||
const noteGroup = event.target;
|
const noteGroup = event.target;
|
||||||
// Snap to corners of grid
|
// Snap to corners of grid
|
||||||
if (map.snapToGrid) {
|
if (map.snapToGrid) {
|
||||||
@ -57,21 +77,20 @@ function Note({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDragEnd(event) {
|
function handleDragEnd(event: Konva.KonvaEventObject<DragEvent>) {
|
||||||
const noteGroup = event.target;
|
const noteGroup = event.target;
|
||||||
onNoteChange &&
|
onNoteChange?.({
|
||||||
onNoteChange({
|
...note,
|
||||||
...note,
|
x: noteGroup.x() / mapWidth,
|
||||||
x: noteGroup.x() / mapWidth,
|
y: noteGroup.y() / mapHeight,
|
||||||
y: noteGroup.y() / mapHeight,
|
lastModifiedBy: userId,
|
||||||
lastModifiedBy: userId,
|
lastModified: Date.now(),
|
||||||
lastModified: Date.now(),
|
});
|
||||||
});
|
onNoteDragEnd?.(event, note.id);
|
||||||
onNoteDragEnd && onNoteDragEnd(note.id);
|
|
||||||
setPreventMapInteraction(false);
|
setPreventMapInteraction(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClick(event) {
|
function handleClick(event: Konva.KonvaEventObject<MouseEvent>) {
|
||||||
if (draggable) {
|
if (draggable) {
|
||||||
const noteNode = event.target;
|
const noteNode = event.target;
|
||||||
onNoteMenuOpen && onNoteMenuOpen(note.id, noteNode);
|
onNoteMenuOpen && onNoteMenuOpen(note.id, noteNode);
|
||||||
@ -79,8 +98,8 @@ function Note({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store note pointer down time to check for a click when note is locked
|
// Store note pointer down time to check for a click when note is locked
|
||||||
const notePointerDownTimeRef = useRef();
|
const notePointerDownTimeRef = useRef<number>(0);
|
||||||
function handlePointerDown(event) {
|
function handlePointerDown(event: Konva.KonvaEventObject<PointerEvent>) {
|
||||||
if (draggable) {
|
if (draggable) {
|
||||||
setPreventMapInteraction(true);
|
setPreventMapInteraction(true);
|
||||||
}
|
}
|
||||||
@ -89,7 +108,7 @@ function Note({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePointerUp(event) {
|
function handlePointerUp(event: Konva.KonvaEventObject<PointerEvent>) {
|
||||||
if (draggable) {
|
if (draggable) {
|
||||||
setPreventMapInteraction(false);
|
setPreventMapInteraction(false);
|
||||||
}
|
}
|
||||||
@ -100,7 +119,7 @@ function Note({
|
|||||||
const delta = event.evt.timeStamp - notePointerDownTimeRef.current;
|
const delta = event.evt.timeStamp - notePointerDownTimeRef.current;
|
||||||
if (delta < 300) {
|
if (delta < 300) {
|
||||||
const noteNode = event.target;
|
const noteNode = event.target;
|
||||||
onNoteMenuOpen(note.id, noteNode);
|
onNoteMenuOpen?.(note.id, noteNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,12 +140,10 @@ function Note({
|
|||||||
const [fontScale, setFontScale] = useState(1);
|
const [fontScale, setFontScale] = useState(1);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const text = textRef.current;
|
const text = textRef.current;
|
||||||
|
|
||||||
if (!text) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findFontSize() {
|
function findFontSize() {
|
||||||
|
if (!text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Create an array from 1 / defaultFontSize of the note height to the full note height
|
// Create an array from 1 / defaultFontSize of the note height to the full note height
|
||||||
let sizes = Array.from(
|
let sizes = Array.from(
|
||||||
{ length: Math.ceil(noteHeight - notePadding * 2) },
|
{ length: Math.ceil(noteHeight - notePadding * 2) },
|
||||||
@ -151,7 +168,7 @@ function Note({
|
|||||||
findFontSize();
|
findFontSize();
|
||||||
}, [note, note.text, note.visible, noteWidth, noteHeight, notePadding]);
|
}, [note, note.text, note.visible, noteWidth, noteHeight, notePadding]);
|
||||||
|
|
||||||
const textRef = useRef();
|
const textRef = useRef<Konva.Text>(null);
|
||||||
|
|
||||||
// Animate to new note positions if edited by others
|
// Animate to new note positions if edited by others
|
||||||
const noteX = note.x * mapWidth;
|
const noteX = note.x * mapWidth;
|
||||||
@ -229,4 +246,9 @@ function Note({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Note.defaultProps = {
|
||||||
|
fadeOnHover: false,
|
||||||
|
draggable: false,
|
||||||
|
};
|
||||||
|
|
||||||
export default Note;
|
export default Note;
|
@ -1,19 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
import DragOverlay from "../map/DragOverlay";
|
|
||||||
|
|
||||||
function NoteDragOverlay({ onNoteRemove, noteId, noteGroup, dragging }) {
|
|
||||||
function handleNoteRemove() {
|
|
||||||
onNoteRemove(noteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DragOverlay
|
|
||||||
dragging={dragging}
|
|
||||||
onRemove={handleNoteRemove}
|
|
||||||
node={noteGroup}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NoteDragOverlay;
|
|
31
src/components/note/NoteDragOverlay.tsx
Normal file
31
src/components/note/NoteDragOverlay.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import Konva from "konva";
|
||||||
|
|
||||||
|
import DragOverlay from "../map/DragOverlay";
|
||||||
|
|
||||||
|
type NoteDragOverlayProps = {
|
||||||
|
onNoteRemove: (noteId: string) => void;
|
||||||
|
noteId: string;
|
||||||
|
noteGroup: Konva.Node;
|
||||||
|
dragging: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function NoteDragOverlay({
|
||||||
|
onNoteRemove,
|
||||||
|
noteId,
|
||||||
|
noteGroup,
|
||||||
|
dragging,
|
||||||
|
}: NoteDragOverlayProps) {
|
||||||
|
function handleNoteRemove() {
|
||||||
|
onNoteRemove(noteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DragOverlay
|
||||||
|
dragging={dragging}
|
||||||
|
onRemove={handleNoteRemove}
|
||||||
|
node={noteGroup}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NoteDragOverlay;
|
@ -1,12 +1,13 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Box, Flex, Text, IconButton } from "theme-ui";
|
import { Box, Flex, Text, IconButton } from "theme-ui";
|
||||||
|
import Konva from "konva";
|
||||||
|
|
||||||
import Slider from "../Slider";
|
import Slider from "../Slider";
|
||||||
import TextareaAutosize from "../TextareaAutoSize";
|
import TextareaAutosize from "../TextareaAutoSize";
|
||||||
|
|
||||||
import MapMenu from "../map/MapMenu";
|
import MapMenu from "../map/MapMenu";
|
||||||
|
|
||||||
import colors, { colorOptions } from "../../helpers/colors";
|
import colors, { Color, colorOptions } from "../../helpers/colors";
|
||||||
|
|
||||||
import usePrevious from "../../hooks/usePrevious";
|
import usePrevious from "../../hooks/usePrevious";
|
||||||
|
|
||||||
@ -19,8 +20,24 @@ import TextIcon from "../../icons/NoteTextIcon";
|
|||||||
|
|
||||||
import { useUserId } from "../../contexts/UserIdContext";
|
import { useUserId } from "../../contexts/UserIdContext";
|
||||||
|
|
||||||
|
import {
|
||||||
|
NoteChangeEventHandler,
|
||||||
|
RequestCloseEventHandler,
|
||||||
|
} from "../../types/Events";
|
||||||
|
import { Note } from "../../types/Note";
|
||||||
|
import { Map } from "../../types/Map";
|
||||||
|
|
||||||
const defaultNoteMaxSize = 6;
|
const defaultNoteMaxSize = 6;
|
||||||
|
|
||||||
|
type NoteMenuProps = {
|
||||||
|
isOpen: boolean;
|
||||||
|
onRequestClose: RequestCloseEventHandler;
|
||||||
|
note?: Note;
|
||||||
|
noteNode?: Konva.Node;
|
||||||
|
onNoteChange: NoteChangeEventHandler;
|
||||||
|
map: Map;
|
||||||
|
};
|
||||||
|
|
||||||
function NoteMenu({
|
function NoteMenu({
|
||||||
isOpen,
|
isOpen,
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
@ -28,7 +45,7 @@ function NoteMenu({
|
|||||||
noteNode,
|
noteNode,
|
||||||
onNoteChange,
|
onNoteChange,
|
||||||
map,
|
map,
|
||||||
}) {
|
}: NoteMenuProps) {
|
||||||
const userId = useUserId();
|
const userId = useUserId();
|
||||||
|
|
||||||
const wasOpen = usePrevious(isOpen);
|
const wasOpen = usePrevious(isOpen);
|
||||||
@ -43,29 +60,30 @@ function NoteMenu({
|
|||||||
if (noteNode) {
|
if (noteNode) {
|
||||||
const nodeRect = noteNode.getClientRect();
|
const nodeRect = noteNode.getClientRect();
|
||||||
const mapElement = document.querySelector(".map");
|
const mapElement = document.querySelector(".map");
|
||||||
const mapRect = mapElement.getBoundingClientRect();
|
if (mapElement) {
|
||||||
|
const mapRect = mapElement.getBoundingClientRect();
|
||||||
// Center X for the menu which is 156px wide
|
// Center X for the menu which is 156px wide
|
||||||
setMenuLeft(mapRect.left + nodeRect.x + nodeRect.width / 2 - 156 / 2);
|
setMenuLeft(mapRect.left + nodeRect.x + nodeRect.width / 2 - 156 / 2);
|
||||||
// Y 12px from the bottom
|
// Y 12px from the bottom
|
||||||
setMenuTop(mapRect.top + nodeRect.y + nodeRect.height + 12);
|
setMenuTop(mapRect.top + nodeRect.y + nodeRect.height + 12);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [isOpen, note, wasOpen, noteNode]);
|
}, [isOpen, note, wasOpen, noteNode]);
|
||||||
|
|
||||||
function handleTextChange(event) {
|
function handleTextChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
|
||||||
const text = event.target.value.substring(0, 1024);
|
const text = event.target.value.substring(0, 1024);
|
||||||
note && onNoteChange({ ...note, text: text });
|
note && onNoteChange({ ...note, text: text });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleColorChange(color) {
|
function handleColorChange(color: Color) {
|
||||||
if (!note) {
|
if (!note) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onNoteChange({ ...note, color: color });
|
onNoteChange({ ...note, color: color });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSizeChange(event) {
|
function handleSizeChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
const newSize = parseFloat(event.target.value);
|
const newSize = parseFloat(event.target.value);
|
||||||
note && onNoteChange({ ...note, size: newSize });
|
note && onNoteChange({ ...note, size: newSize });
|
||||||
}
|
}
|
||||||
@ -82,30 +100,35 @@ function NoteMenu({
|
|||||||
note && onNoteChange({ ...note, textOnly: !note.textOnly });
|
note && onNoteChange({ ...note, textOnly: !note.textOnly });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleModalContent(node) {
|
function handleModalContent(node: HTMLElement) {
|
||||||
if (node) {
|
if (node) {
|
||||||
// Focus input
|
// Focus input
|
||||||
const tokenLabelInput = node.querySelector("#changeNoteText");
|
const tokenLabelInput =
|
||||||
tokenLabelInput.focus();
|
node.querySelector<HTMLInputElement>("#changeNoteText");
|
||||||
tokenLabelInput.select();
|
if (tokenLabelInput) {
|
||||||
|
tokenLabelInput.focus();
|
||||||
|
tokenLabelInput.select();
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure menu is in bounds
|
// Ensure menu is in bounds
|
||||||
const nodeRect = node.getBoundingClientRect();
|
const nodeRect = node.getBoundingClientRect();
|
||||||
const mapElement = document.querySelector(".map");
|
const mapElement = document.querySelector(".map");
|
||||||
const mapRect = mapElement.getBoundingClientRect();
|
if (mapElement) {
|
||||||
setMenuLeft((prevLeft) =>
|
const mapRect = mapElement.getBoundingClientRect();
|
||||||
Math.min(
|
setMenuLeft((prevLeft) =>
|
||||||
mapRect.right - nodeRect.width,
|
Math.min(
|
||||||
Math.max(mapRect.left, prevLeft)
|
mapRect.right - nodeRect.width,
|
||||||
)
|
Math.max(mapRect.left, prevLeft)
|
||||||
);
|
)
|
||||||
setMenuTop((prevTop) =>
|
);
|
||||||
Math.min(mapRect.bottom - nodeRect.height, prevTop)
|
setMenuTop((prevTop) =>
|
||||||
);
|
Math.min(mapRect.bottom - nodeRect.height, prevTop)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTextKeyPress(e) {
|
function handleTextKeyPress(e: React.KeyboardEvent<HTMLTextAreaElement>) {
|
||||||
if (e.key === "Enter" && !e.shiftKey) {
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onRequestClose();
|
onRequestClose();
|
@ -23,33 +23,21 @@ import TokenBar from "../components/token/TokenBar";
|
|||||||
|
|
||||||
import GlobalImageDrop from "../components/image/GlobalImageDrop";
|
import GlobalImageDrop from "../components/image/GlobalImageDrop";
|
||||||
|
|
||||||
import { Map as MapType } from "../types/Map";
|
import {
|
||||||
|
Map as MapType,
|
||||||
|
MapActions,
|
||||||
|
MapActionsIndexKey,
|
||||||
|
MapActionsKey,
|
||||||
|
} from "../types/Map";
|
||||||
import { MapState } from "../types/MapState";
|
import { MapState } from "../types/MapState";
|
||||||
import {
|
import {
|
||||||
Asset,
|
|
||||||
AssetManifest,
|
AssetManifest,
|
||||||
AssetManifestAsset,
|
AssetManifestAsset,
|
||||||
AssetManifestAssets,
|
AssetManifestAssets,
|
||||||
} from "../types/Asset";
|
} from "../types/Asset";
|
||||||
import { TokenState } from "../types/TokenState";
|
import { TokenState } from "../types/TokenState";
|
||||||
import { Drawing, DrawingState } from "../types/Drawing";
|
import { DrawingState } from "../types/Drawing";
|
||||||
import { Fog, FogState } from "../types/Fog";
|
import { FogState } from "../types/Fog";
|
||||||
|
|
||||||
type MapActions = {
|
|
||||||
mapDrawActions: Action<Drawing>[];
|
|
||||||
mapDrawActionIndex: number;
|
|
||||||
fogDrawActions: Action<Fog>[];
|
|
||||||
fogDrawActionIndex: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type MapActionsKey = keyof Pick<
|
|
||||||
MapActions,
|
|
||||||
"mapDrawActions" | "fogDrawActions"
|
|
||||||
>;
|
|
||||||
type MapActionsIndexKey = keyof Pick<
|
|
||||||
MapActions,
|
|
||||||
"mapDrawActionIndex" | "fogDrawActionIndex"
|
|
||||||
>;
|
|
||||||
|
|
||||||
const defaultMapActions: MapActions = {
|
const defaultMapActions: MapActions = {
|
||||||
mapDrawActions: [],
|
mapDrawActions: [],
|
||||||
|
@ -2,22 +2,39 @@ import Konva from "konva";
|
|||||||
import { DefaultDice } from "./Dice";
|
import { DefaultDice } from "./Dice";
|
||||||
import { Map } from "./Map";
|
import { Map } from "./Map";
|
||||||
import { MapState } from "./MapState";
|
import { MapState } from "./MapState";
|
||||||
|
import { Note } from "./Note";
|
||||||
import { TokenState } from "./TokenState";
|
import { TokenState } from "./TokenState";
|
||||||
|
|
||||||
export type MapChangeEventHandler = (map?: Map, mapState?: MapState) => void;
|
export type MapChangeEventHandler = (map?: Map, mapState?: MapState) => void;
|
||||||
|
|
||||||
export type MapResetEventHandler = (newState: MapState) => void;
|
export type MapResetEventHandler = (newState: MapState) => void;
|
||||||
|
export type MapSettingsChangeEventHandler = (change: Partial<Map>) => void;
|
||||||
|
export type MapStateSettingsChangeEventHandler = (
|
||||||
|
change: Partial<MapState>
|
||||||
|
) => void;
|
||||||
|
|
||||||
export type DiceSelectEventHandler = (dice: DefaultDice) => void;
|
export type DiceSelectEventHandler = (dice: DefaultDice) => void;
|
||||||
|
|
||||||
export type RequestCloseEventHandler = () => void;
|
export type RequestCloseEventHandler = () => void;
|
||||||
|
|
||||||
export type MapTokensStateCreateHandler = (states: TokenState[]) => void;
|
export type MapTokensStateCreateHandler = (states: TokenState[]) => void;
|
||||||
|
export type MapTokenStateRemoveHandler = (state: TokenState) => void;
|
||||||
|
|
||||||
export type TokenStateChangeEventHandler = (
|
export type TokenStateChangeEventHandler = (
|
||||||
change: Partial<Record<string, Partial<TokenState>>>
|
changes: Record<string, Partial<TokenState>>
|
||||||
) => void;
|
) => void;
|
||||||
export type TokenMenuOpenChangeEventHandler = (
|
export type TokenMenuOpenChangeEventHandler = (
|
||||||
tokenStateId: string,
|
tokenStateId: string,
|
||||||
tokenImage: Konva.Node
|
tokenImage: Konva.Node
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
|
export type NoteAddEventHander = (note: Note) => void;
|
||||||
|
export type NoteRemoveEventHander = (noteId: string) => void;
|
||||||
|
export type NoteChangeEventHandler = (change: Partial<Note>) => void;
|
||||||
|
export type NoteMenuOpenEventHandler = (
|
||||||
|
noteId: string,
|
||||||
|
noteNode: Konva.Node
|
||||||
|
) => void;
|
||||||
|
export type NoteDragEventHandler = (
|
||||||
|
event: Konva.KonvaEventObject<DragEvent>,
|
||||||
|
noteId: string
|
||||||
|
) => void;
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import Action from "../actions/Action";
|
||||||
|
import { Drawing } from "./Drawing";
|
||||||
|
import { Fog } from "./Fog";
|
||||||
import { Grid } from "./Grid";
|
import { Grid } from "./Grid";
|
||||||
|
|
||||||
export type MapToolId =
|
export type MapToolId =
|
||||||
|
| "map"
|
||||||
| "move"
|
| "move"
|
||||||
| "fog"
|
| "fog"
|
||||||
| "drawing"
|
| "drawing"
|
||||||
@ -42,12 +46,30 @@ export type FileMapResolutions = {
|
|||||||
ultra?: string;
|
ultra?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MapQuality = keyof FileMapResolutions | "original";
|
||||||
|
|
||||||
export type FileMap = BaseMap & {
|
export type FileMap = BaseMap & {
|
||||||
type: "file";
|
type: "file";
|
||||||
file: string;
|
file: string;
|
||||||
resolutions: FileMapResolutions;
|
resolutions: FileMapResolutions;
|
||||||
thumbnail: string;
|
thumbnail: string;
|
||||||
quality: keyof FileMapResolutions | "original";
|
quality: MapQuality;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Map = DefaultMap | FileMap;
|
export type Map = DefaultMap | FileMap;
|
||||||
|
|
||||||
|
export type MapActions = {
|
||||||
|
mapDrawActions: Action<Drawing>[];
|
||||||
|
mapDrawActionIndex: number;
|
||||||
|
fogDrawActions: Action<Fog>[];
|
||||||
|
fogDrawActionIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MapActionsKey = keyof Pick<
|
||||||
|
MapActions,
|
||||||
|
"mapDrawActions" | "fogDrawActions"
|
||||||
|
>;
|
||||||
|
export type MapActionsIndexKey = keyof Pick<
|
||||||
|
MapActions,
|
||||||
|
"mapDrawActionIndex" | "fogDrawActionIndex"
|
||||||
|
>;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Konva from "konva";
|
||||||
import { Color } from "../helpers/colors";
|
import { Color } from "../helpers/colors";
|
||||||
|
|
||||||
export type Note = {
|
export type Note = {
|
||||||
@ -13,3 +14,14 @@ export type Note = {
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NoteMenuOptions = {
|
||||||
|
noteId: string;
|
||||||
|
noteNode: Konva.Node;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NoteDraggingOptions = {
|
||||||
|
dragging: boolean;
|
||||||
|
noteId: string;
|
||||||
|
noteGroup: Konva.Node;
|
||||||
|
};
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import Konva from "konva";
|
||||||
import { Outline } from "./Outline";
|
import { Outline } from "./Outline";
|
||||||
|
import { TokenState } from "./TokenState";
|
||||||
|
|
||||||
export type TokenCategory = "character" | "vehicle" | "prop";
|
export type TokenCategory = "character" | "vehicle" | "prop";
|
||||||
|
|
||||||
@ -29,3 +31,14 @@ export type FileToken = BaseToken & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Token = DefaultToken | FileToken;
|
export type Token = DefaultToken | FileToken;
|
||||||
|
|
||||||
|
export type TokenMenuOptions = {
|
||||||
|
tokenStateId: string;
|
||||||
|
tokenImage: Konva.Node;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TokenDraggingOptions = {
|
||||||
|
dragging: boolean;
|
||||||
|
tokenState: TokenState;
|
||||||
|
tokenGroup: Konva.Node;
|
||||||
|
};
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { Outline } from "./Outline";
|
import { Outline } from "./Outline";
|
||||||
|
import { TokenCategory } from "./Token";
|
||||||
|
|
||||||
export type BaseTokenState = {
|
export type BaseTokenState = {
|
||||||
id: string;
|
id: string;
|
||||||
tokenId: string;
|
tokenId: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
size: number;
|
size: number;
|
||||||
category: string;
|
category: TokenCategory;
|
||||||
label: string;
|
label: string;
|
||||||
statuses: string[];
|
statuses: string[];
|
||||||
x: number;
|
x: number;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user