Typescript
This commit is contained in:
parent
d80bfa2f1e
commit
c7b8990a7b
|
@ -102,6 +102,7 @@
|
||||||
"@types/lodash.get": "^4.4.6",
|
"@types/lodash.get": "^4.4.6",
|
||||||
"@types/lodash.set": "^4.3.6",
|
"@types/lodash.set": "^4.3.6",
|
||||||
"@types/node": "^15.6.0",
|
"@types/node": "^15.6.0",
|
||||||
|
"@types/normalize-wheel": "^1.0.0",
|
||||||
"@types/react": "^17.0.6",
|
"@types/react": "^17.0.6",
|
||||||
"@types/react-dom": "^17.0.5",
|
"@types/react-dom": "^17.0.5",
|
||||||
"@types/react-modal": "^3.12.0",
|
"@types/react-modal": "^3.12.0",
|
||||||
|
|
|
@ -23,6 +23,8 @@ 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 { Stage as StageType } from "konva/types/Stage";
|
||||||
|
import { Layer as LayerType } from "konva/types/Layer";
|
||||||
|
|
||||||
type MapSettingsChangeEventHandler = (change: Partial<Map>) => void;
|
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 defaultInset = getGridDefaultInset(map.grid, map.width, map.height);
|
||||||
|
|
||||||
const stageTranslateRef = useRef({ x: 0, y: 0 });
|
const stageTranslateRef = useRef({ x: 0, y: 0 });
|
||||||
const mapStageRef = useRef();
|
const mapStageRef = useRef<StageType>(null);
|
||||||
const mapLayerRef = useRef();
|
const mapLayerRef = useRef<LayerType>(null);
|
||||||
const [preventMapInteraction, setPreventMapInteraction] = useState(false);
|
const [preventMapInteraction, setPreventMapInteraction] = useState(false);
|
||||||
|
|
||||||
function handleResize(width?: number, height?: number): void {
|
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);
|
usePreventOverscroll(containerRef);
|
||||||
|
|
||||||
const [mapWidth, mapHeight] = useImageCenter(
|
const [mapWidth, mapHeight] = useImageCenter(
|
||||||
|
|
|
@ -309,7 +309,7 @@ type DefaultData = {
|
||||||
export function useDataURL(
|
export function useDataURL(
|
||||||
data: FileData | DefaultData,
|
data: FileData | DefaultData,
|
||||||
defaultSources: Record<string, string>,
|
defaultSources: Record<string, string>,
|
||||||
unknownSource: string | undefined,
|
unknownSource: string | undefined = undefined,
|
||||||
thumbnail = false
|
thumbnail = false
|
||||||
) {
|
) {
|
||||||
const [assetId, setAssetId] = useState<string>();
|
const [assetId, setAssetId] = useState<string>();
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { useKeyboard, useBlur } from "./KeyboardContext";
|
||||||
import { getGroupItems, groupsFromIds } from "../helpers/group";
|
import { getGroupItems, groupsFromIds } from "../helpers/group";
|
||||||
|
|
||||||
import shortcuts from "../shortcuts";
|
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 GroupSelectMode = "single" | "multiple" | "range";
|
||||||
export type GroupSelectModeChangeEventHandler = (
|
export type GroupSelectModeChangeEventHandler = (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import { Stage } from "konva/types/Stage";
|
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);
|
const MapStageContext = React.createContext<MapStage | undefined>(undefined);
|
||||||
export const MapStageProvider = MapStageContext.Provider;
|
export const MapStageProvider = MapStageContext.Provider;
|
||||||
|
|
|
@ -1,26 +1,24 @@
|
||||||
|
import { Layer } from "konva/types/Layer";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
type useImageCenterProps = {
|
import { MapStage } from "../contexts/MapStageContext";
|
||||||
data:
|
import Vector2 from "../helpers/Vector2";
|
||||||
stageRef:
|
|
||||||
stageWidth: number;
|
type ImageData = {
|
||||||
stageHeight: number;
|
id: string;
|
||||||
stageTranslateRef:
|
width: number;
|
||||||
setStageScale:
|
height: number;
|
||||||
imageLayerRef:
|
};
|
||||||
containerRef:
|
|
||||||
responsive?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
function useImageCenter(
|
function useImageCenter(
|
||||||
data,
|
data: ImageData,
|
||||||
stageRef,
|
stageRef: MapStage,
|
||||||
stageWidth,
|
stageWidth: number,
|
||||||
stageHeight,
|
stageHeight: number,
|
||||||
stageTranslateRef,
|
stageTranslateRef: React.MutableRefObject<Vector2>,
|
||||||
setStageScale,
|
setStageScale: React.Dispatch<React.SetStateAction<number>>,
|
||||||
imageLayerRef,
|
imageLayerRef: React.RefObject<Layer>,
|
||||||
containerRef,
|
containerRef: React.RefObject<HTMLDivElement>,
|
||||||
responsive = false
|
responsive = false
|
||||||
) {
|
) {
|
||||||
const stageRatio = stageWidth / stageHeight;
|
const stageRatio = stageWidth / stageHeight;
|
||||||
|
@ -37,7 +35,7 @@ function useImageCenter(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset image translate and stage scale
|
// Reset image translate and stage scale
|
||||||
const previousDataIdRef = useRef();
|
const previousDataIdRef = useRef<string>();
|
||||||
const previousStageRatioRef = useRef(stageRatio);
|
const previousStageRatioRef = useRef(stageRatio);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
@ -45,7 +43,12 @@ function useImageCenter(
|
||||||
}
|
}
|
||||||
|
|
||||||
const layer = imageLayerRef.current;
|
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 previousDataId = previousDataIdRef.current;
|
||||||
const previousStageRatio = previousStageRatioRef.current;
|
const previousStageRatio = previousStageRatioRef.current;
|
||||||
|
|
||||||
|
@ -68,7 +71,7 @@ function useImageCenter(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
layer.position(newTranslate);
|
layer.position(newTranslate);
|
||||||
stageRef.current.position({ x: 0, y: 0 });
|
stage.position({ x: 0, y: 0 });
|
||||||
stageTranslateRef.current = { x: 0, y: 0 };
|
stageTranslateRef.current = { x: 0, y: 0 };
|
||||||
|
|
||||||
setStageScale(1);
|
setStageScale(1);
|
|
@ -5,14 +5,16 @@ import { useDataURL } from "../contexts/AssetsContext";
|
||||||
|
|
||||||
import { mapSources as defaultMapSources } from "../maps";
|
import { mapSources as defaultMapSources } from "../maps";
|
||||||
|
|
||||||
function useMapImage(map) {
|
import { Map } from "../types/Map";
|
||||||
|
|
||||||
|
function useMapImage(map: Map) {
|
||||||
const mapURL = useDataURL(map, defaultMapSources);
|
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
|
// Create a map source that only updates when the image is fully loaded
|
||||||
const [loadedMapImage, setLoadedMapImage] = useState();
|
const [loadedMapImage, setLoadedMapImage] = useState<HTMLImageElement>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mapImageStatus === "loaded") {
|
if (mapImageStatus === "loaded" && mapImage) {
|
||||||
setLoadedMapImage(mapImage);
|
setLoadedMapImage(mapImage);
|
||||||
}
|
}
|
||||||
}, [mapImage, mapImageStatus]);
|
}, [mapImage, mapImageStatus]);
|
|
@ -6,7 +6,7 @@ import { useRef, useEffect } from "react";
|
||||||
* Creates DOM element to be used as React root.
|
* Creates DOM element to be used as React root.
|
||||||
* @returns {HTMLElement}
|
* @returns {HTMLElement}
|
||||||
*/
|
*/
|
||||||
function createRootElement(id) {
|
function createRootElement(id: string) {
|
||||||
const rootContainer = document.createElement("div");
|
const rootContainer = document.createElement("div");
|
||||||
rootContainer.setAttribute("id", id);
|
rootContainer.setAttribute("id", id);
|
||||||
return rootContainer;
|
return rootContainer;
|
||||||
|
@ -16,11 +16,13 @@ function createRootElement(id) {
|
||||||
* Appends element as last child of body.
|
* Appends element as last child of body.
|
||||||
* @param {HTMLElement} rootElem
|
* @param {HTMLElement} rootElem
|
||||||
*/
|
*/
|
||||||
function addRootElement(rootElem) {
|
function addRootElement(rootElem: HTMLElement) {
|
||||||
document.body.insertBefore(
|
if (document.body.lastElementChild) {
|
||||||
rootElem,
|
document.body.insertBefore(
|
||||||
document.body.lastElementChild.nextElementSibling
|
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'
|
* @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.
|
* @returns {HTMLElement} The DOM node to use as the Portal target.
|
||||||
*/
|
*/
|
||||||
function usePortal(id) {
|
function usePortal(id: string): HTMLElement {
|
||||||
const rootElemRef = useRef(null);
|
const rootElemRef = useRef<HTMLElement | null>(null);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
function setupElement() {
|
function setupElement() {
|
||||||
// Look for existing target dom element to append to
|
// 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
|
// Parent is either a new root or the existing dom element
|
||||||
const parentElem = existingParent || createRootElement(id);
|
const parentElem = existingParent || createRootElement(id);
|
||||||
|
|
||||||
|
@ -50,10 +54,10 @@ function usePortal(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the detached element to the parent
|
// Add the detached element to the parent
|
||||||
parentElem.appendChild(rootElemRef.current);
|
rootElemRef.current && parentElem.appendChild(rootElemRef.current);
|
||||||
|
|
||||||
return function removeElement() {
|
return function removeElement() {
|
||||||
rootElemRef.current.remove();
|
rootElemRef.current && rootElemRef.current.remove();
|
||||||
if (parentElem.childNodes.length === -1) {
|
if (parentElem.childNodes.length === -1) {
|
||||||
parentElem.remove();
|
parentElem.remove();
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
import { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
function usePreventOverscroll(elementRef) {
|
function usePreventOverscroll(elementRef: React.RefObject<HTMLElement>) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Stop overscroll on chrome and safari
|
// Stop overscroll on chrome and safari
|
||||||
// also stop pinch to zoom on chrome
|
// also stop pinch to zoom on chrome
|
||||||
function preventOverscroll(event) {
|
function preventOverscroll(event: WheelEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
const element = elementRef.current;
|
const element = elementRef.current;
|
|
@ -1,11 +1,8 @@
|
||||||
function usePreventSelect() {
|
function usePreventSelect() {
|
||||||
function clearSelection() {
|
function clearSelection() {
|
||||||
if (window.getSelection) {
|
window?.getSelection()?.removeAllRanges();
|
||||||
window.getSelection().removeAllRanges();
|
// @ts-ignore
|
||||||
}
|
document?.selection?.empty();
|
||||||
if (document.selection) {
|
|
||||||
document.selection.empty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
function preventSelect() {
|
function preventSelect() {
|
||||||
clearSelection();
|
clearSelection();
|
|
@ -1,9 +1,9 @@
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
function usePreventTouch(elementRef) {
|
function usePreventTouch(elementRef: React.RefObject<HTMLElement>) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Stop 3d touch
|
// Stop 3d touch
|
||||||
function prevent3DTouch(event) {
|
function prevent3DTouch(event: TouchEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
const element = elementRef.current;
|
const element = elementRef.current;
|
|
@ -1,7 +1,7 @@
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
function usePrevious(value) {
|
function usePrevious<T>(value: T): T {
|
||||||
const ref = useRef();
|
const ref = useRef<T>(value);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current = value;
|
ref.current = value;
|
||||||
}, [value]);
|
}, [value]);
|
|
@ -1,35 +1,42 @@
|
||||||
import { useRef, useEffect, useState } from "react";
|
import { useRef, useEffect, useState } from "react";
|
||||||
import { useGesture } from "react-use-gesture";
|
import { useGesture } from "react-use-gesture";
|
||||||
|
import { Handlers } from "react-use-gesture/dist/types";
|
||||||
import normalizeWheel from "normalize-wheel";
|
import normalizeWheel from "normalize-wheel";
|
||||||
|
import { Stage } from "konva/types/Stage";
|
||||||
|
import { Layer } from "konva/types/Layer";
|
||||||
|
|
||||||
import { useKeyboard, useBlur } from "../contexts/KeyboardContext";
|
import { useKeyboard, useBlur } from "../contexts/KeyboardContext";
|
||||||
|
|
||||||
import shortcuts from "../shortcuts";
|
import shortcuts from "../shortcuts";
|
||||||
|
|
||||||
|
import Vector2 from "../helpers/Vector2";
|
||||||
|
|
||||||
const wheelZoomSpeed = -1;
|
const wheelZoomSpeed = -1;
|
||||||
const touchZoomSpeed = 0.005;
|
const touchZoomSpeed = 0.005;
|
||||||
const minZoom = 0.1;
|
const minZoom = 0.1;
|
||||||
|
|
||||||
|
type StageScaleChangeEventHandler = (newScale: number) => void;
|
||||||
|
|
||||||
function useStageInteraction(
|
function useStageInteraction(
|
||||||
stage,
|
stage: Stage,
|
||||||
stageScale,
|
stageScale: number,
|
||||||
onStageScaleChange,
|
onStageScaleChange: StageScaleChangeEventHandler,
|
||||||
stageTranslateRef,
|
stageTranslateRef: React.MutableRefObject<Vector2>,
|
||||||
layer,
|
layer: Layer,
|
||||||
maxZoom = 10,
|
maxZoom = 10,
|
||||||
tool = "move",
|
tool = "move",
|
||||||
preventInteraction = false,
|
preventInteraction = false,
|
||||||
gesture = {}
|
gesture: Handlers = {}
|
||||||
) {
|
) {
|
||||||
const isInteractingWithCanvas = useRef(false);
|
const isInteractingWithCanvas = useRef(false);
|
||||||
const pinchPreviousDistanceRef = useRef();
|
const pinchPreviousDistanceRef = useRef<number>(0);
|
||||||
const pinchPreviousOriginRef = useRef();
|
const pinchPreviousOriginRef = useRef<Vector2>({ x: 0, y: 0 });
|
||||||
|
|
||||||
const [zoomSpeed, setZoomSpeed] = useState(1);
|
const [zoomSpeed, setZoomSpeed] = useState(1);
|
||||||
|
|
||||||
// Prevent accessibility pinch to zoom on Mac
|
// Prevent accessibility pinch to zoom on Mac
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleGesture(e) {
|
function handleGesture(e: Event) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
window.addEventListener("gesturestart", handleGesture);
|
window.addEventListener("gesturestart", handleGesture);
|
||||||
|
@ -69,16 +76,18 @@ function useStageInteraction(
|
||||||
|
|
||||||
// Center on pointer
|
// Center on pointer
|
||||||
const pointer = stage.getPointerPosition();
|
const pointer = stage.getPointerPosition();
|
||||||
const newTranslate = {
|
if (pointer) {
|
||||||
x: pointer.x - ((pointer.x - stage.x()) / stageScale) * newScale,
|
const newTranslate = {
|
||||||
y: pointer.y - ((pointer.y - stage.y()) / stageScale) * newScale,
|
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);
|
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.
|
// TODO: Find better way to detect whether keyboard event should fire.
|
||||||
// This one fires on all open stages
|
// This one fires on all open stages
|
||||||
if (preventInteraction) {
|
if (preventInteraction) {
|
||||||
|
@ -222,7 +231,7 @@ function useStageInteraction(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyUp(event) {
|
function handleKeyUp(event: KeyboardEvent) {
|
||||||
if (shortcuts.stagePrecisionZoom(event)) {
|
if (shortcuts.stagePrecisionZoom(event)) {
|
||||||
setZoomSpeed(1);
|
setZoomSpeed(1);
|
||||||
}
|
}
|
|
@ -3038,6 +3038,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
|
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
|
||||||
integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==
|
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":
|
"@types/offscreencanvas@~2019.3.0":
|
||||||
version "2019.3.0"
|
version "2019.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz#3336428ec7e9180cf4566dfea5da04eb586a6553"
|
resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz#3336428ec7e9180cf4566dfea5da04eb586a6553"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user