Typescript

This commit is contained in:
Mitchell McCaffrey 2021-07-16 16:58:14 +10:00
parent d80bfa2f1e
commit c7b8990a7b
19 changed files with 97 additions and 74 deletions

View File

@ -102,6 +102,7 @@
"@types/lodash.get": "^4.4.6",
"@types/lodash.set": "^4.3.6",
"@types/node": "^15.6.0",
"@types/normalize-wheel": "^1.0.0",
"@types/react": "^17.0.6",
"@types/react-dom": "^17.0.5",
"@types/react-modal": "^3.12.0",

View File

@ -23,6 +23,8 @@ 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<Map>) => void;
@ -41,8 +43,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();
const mapLayerRef = useRef();
const mapStageRef = useRef<StageType>(null);
const mapLayerRef = useRef<LayerType>(null);
const [preventMapInteraction, setPreventMapInteraction] = useState(false);
function handleResize(width?: number, height?: number): void {
@ -55,7 +57,7 @@ function MapEditor({ map, onSettingsChange }: MapEditorProps) {
}
}
const containerRef = useRef(null);
const containerRef = useRef<HTMLDivElement>(null);
usePreventOverscroll(containerRef);
const [mapWidth, mapHeight] = useImageCenter(

View File

@ -309,7 +309,7 @@ type DefaultData = {
export function useDataURL(
data: FileData | DefaultData,
defaultSources: Record<string, string>,
unknownSource: string | undefined,
unknownSource: string | undefined = undefined,
thumbnail = false
) {
const [assetId, setAssetId] = useState<string>();

View File

@ -7,7 +7,7 @@ import { useKeyboard, useBlur } from "./KeyboardContext";
import { getGroupItems, groupsFromIds } from "../helpers/group";
import shortcuts from "../shortcuts";
import { Group, GroupContainer, GroupItem } from "../types/Group";
import { Group, GroupItem } from "../types/Group";
export type GroupSelectMode = "single" | "multiple" | "range";
export type GroupSelectModeChangeEventHandler = (

View File

@ -1,7 +1,7 @@
import React, { useContext } from "react";
import { Stage } from "konva/types/Stage";
type MapStage = React.MutableRefObject<Stage | null>;
export type MapStage = React.MutableRefObject<Stage | null>;
const MapStageContext = React.createContext<MapStage | undefined>(undefined);
export const MapStageProvider = MapStageContext.Provider;

View File

@ -1,26 +1,24 @@
import { Layer } from "konva/types/Layer";
import { useEffect, useRef } from "react";
type useImageCenterProps = {
data:
stageRef:
stageWidth: number;
stageHeight: number;
stageTranslateRef:
setStageScale:
imageLayerRef:
containerRef:
responsive?: boolean
}
import { MapStage } from "../contexts/MapStageContext";
import Vector2 from "../helpers/Vector2";
type ImageData = {
id: string;
width: number;
height: number;
};
function useImageCenter(
data,
stageRef,
stageWidth,
stageHeight,
stageTranslateRef,
setStageScale,
imageLayerRef,
containerRef,
data: ImageData,
stageRef: MapStage,
stageWidth: number,
stageHeight: number,
stageTranslateRef: React.MutableRefObject<Vector2>,
setStageScale: React.Dispatch<React.SetStateAction<number>>,
imageLayerRef: React.RefObject<Layer>,
containerRef: React.RefObject<HTMLDivElement>,
responsive = false
) {
const stageRatio = stageWidth / stageHeight;
@ -37,7 +35,7 @@ function useImageCenter(
}
// Reset image translate and stage scale
const previousDataIdRef = useRef();
const previousDataIdRef = useRef<string>();
const previousStageRatioRef = useRef(stageRatio);
useEffect(() => {
if (!data) {
@ -45,7 +43,12 @@ function useImageCenter(
}
const layer = imageLayerRef.current;
const containerRect = containerRef.current.getBoundingClientRect();
const container = containerRef.current;
const stage = stageRef.current;
if (!container || !stage) {
return;
}
const containerRect = container.getBoundingClientRect();
const previousDataId = previousDataIdRef.current;
const previousStageRatio = previousStageRatioRef.current;
@ -68,7 +71,7 @@ function useImageCenter(
};
}
layer.position(newTranslate);
stageRef.current.position({ x: 0, y: 0 });
stage.position({ x: 0, y: 0 });
stageTranslateRef.current = { x: 0, y: 0 };
setStageScale(1);

View File

@ -5,14 +5,16 @@ import { useDataURL } from "../contexts/AssetsContext";
import { mapSources as defaultMapSources } from "../maps";
function useMapImage(map) {
import { Map } from "../types/Map";
function useMapImage(map: Map) {
const mapURL = useDataURL(map, defaultMapSources);
const [mapImage, mapImageStatus] = useImage(mapURL);
const [mapImage, mapImageStatus] = useImage(mapURL || "");
// Create a map source that only updates when the image is fully loaded
const [loadedMapImage, setLoadedMapImage] = useState();
const [loadedMapImage, setLoadedMapImage] = useState<HTMLImageElement>();
useEffect(() => {
if (mapImageStatus === "loaded") {
if (mapImageStatus === "loaded" && mapImage) {
setLoadedMapImage(mapImage);
}
}, [mapImage, mapImageStatus]);

View File

@ -6,7 +6,7 @@ import { useRef, useEffect } from "react";
* Creates DOM element to be used as React root.
* @returns {HTMLElement}
*/
function createRootElement(id) {
function createRootElement(id: string) {
const rootContainer = document.createElement("div");
rootContainer.setAttribute("id", id);
return rootContainer;
@ -16,11 +16,13 @@ function createRootElement(id) {
* Appends element as last child of body.
* @param {HTMLElement} rootElem
*/
function addRootElement(rootElem) {
document.body.insertBefore(
rootElem,
document.body.lastElementChild.nextElementSibling
);
function addRootElement(rootElem: HTMLElement) {
if (document.body.lastElementChild) {
document.body.insertBefore(
rootElem,
document.body.lastElementChild?.nextElementSibling
);
}
}
/**
@ -34,13 +36,15 @@ function addRootElement(rootElem) {
* @param {String} id The id of the target container, e.g 'modal' or 'spotlight'
* @returns {HTMLElement} The DOM node to use as the Portal target.
*/
function usePortal(id) {
const rootElemRef = useRef(null);
function usePortal(id: string): HTMLElement {
const rootElemRef = useRef<HTMLElement | null>(null);
useEffect(
function setupElement() {
// Look for existing target dom element to append to
const existingParent = document.querySelector(`#${id}`);
const existingParent: HTMLElement | null = document.querySelector(
`#${id}`
);
// Parent is either a new root or the existing dom element
const parentElem = existingParent || createRootElement(id);
@ -50,10 +54,10 @@ function usePortal(id) {
}
// Add the detached element to the parent
parentElem.appendChild(rootElemRef.current);
rootElemRef.current && parentElem.appendChild(rootElemRef.current);
return function removeElement() {
rootElemRef.current.remove();
rootElemRef.current && rootElemRef.current.remove();
if (parentElem.childNodes.length === -1) {
parentElem.remove();
}

View File

@ -1,10 +1,10 @@
import { useEffect } from "react";
import React, { useEffect } from "react";
function usePreventOverscroll(elementRef) {
function usePreventOverscroll(elementRef: React.RefObject<HTMLElement>) {
useEffect(() => {
// Stop overscroll on chrome and safari
// also stop pinch to zoom on chrome
function preventOverscroll(event) {
function preventOverscroll(event: WheelEvent) {
event.preventDefault();
}
const element = elementRef.current;

View File

@ -1,11 +1,8 @@
function usePreventSelect() {
function clearSelection() {
if (window.getSelection) {
window.getSelection().removeAllRanges();
}
if (document.selection) {
document.selection.empty();
}
window?.getSelection()?.removeAllRanges();
// @ts-ignore
document?.selection?.empty();
}
function preventSelect() {
clearSelection();

View File

@ -1,9 +1,9 @@
import { useEffect } from "react";
function usePreventTouch(elementRef) {
function usePreventTouch(elementRef: React.RefObject<HTMLElement>) {
useEffect(() => {
// Stop 3d touch
function prevent3DTouch(event) {
function prevent3DTouch(event: TouchEvent) {
event.preventDefault();
}
const element = elementRef.current;

View File

@ -1,7 +1,7 @@
import { useEffect, useRef } from "react";
function usePrevious(value) {
const ref = useRef();
function usePrevious<T>(value: T): T {
const ref = useRef<T>(value);
useEffect(() => {
ref.current = value;
}, [value]);

View File

@ -1,35 +1,42 @@
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 { Stage } from "konva/types/Stage";
import { Layer } from "konva/types/Layer";
import { useKeyboard, useBlur } from "../contexts/KeyboardContext";
import shortcuts from "../shortcuts";
import Vector2 from "../helpers/Vector2";
const wheelZoomSpeed = -1;
const touchZoomSpeed = 0.005;
const minZoom = 0.1;
type StageScaleChangeEventHandler = (newScale: number) => void;
function useStageInteraction(
stage,
stageScale,
onStageScaleChange,
stageTranslateRef,
layer,
stage: Stage,
stageScale: number,
onStageScaleChange: StageScaleChangeEventHandler,
stageTranslateRef: React.MutableRefObject<Vector2>,
layer: Layer,
maxZoom = 10,
tool = "move",
preventInteraction = false,
gesture = {}
gesture: Handlers = {}
) {
const isInteractingWithCanvas = useRef(false);
const pinchPreviousDistanceRef = useRef();
const pinchPreviousOriginRef = useRef();
const pinchPreviousDistanceRef = useRef<number>(0);
const pinchPreviousOriginRef = useRef<Vector2>({ x: 0, y: 0 });
const [zoomSpeed, setZoomSpeed] = useState(1);
// Prevent accessibility pinch to zoom on Mac
useEffect(() => {
function handleGesture(e) {
function handleGesture(e: Event) {
e.preventDefault();
}
window.addEventListener("gesturestart", handleGesture);
@ -69,16 +76,18 @@ function useStageInteraction(
// Center on pointer
const pointer = stage.getPointerPosition();
const newTranslate = {
x: pointer.x - ((pointer.x - stage.x()) / stageScale) * newScale,
y: pointer.y - ((pointer.y - stage.y()) / stageScale) * newScale,
};
if (pointer) {
const newTranslate = {
x: pointer.x - ((pointer.x - stage.x()) / stageScale) * newScale,
y: pointer.y - ((pointer.y - stage.y()) / stageScale) * newScale,
};
stage.position(newTranslate);
stage.position(newTranslate);
stageTranslateRef.current = newTranslate;
stageTranslateRef.current = newTranslate;
onStageScaleChange(newScale);
onStageScaleChange(newScale);
}
}
gesture.onWheel && gesture.onWheel(props);
@ -186,7 +195,7 @@ function useStageInteraction(
}
);
function handleKeyDown(event) {
function handleKeyDown(event: KeyboardEvent) {
// TODO: Find better way to detect whether keyboard event should fire.
// This one fires on all open stages
if (preventInteraction) {
@ -222,7 +231,7 @@ function useStageInteraction(
}
}
function handleKeyUp(event) {
function handleKeyUp(event: KeyboardEvent) {
if (shortcuts.stagePrecisionZoom(event)) {
setZoomSpeed(1);
}

View File

@ -3038,6 +3038,11 @@
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==
"@types/normalize-wheel@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/normalize-wheel/-/normalize-wheel-1.0.0.tgz#d973b53557dc59c6136b5b737ae930e9218cb452"
integrity sha512-SzWYVzP7Q8w4/976Gi3a6+J/8/VNTq6AW7wDafXorr1MYTxyZqJTbUvwQt1hiG3PXyFUMIKr+s6q3+MLz2c/TQ==
"@types/offscreencanvas@~2019.3.0":
version "2019.3.0"
resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz#3336428ec7e9180cf4566dfea5da04eb586a6553"