diff --git a/src/components/map/DragOverlay.tsx b/src/components/map/DragOverlay.tsx index 641fb43..cd831b6 100644 --- a/src/components/map/DragOverlay.tsx +++ b/src/components/map/DragOverlay.tsx @@ -1,12 +1,12 @@ import { useEffect, useRef, useState } from "react"; import { Box, IconButton } from "theme-ui"; -import { Node } from "konva/types/Node"; +import Konva from "konva"; import RemoveTokenIcon from "../../icons/RemoveTokenIcon"; type DragOverlayProps = { dragging: boolean; - node: Node; + node: Konva.Node; onRemove: () => void; }; diff --git a/src/components/map/MapEditor.tsx b/src/components/map/MapEditor.tsx index 140bb8f..167b8fc 100644 --- a/src/components/map/MapEditor.tsx +++ b/src/components/map/MapEditor.tsx @@ -1,6 +1,7 @@ import React, { useState, useRef } from "react"; import { Box, IconButton } from "theme-ui"; import { Stage, Layer, Image } from "react-konva"; +import Konva from "konva"; import ReactResizeDetector from "react-resize-detector"; import useMapImage from "../../hooks/useMapImage"; @@ -23,8 +24,6 @@ import MapGrid from "./MapGrid"; import MapGridEditor from "./MapGridEditor"; import { Map } from "../../types/Map"; import { GridInset } from "../../types/Grid"; -import { Stage as StageType } from "konva/types/Stage"; -import { Layer as LayerType } from "konva/types/Layer"; type MapSettingsChangeEventHandler = (change: Partial) => void; @@ -43,8 +42,8 @@ function MapEditor({ map, onSettingsChange }: MapEditorProps) { const defaultInset = getGridDefaultInset(map.grid, map.width, map.height); const stageTranslateRef = useRef({ x: 0, y: 0 }); - const mapStageRef = useRef(null); - const mapLayerRef = useRef(null); + const mapStageRef = useRef(null); + const mapLayerRef = useRef(null); const [preventMapInteraction, setPreventMapInteraction] = useState(false); function handleResize(width?: number, height?: number): void { diff --git a/src/components/map/MapGridEditor.tsx b/src/components/map/MapGridEditor.tsx index 75254e9..0475566 100644 --- a/src/components/map/MapGridEditor.tsx +++ b/src/components/map/MapGridEditor.tsx @@ -1,6 +1,6 @@ import { useRef } from "react"; import { Group, Circle, Rect } from "react-konva"; -import { KonvaEventObject, Node } from "konva/types/Node"; +import Konva from "konva"; import { useDebouncedStageScale, @@ -49,19 +49,23 @@ function MapGridEditor({ map, onGridChange }: MapGridEditorProps) { const handlePreviousPositionRef = useRef(); - function handleScaleCircleDragStart(event: KonvaEventObject) { + function handleScaleCircleDragStart( + event: Konva.KonvaEventObject + ) { const handle = event.target; const position = getHandleNormalizedPosition(handle); handlePreviousPositionRef.current = position; } - function handleScaleCircleDragMove(event: KonvaEventObject) { + function handleScaleCircleDragMove( + event: Konva.KonvaEventObject + ) { const handle = event.target; onGridChange(getHandleInset(handle)); handlePreviousPositionRef.current = getHandleNormalizedPosition(handle); } - function handleScaleCircleDragEnd(event: KonvaEventObject) { + function handleScaleCircleDragEnd(event: Konva.KonvaEventObject) { onGridChange(getHandleInset(event.target)); setPreventMapInteraction(false); } @@ -74,7 +78,7 @@ function MapGridEditor({ map, onGridChange }: MapGridEditorProps) { setPreventMapInteraction(false); } - function getHandleInset(handle: Node): GridInset { + function getHandleInset(handle: Konva.Node): GridInset { const name = handle.name(); // Find distance and direction of dragging @@ -202,7 +206,7 @@ function MapGridEditor({ map, onGridChange }: MapGridEditorProps) { useKeyboard(handleKeyDown); - function getHandleNormalizedPosition(handle: Node) { + function getHandleNormalizedPosition(handle: Konva.Node) { return Vector2.divide({ x: handle.x(), y: handle.y() }, mapSize); } diff --git a/src/components/map/MapInteraction.tsx b/src/components/map/MapInteraction.tsx index 202445b..1a778dd 100644 --- a/src/components/map/MapInteraction.tsx +++ b/src/components/map/MapInteraction.tsx @@ -2,6 +2,7 @@ import React, { useRef, useEffect, useState } from "react"; import { Box } from "theme-ui"; import ReactResizeDetector from "react-resize-detector"; import { Stage, Layer, Image } from "react-konva"; +import Konva from "konva"; import { EventEmitter } from "events"; import useMapImage from "../../hooks/useMapImage"; @@ -18,8 +19,6 @@ import { GridProvider } from "../../contexts/GridContext"; import { useKeyboard } from "../../contexts/KeyboardContext"; import shortcuts from "../../shortcuts"; -import { Layer as LayerType } from "konva/types/Layer"; -import { Image as ImageType } from "konva/types/shapes/Image"; import { Map, MapToolId } from "../../types/Map"; import { MapState } from "../../types/MapState"; @@ -63,8 +62,8 @@ function MapInteraction({ // Avoid state udpates when panning the map by using a ref and updating the konva element directly const stageTranslateRef = useRef({ x: 0, y: 0 }); const mapStageRef = useMapStage(); - const mapLayerRef = useRef(null); - const mapImageRef = useRef(null); + const mapLayerRef = useRef(null); + const mapImageRef = useRef(null); function handleResize(width?: number, height?: number) { if (width && height && width > 0 && height > 0) { @@ -223,7 +222,7 @@ function MapInteraction({ > ) => void; + onTokenDragEnd: (event: Konva.KonvaEventObject) => void; + draggable: boolean; + fadeOnHover: boolean; + map: Map; +}; function MapToken({ tokenState, @@ -33,7 +51,7 @@ function MapToken({ draggable, fadeOnHover, map, -}) { +}: MapTokenStateProps) { const userId = useUserId(); const mapWidth = useMapWidth(); @@ -43,16 +61,16 @@ function MapToken({ const gridCellPixelSize = useGridCellPixelSize(); const tokenURL = useDataURL(tokenState, tokenSources); - const [tokenImage] = useImage(tokenURL); + const [tokenImage] = useImage(tokenURL || ""); const tokenAspectRatio = tokenState.width / tokenState.height; const snapPositionToGrid = useGridSnapping(); - const intersectingTokensRef = useRef([]); + const intersectingTokensRef = useRef([]); const previousDragPositionRef = useRef({ x: 0, y: 0 }); - function handleDragStart(event) { + function handleDragStart(event: Konva.KonvaEventObject) { const tokenGroup = event.target; if (tokenState.category === "vehicle") { @@ -65,7 +83,7 @@ function MapToken({ ); // Find all other tokens on the map - const layer = tokenGroup.getLayer(); + const layer = tokenGroup.getLayer() as Konva.Layer; const tokens = layer.find(".character"); for (let other of tokens) { if (other === tokenGroup) { @@ -80,7 +98,7 @@ function MapToken({ onTokenDragStart(event); } - function handleDragMove(event) { + function handleDragMove(event: Konva.KonvaEventObject) { const tokenGroup = event.target; // Snap to corners of grid if (map.snapToGrid) { @@ -98,10 +116,10 @@ function MapToken({ } } - function handleDragEnd(event) { + function handleDragEnd(event: Konva.KonvaEventObject) { const tokenGroup = event.target; - const mountChanges = {}; + const mountChanges: Record> = {}; if (tokenState.category === "vehicle") { for (let other of intersectingTokensRef.current) { mountChanges[other.id()] = { @@ -127,7 +145,7 @@ function MapToken({ onTokenDragEnd(event); } - function handleClick(event) { + function handleClick(event: Konva.KonvaEventObject) { if (draggable) { const tokenImage = event.target; onTokenMenuOpen(tokenState.id, tokenImage); @@ -136,8 +154,8 @@ function MapToken({ const [tokenOpacity, setTokenOpacity] = useState(1); // Store token pointer down time to check for a click when token is locked - const tokenPointerDownTimeRef = useRef(); - function handlePointerDown(event) { + const tokenPointerDownTimeRef = useRef(0); + function handlePointerDown(event: Konva.KonvaEventObject) { if (draggable) { setPreventMapInteraction(true); } @@ -146,7 +164,7 @@ function MapToken({ } } - function handlePointerUp(event) { + function handlePointerUp(event: Konva.KonvaEventObject) { if (draggable) { setPreventMapInteraction(false); } diff --git a/src/contexts/AssetsContext.tsx b/src/contexts/AssetsContext.tsx index 1b4fea4..bd0c6d9 100644 --- a/src/contexts/AssetsContext.tsx +++ b/src/contexts/AssetsContext.tsx @@ -293,7 +293,7 @@ export function useAssetURL( type FileData = { file: string; type: "file"; - thumbnail: string; + thumbnail?: string; quality?: string; resolutions?: Record; }; diff --git a/src/contexts/MapStageContext.tsx b/src/contexts/MapStageContext.tsx index 81893dd..468b6a6 100644 --- a/src/contexts/MapStageContext.tsx +++ b/src/contexts/MapStageContext.tsx @@ -1,7 +1,7 @@ import React, { useContext } from "react"; -import { Stage } from "konva/types/Stage"; +import Konva from "konva"; -export type MapStage = React.MutableRefObject; +export type MapStage = React.MutableRefObject; const MapStageContext = React.createContext(undefined); export const MapStageProvider = MapStageContext.Provider; diff --git a/src/helpers/konva.tsx b/src/helpers/konva.tsx index 776d4a6..b9ef535 100644 --- a/src/helpers/konva.tsx +++ b/src/helpers/konva.tsx @@ -1,14 +1,13 @@ import React, { useState, useEffect, useRef } from "react"; import Konva from "konva"; import { Line, Group, Path, Circle } from "react-konva"; -import { LineConfig } from "konva/types/shapes/Line"; import Color from "color"; import Vector2 from "./Vector2"; type HoleyLineProps = { holes: number[][]; -} & LineConfig; +} & Konva.LineConfig; // Holes should be wound in the opposite direction as the containing points array export function HoleyLine({ holes, ...props }: HoleyLineProps) { @@ -115,7 +114,7 @@ export function HoleyLine({ holes, ...props }: HoleyLineProps) { } } - return ; + return ; } type TickProps = { @@ -180,11 +179,11 @@ export function Trail({ segments, color, }: TrailProps) { - const trailRef: React.MutableRefObject = useRef(); - const pointsRef: React.MutableRefObject = useRef([]); + const trailRef = useRef(null); + const pointsRef = useRef([]); const prevPositionRef = useRef(position); const positionRef = useRef(position); - const circleRef: React.MutableRefObject = useRef(); + const circleRef = useRef(null); // Color of the end of the trail const transparentColorRef = useRef( Color(color).lighten(0.5).alpha(0).string() @@ -250,7 +249,7 @@ export function Trail({ }, []); // Custom scene function for drawing a trail from a line - function sceneFunc(context: CanvasRenderingContext2D) { + function sceneFunc(context: Konva.Context) { // Resample points to ensure a smooth trail const resampledPoints = Vector2.resample(pointsRef.current, segments); if (resampledPoints.length === 0) { @@ -302,6 +301,7 @@ export function Trail({ ); gradient.addColorStop(0, color); gradient.addColorStop(1, transparentColorRef.current); + // @ts-ignore context.fillStyle = gradient; context.fill(); } diff --git a/src/helpers/token.ts b/src/helpers/token.ts index cf941b5..f225837 100644 --- a/src/helpers/token.ts +++ b/src/helpers/token.ts @@ -1,6 +1,6 @@ import { v4 as uuid } from "uuid"; import Case from "case"; -import { Stage } from "konva/types/Stage"; +import Konva from "konva"; import blobToBuffer from "./blobToBuffer"; import { createThumbnail, getImageOutline } from "./image"; @@ -143,7 +143,7 @@ export async function createTokenFromFile( } export function clientPositionToMapPosition( - mapStage: Stage, + mapStage: Konva.Stage, clientPosition: Vector2, checkMapBounds = true ): Vector2 | undefined { diff --git a/src/hooks/useImageCenter.ts b/src/hooks/useImageCenter.ts index 9830ca6..acc1a58 100644 --- a/src/hooks/useImageCenter.ts +++ b/src/hooks/useImageCenter.ts @@ -1,4 +1,4 @@ -import { Layer } from "konva/types/Layer"; +import Konva from "konva"; import { useEffect, useRef } from "react"; import { MapStage } from "../contexts/MapStageContext"; @@ -17,7 +17,7 @@ function useImageCenter( stageHeight: number, stageTranslateRef: React.MutableRefObject, setStageScale: React.Dispatch>, - imageLayerRef: React.RefObject, + imageLayerRef: React.RefObject, containerRef: React.RefObject, responsive = false ) { diff --git a/src/hooks/useMapImage.ts b/src/hooks/useMapImage.ts index 89b7720..de2309e 100644 --- a/src/hooks/useMapImage.ts +++ b/src/hooks/useMapImage.ts @@ -7,7 +7,9 @@ import { mapSources as defaultMapSources } from "../maps"; import { Map } from "../types/Map"; -function useMapImage(map: Map) { +function useMapImage( + map: Map +): [HTMLImageElement | undefined, "loaded" | "loading" | "failed"] { const mapURL = useDataURL(map, defaultMapSources); const [mapImage, mapImageStatus] = useImage(mapURL || ""); diff --git a/src/hooks/useStageInteraction.ts b/src/hooks/useStageInteraction.ts index 4ffe169..88ad511 100644 --- a/src/hooks/useStageInteraction.ts +++ b/src/hooks/useStageInteraction.ts @@ -2,7 +2,7 @@ import { useRef, useEffect, useState } from "react"; import { useGesture } from "react-use-gesture"; import { Handlers } from "react-use-gesture/dist/types"; import normalizeWheel from "normalize-wheel"; -import { Layer } from "konva/types/Layer"; +import Konva from "konva"; import { useKeyboard, useBlur } from "../contexts/KeyboardContext"; import { MapStage } from "../contexts/MapStageContext"; @@ -22,7 +22,7 @@ function useStageInteraction( stageScale: number, onStageScaleChange: StageScaleChangeEventHandler, stageTranslateRef: React.MutableRefObject, - layerRef: React.RefObject, + layerRef: React.RefObject, maxZoom = 10, tool = "move", preventInteraction = false, diff --git a/src/routes/Game.tsx b/src/routes/Game.tsx index 0c02432..09115e9 100644 --- a/src/routes/Game.tsx +++ b/src/routes/Game.tsx @@ -1,6 +1,7 @@ -import React, { useState, useEffect, useRef } from "react"; +import { useState, useEffect, useRef } from "react"; import { Flex, Box, Text } from "theme-ui"; import { useParams } from "react-router-dom"; +import Konva from "konva"; import Banner from "../components/banner/Banner"; import ReconnectBanner from "../components/banner/ReconnectBanner"; @@ -28,7 +29,6 @@ import NetworkedMapAndTokens from "../network/NetworkedMapAndTokens"; import NetworkedParty from "../network/NetworkedParty"; import Session from "../network/Session"; -import { Stage } from "konva/types/Stage"; function Game() { const { id: gameId }: { id: string } = useParams(); @@ -108,7 +108,7 @@ function Game() { // A ref to the Konva stage // the ref will be assigned in the MapInteraction component - const mapStageRef = useRef(null); + const mapStageRef = useRef(null); return ( diff --git a/src/types/Events.ts b/src/types/Events.ts index ee99c72..47d212e 100644 --- a/src/types/Events.ts +++ b/src/types/Events.ts @@ -1,3 +1,4 @@ +import Konva from "konva"; import { DefaultDice } from "./Dice"; import { Map } from "./Map"; import { MapState } from "./MapState"; @@ -12,3 +13,11 @@ export type DiceSelectEventHandler = (dice: DefaultDice) => void; export type RequestCloseEventHandler = () => void; export type MapTokensStateCreateHandler = (states: TokenState[]) => void; + +export type TokenStateChangeEventHandler = ( + change: Partial>> +) => void; +export type TokenMenuOpenChangeEventHandler = ( + tokenStateId: string, + tokenImage: Konva.Node +) => void;