diff --git a/package.json b/package.json
index 546abb7..56f83e2 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
"dependencies": {
"@babylonjs/core": "^4.2.0",
"@babylonjs/loaders": "^4.2.0",
+ "@dnd-kit/core": "3.0.0",
"@mitchemmc/dexie-export-import": "^1.0.1",
"@msgpack/msgpack": "^2.4.1",
"@sentry/react": "^6.2.2",
@@ -25,7 +26,6 @@
"file-saver": "^2.0.5",
"fuse.js": "^6.4.6",
"image-outline": "^0.1.0",
- "interactjs": "^1.10.8",
"konva": "^7.2.5",
"lodash.clonedeep": "^4.5.0",
"lodash.get": "^4.4.2",
diff --git a/src/App.js b/src/App.js
index 7962f7b..ee51563 100644
--- a/src/App.js
+++ b/src/App.js
@@ -13,12 +13,8 @@ import Donate from "./routes/Donate";
import { AuthProvider } from "./contexts/AuthContext";
import { DatabaseProvider } from "./contexts/DatabaseContext";
-import { MapDataProvider } from "./contexts/MapDataContext";
-import { TokenDataProvider } from "./contexts/TokenDataContext";
-import { MapLoadingProvider } from "./contexts/MapLoadingContext";
import { SettingsProvider } from "./contexts/SettingsContext";
import { KeyboardProvider } from "./contexts/KeyboardContext";
-import { AssetsProvider, AssetURLsProvider } from "./contexts/AssetsContext";
import { ToastProvider } from "./components/Toast";
@@ -49,17 +45,7 @@ function App() {
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/src/components/Draggable.js b/src/components/Draggable.js
new file mode 100644
index 0000000..f007fea
--- /dev/null
+++ b/src/components/Draggable.js
@@ -0,0 +1,27 @@
+import React from "react";
+import { useDraggable } from "@dnd-kit/core";
+
+function Draggable({ id, children, data }) {
+ const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
+ id,
+ data,
+ });
+
+ const style = {
+ border: "none",
+ background: "transparent",
+ margin: "0px",
+ padding: "0px",
+ cursor: "pointer",
+ touchAction: "none",
+ opacity: isDragging ? 0.5 : undefined,
+ };
+
+ return (
+
+ );
+}
+
+export default Draggable;
diff --git a/src/components/Droppable.js b/src/components/Droppable.js
new file mode 100644
index 0000000..c51c25e
--- /dev/null
+++ b/src/components/Droppable.js
@@ -0,0 +1,18 @@
+import React from "react";
+import { useDroppable } from "@dnd-kit/core";
+
+function Droppable({ id, children, disabled }) {
+ const { setNodeRef } = useDroppable({ id, disabled });
+
+ return (
+
+ {children}
+
+ );
+}
+
+Droppable.defaultProps = {
+ disabled: false,
+};
+
+export default Droppable;
diff --git a/src/components/DragOverlay.js b/src/components/map/DragOverlay.js
similarity index 97%
rename from src/components/DragOverlay.js
rename to src/components/map/DragOverlay.js
index a89f02d..57f140e 100644
--- a/src/components/DragOverlay.js
+++ b/src/components/map/DragOverlay.js
@@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from "react";
import { Box, IconButton } from "theme-ui";
-import RemoveTokenIcon from "../icons/RemoveTokenIcon";
+import RemoveTokenIcon from "../../icons/RemoveTokenIcon";
function DragOverlay({ dragging, node, onRemove }) {
const [isRemoveHovered, setIsRemoveHovered] = useState(false);
diff --git a/src/components/map/Map.js b/src/components/map/Map.js
index 340749e..ba0df02 100644
--- a/src/components/map/Map.js
+++ b/src/components/map/Map.js
@@ -1,4 +1,5 @@
import React, { useState } from "react";
+import { Box } from "theme-ui";
import MapControls from "./MapControls";
import MapInteraction from "./MapInteraction";
@@ -18,6 +19,8 @@ import TokenDragOverlay from "../token/TokenDragOverlay";
import NoteMenu from "../note/NoteMenu";
import NoteDragOverlay from "../note/NoteDragOverlay";
+import Droppable from "../Droppable";
+
import {
AddShapeAction,
CutShapeAction,
@@ -336,30 +339,34 @@ function Map({
);
return (
-
- {mapControls}
- {tokenMenu}
- {noteMenu}
- {tokenDragOverlay}
- {noteDragOverlay}
- >
- }
- selectedToolId={selectedToolId}
- onSelectedToolChange={setSelectedToolId}
- disabledControls={disabledControls}
- >
- {mapGrid}
- {mapDrawing}
- {mapNotes}
- {mapTokens}
- {mapFog}
- {mapPointer}
- {mapMeasure}
-
+
+
+
+ {mapControls}
+ {tokenMenu}
+ {noteMenu}
+ {tokenDragOverlay}
+ {noteDragOverlay}
+ >
+ }
+ selectedToolId={selectedToolId}
+ onSelectedToolChange={setSelectedToolId}
+ disabledControls={disabledControls}
+ >
+ {mapGrid}
+ {mapDrawing}
+ {mapNotes}
+ {mapTokens}
+ {mapFog}
+ {mapPointer}
+ {mapMeasure}
+
+
+
);
}
diff --git a/src/components/map/MapInteraction.js b/src/components/map/MapInteraction.js
index fc08eff..61ba335 100644
--- a/src/components/map/MapInteraction.js
+++ b/src/components/map/MapInteraction.js
@@ -181,11 +181,12 @@ function MapInteraction({
+
diff --git a/src/components/token/ProxyToken.js b/src/components/token/ProxyToken.js
deleted file mode 100644
index 989e8fe..0000000
--- a/src/components/token/ProxyToken.js
+++ /dev/null
@@ -1,172 +0,0 @@
-import React, { useEffect, useRef, useState } from "react";
-import ReactDOM from "react-dom";
-import { Image, Box } from "theme-ui";
-import interact from "interactjs";
-
-import usePortal from "../../hooks/usePortal";
-
-import { useMapStage } from "../../contexts/MapStageContext";
-
-/**
- * @callback onProxyDragEnd
- * @param {boolean} isOnMap whether the token was dropped on the map
- * @param {Object} token the token that was dropped
- */
-
-/**
- *
- * @param {string} tokenClassName The class name to attach the interactjs handler to
- * @param {onProxyDragEnd} onProxyDragEnd Called when the proxy token is dropped
- * @param {Object} tokens An optional mapping of tokens to use as a base when calling OnProxyDragEnd
-
- */
-function ProxyToken({ tokenClassName, onProxyDragEnd, tokens }) {
- const proxyContainer = usePortal("root");
-
- const [imageSource, setImageSource] = useState("");
- const proxyRef = useRef();
-
- // Store the tokens in a ref and access in the interactjs loop
- // This is needed to stop interactjs from creating multiple listeners
- const tokensRef = useRef(tokens);
- useEffect(() => {
- tokensRef.current = tokens;
- }, [tokens]);
-
- const proxyOnMap = useRef(false);
- const mapStageRef = useMapStage();
-
- useEffect(() => {
- interact(`.${tokenClassName}`).draggable({
- listeners: {
- start: (event) => {
- let target = event.target;
-
- // Hide the token and copy it's image to the proxy
- target.parentElement.style.opacity = "0.25";
- setImageSource(target.src);
-
- let proxy = proxyRef.current;
- if (proxy) {
- // Find and set the initial offset of the token to the proxy
- const proxyRect = proxy.getBoundingClientRect();
- const targetRect = target.getBoundingClientRect();
- const xOffset = targetRect.left - proxyRect.left;
- const yOffset = targetRect.top - proxyRect.top;
- proxy.style.transform = `translate(${xOffset}px, ${yOffset}px)`;
- proxy.setAttribute("data-x", xOffset);
- proxy.setAttribute("data-y", yOffset);
-
- // Copy width and height of target
- proxy.style.width = `${targetRect.width}px`;
- proxy.style.height = `${targetRect.height}px`;
- }
- },
-
- move: (event) => {
- let proxy = proxyRef.current;
- // Move the proxy based off of the movment of the token
- if (proxy) {
- // keep the dragged position in the data-x/data-y attributes
- const x =
- (parseFloat(proxy.getAttribute("data-x")) || 0) + event.dx;
- const y =
- (parseFloat(proxy.getAttribute("data-y")) || 0) + event.dy;
- proxy.style.transform = `translate(${x}px, ${y}px)`;
-
- // Check whether the proxy is on the right or left hand side of the screen
- // if not set proxyOnMap to true
- const proxyRect = proxy.getBoundingClientRect();
- const map = document.querySelector(".map");
- const mapRect = map.getBoundingClientRect();
- proxyOnMap.current =
- proxyRect.left > mapRect.left && proxyRect.right < mapRect.right;
-
- // update the posiion attributes
- proxy.setAttribute("data-x", x);
- proxy.setAttribute("data-y", y);
- }
- },
-
- end: (event) => {
- let target = event.target;
- const id = target.dataset.id;
- let proxy = proxyRef.current;
- if (proxy) {
- const mapStage = mapStageRef.current;
- if (onProxyDragEnd && mapStage) {
- const mapImage = mapStage.findOne("#mapImage");
- const map = document.querySelector(".map");
- const mapRect = map.getBoundingClientRect();
- const position = {
- x: event.clientX - mapRect.left,
- y: event.clientY - mapRect.top,
- };
- const transform = mapImage.getAbsoluteTransform().copy().invert();
- const relativePosition = transform.point(position);
- const normalizedPosition = {
- x: relativePosition.x / mapImage.width(),
- y: relativePosition.y / mapImage.height(),
- };
- // Get the token from the supplied tokens if it exists
- const token = tokensRef.current[id] || {};
- onProxyDragEnd(proxyOnMap.current, {
- ...token,
- x: normalizedPosition.x,
- y: normalizedPosition.y,
- });
- }
-
- // Reset the proxy position
- proxy.style.transform = "translate(0px, 0px)";
- proxy.setAttribute("data-x", 0);
- proxy.setAttribute("data-y", 0);
- }
-
- // Show the token
- target.parentElement.style.opacity = "1";
- setImageSource("");
- },
- },
- });
- }, [onProxyDragEnd, tokenClassName, proxyContainer, mapStageRef]);
-
- if (!imageSource) {
- return null;
- }
-
- // Create a portal to allow the proxy to move past the bounds of the token
- return ReactDOM.createPortal(
-
-
-
-
- ,
- proxyContainer
- );
-}
-
-ProxyToken.defaultProps = {
- tokens: {},
-};
-
-export default ProxyToken;
diff --git a/src/components/token/TokenBar.js b/src/components/token/TokenBar.js
index dfc9273..ec01f07 100644
--- a/src/components/token/TokenBar.js
+++ b/src/components/token/TokenBar.js
@@ -1,98 +1,77 @@
import React from "react";
+import { createPortal } from "react-dom";
import { Box, Flex } from "theme-ui";
-import shortid from "shortid";
import SimpleBar from "simplebar-react";
+import { DragOverlay } from "@dnd-kit/core";
import ListToken from "./ListToken";
-import ProxyToken from "./ProxyToken";
-
import SelectTokensButton from "./SelectTokensButton";
-
-import { fromEntries } from "../../helpers/shared";
+import Draggable from "../Draggable";
import useSetting from "../../hooks/useSetting";
-import { useAuth } from "../../contexts/AuthContext";
import { useTokenData } from "../../contexts/TokenDataContext";
+import { useDragId } from "../../contexts/DragContext";
-const listTokenClassName = "list-token";
-
-function TokenBar({ onMapTokenStateCreate }) {
- const { userId } = useAuth();
- const { ownedTokens, tokens } = useTokenData();
+function TokenBar() {
+ const { ownedTokens } = useTokenData();
const [fullScreen] = useSetting("map.fullScreen");
- function handleProxyDragEnd(isOnMap, token) {
- if (isOnMap && onMapTokenStateCreate) {
- // Create a token state from the dragged token
- let tokenState = {
- id: shortid.generate(),
- tokenId: token.id,
- owner: userId,
- size: token.defaultSize,
- category: token.defaultCategory,
- label: token.defaultLabel,
- statuses: [],
- x: token.x,
- y: token.y,
- lastModifiedBy: userId,
- lastModified: Date.now(),
- rotation: 0,
- locked: false,
- visible: true,
- type: token.type,
- outline: token.outline,
- width: token.width,
- height: token.height,
- };
- if (token.type === "file") {
- tokenState.file = token.file;
- } else if (token.type === "default") {
- tokenState.key = token.key;
- }
- onMapTokenStateCreate(tokenState);
- }
- }
+ const activeDragId = useDragId();
return (
- <>
-
+
-
- {ownedTokens
- .filter((token) => !token.hideInSidebar)
- .map((token) => (
-
- ))}
-
-
-
-
-
- [token.id, token]))}
- />
- >
+ {ownedTokens
+ .filter((token) => !token.hideInSidebar)
+ .map((token) => (
+
+ ))}
+
+
+
+
+ {createPortal(
+
+ {activeDragId && (
+ `sidebar-${token.id}` === activeDragId
+ )}
+ />
+ )}
+ ,
+ document.body
+ )}
+
);
}
diff --git a/src/components/token/TokenDragOverlay.js b/src/components/token/TokenDragOverlay.js
index d5d9348..731ae16 100644
--- a/src/components/token/TokenDragOverlay.js
+++ b/src/components/token/TokenDragOverlay.js
@@ -6,7 +6,7 @@ import {
useMapHeight,
} from "../../contexts/MapInteractionContext";
-import DragOverlay from "../DragOverlay";
+import DragOverlay from "../map/DragOverlay";
function TokenDragOverlay({
onTokenStateRemove,
diff --git a/src/contexts/DragContext.js b/src/contexts/DragContext.js
new file mode 100644
index 0000000..faf7d59
--- /dev/null
+++ b/src/contexts/DragContext.js
@@ -0,0 +1,36 @@
+import React, { useState, useContext } from "react";
+import { DndContext } from "@dnd-kit/core";
+
+/**
+ * @type {React.Context}
+ */
+const DragIdContext = React.createContext();
+
+export function DragProvider({ children, onDragEnd }) {
+ const [activeDragId, setActiveDragId] = useState(null);
+
+ function handleDragStart({ active }) {
+ setActiveDragId(active.id);
+ }
+
+ function handleDragEnd(event) {
+ setActiveDragId(null);
+ onDragEnd && onDragEnd(event);
+ }
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+export function useDragId() {
+ const context = useContext(DragIdContext);
+ if (context === undefined) {
+ throw new Error("useDragId must be used within a DragProvider");
+ }
+ return context;
+}
diff --git a/src/network/NetworkedMapAndTokens.js b/src/network/NetworkedMapAndTokens.js
index 6517d25..37035f6 100644
--- a/src/network/NetworkedMapAndTokens.js
+++ b/src/network/NetworkedMapAndTokens.js
@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from "react";
import { useToasts } from "react-toast-notifications";
+import { v4 as uuid } from "uuid";
import { useMapData } from "../contexts/MapDataContext";
import { useMapLoading } from "../contexts/MapLoadingContext";
@@ -7,6 +8,9 @@ import { useAuth } from "../contexts/AuthContext";
import { useDatabase } from "../contexts/DatabaseContext";
import { useParty } from "../contexts/PartyContext";
import { useAssets } from "../contexts/AssetsContext";
+import { DragProvider } from "../contexts/DragContext";
+import { useTokenData } from "../contexts/TokenDataContext";
+import { useMapStage } from "../contexts/MapStageContext";
import { omit } from "../helpers/shared";
@@ -40,8 +44,10 @@ function NetworkedMapAndTokens({ session }) {
const { userId } = useAuth();
const partyState = useParty();
const { assetLoadStart, assetProgressUpdate, isLoading } = useMapLoading();
+ const mapStageRef = useMapStage();
const { updateMapState } = useMapData();
+ const { tokensById } = useTokenData();
const { getAsset, putAsset } = useAssets();
const [currentMap, setCurrentMap] = useState(null);
@@ -379,6 +385,53 @@ function NetworkedMapAndTokens({ session }) {
});
}
+ function handleDragEnd({ active, over }) {
+ const tokenId = active?.data?.current?.tokenId;
+ const token = tokensById[tokenId];
+ const mapStage = mapStageRef.current;
+ if (over?.id === "map" && tokenId && token && mapStage) {
+ const mapImage = mapStage.findOne("#mapImage");
+ // TODO: Get proper pointer position when dnd-kit is updated
+ // https://github.com/clauderic/dnd-kit/issues/238
+ const pointerPosition = {
+ x: over.rect.width / 2,
+ y: over.rect.height / 2,
+ };
+ const transform = mapImage.getAbsoluteTransform().copy().invert();
+ const relativePosition = transform.point(pointerPosition);
+ const normalizedPosition = {
+ x: relativePosition.x / mapImage.width(),
+ y: relativePosition.y / mapImage.height(),
+ };
+ let tokenState = {
+ id: uuid(),
+ tokenId: token.id,
+ owner: userId,
+ size: token.defaultSize,
+ category: token.defaultCategory,
+ label: token.defaultLabel,
+ statuses: [],
+ x: normalizedPosition.x,
+ y: normalizedPosition.y,
+ lastModifiedBy: userId,
+ lastModified: Date.now(),
+ rotation: 0,
+ locked: false,
+ visible: true,
+ type: token.type,
+ outline: token.outline,
+ width: token.width,
+ height: token.height,
+ };
+ if (token.type === "file") {
+ tokenState.file = token.file;
+ } else if (token.type === "default") {
+ tokenState.key = token.key;
+ }
+ handleMapTokenStateCreate(tokenState);
+ }
+ }
+
useEffect(() => {
async function handlePeerData({ id, data, reply }) {
if (id === "assetRequest") {
@@ -451,7 +504,7 @@ function NetworkedMapAndTokens({ session }) {
}
return (
- <>
+
-
- >
+
+
);
}
diff --git a/src/routes/Game.js b/src/routes/Game.js
index 190fb86..aa324c2 100644
--- a/src/routes/Game.js
+++ b/src/routes/Game.js
@@ -18,6 +18,10 @@ import { MapStageProvider } from "../contexts/MapStageContext";
import { useDatabase } from "../contexts/DatabaseContext";
import { PlayerProvider } from "../contexts/PlayerContext";
import { PartyProvider } from "../contexts/PartyContext";
+import { AssetsProvider, AssetURLsProvider } from "../contexts/AssetsContext";
+import { MapDataProvider } from "../contexts/MapDataContext";
+import { TokenDataProvider } from "../contexts/TokenDataContext";
+import { MapLoadingProvider } from "../contexts/MapLoadingContext";
import NetworkedMapAndTokens from "../network/NetworkedMapAndTokens";
import NetworkedParty from "../network/NetworkedParty";
@@ -84,7 +88,6 @@ function Game() {
};
}, [session]);
-
// Join game
useEffect(() => {
if (sessionStatus === "ready" && databaseStatus !== "loading") {
@@ -103,50 +106,62 @@ function Game() {
const mapStageRef = useRef();
return (
-
-
-
-
-
-
-
-
-
- setPeerError(null)}
- >
-
-
- {peerError} See FAQ for more
- information.
-
-
-
-
-
-
- setGameExpired(false)}
- />
-
- {!sessionStatus && }
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setPeerError(null)}
+ >
+
+
+ {peerError} See FAQ{" "}
+ for more information.
+
+
+
+
+
+
+ setGameExpired(false)}
+ />
+
+ {!sessionStatus && }
+
+
+
+
+
+
+
+
+
);
}
diff --git a/yarn.lock b/yarn.lock
index fe9cb37..827f281 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1796,6 +1796,29 @@
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18"
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
+"@dnd-kit/accessibility@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.0.0.tgz#b56e3750414fd907b7d6972b3116aa8f96d07fde"
+ integrity sha512-QwaQ1IJHQHMMuAGOOYHQSx7h7vMZPfO97aDts8t5N/MY7n2QTDSnW+kF7uRQ1tVBkr6vJ+BqHWG5dlgGvwVjow==
+ dependencies:
+ tslib "^2.0.0"
+
+"@dnd-kit/core@3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-3.0.0.tgz#96dadb6b2dba05ab177e0190b33ae219017bc167"
+ integrity sha512-QxHLfZHOLkQWK0FPbr5hefWZzsdZfDuluKPwIK1bT2lwp/4hmFXRA6ivqX3FT4g8T0d2de2C1jxYhKM4H3uMQw==
+ dependencies:
+ "@dnd-kit/accessibility" "^3.0.0"
+ "@dnd-kit/utilities" "^2.0.0"
+ tslib "^2.0.0"
+
+"@dnd-kit/utilities@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/utilities/-/utilities-2.0.0.tgz#a8462dff65c6f606ecbe95273c7e263b14a1ab97"
+ integrity sha512-bjs49yMNzMM+BYRsBUhTqhTk6HEvhuY3leFt6Em6NaYGgygaMbtGbbXof/UXBv7rqyyi0OkmBBnrCCcxqS2t/g==
+ dependencies:
+ tslib "^2.0.0"
+
"@emotion/cache@^10.0.27":
version "10.0.29"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0"
@@ -2008,11 +2031,6 @@
dependencies:
"@hapi/hoek" "^8.3.0"
-"@interactjs/types@1.10.8":
- version "1.10.8"
- resolved "https://registry.yarnpkg.com/@interactjs/types/-/types-1.10.8.tgz#098da479de9c5ac9c8ba97d113746b7dcd9c2204"
- integrity sha512-qU2QfnN7r8AU4mSd2W3XmRtR0d35R1PReIT9b5YzpNLX9S0OQgNBLrEEFyXpa9alq/9h6wYNIwPCVAsknF5uZw==
-
"@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
@@ -7274,13 +7292,6 @@ ini@^1.3.5:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
-interactjs@^1.10.8:
- version "1.10.8"
- resolved "https://registry.yarnpkg.com/interactjs/-/interactjs-1.10.8.tgz#a85b6e89ebf2ed88ea1678287ffcf0becf0dfb1c"
- integrity sha512-hIU82lF9mplmAHVTUmZbHMHKm96AwlD0zWGuf9krKt2dhALHsMOdU+yVilPqIv1VpNAGV66F9B14Rfs4ulS2nA==
- dependencies:
- "@interactjs/types" "1.10.8"
-
internal-ip@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
@@ -12816,6 +12827,11 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+tslib@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
+ integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
+
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"