Fix drag and drop add to map bugs with scrolled tile grid
This commit is contained in:
parent
2088c2ab04
commit
57cce9346d
@ -5,8 +5,8 @@
|
||||
"dependencies": {
|
||||
"@babylonjs/core": "^4.2.0",
|
||||
"@babylonjs/loaders": "^4.2.0",
|
||||
"@dnd-kit/core": "3.0.2",
|
||||
"@dnd-kit/sortable": "^3.0.1",
|
||||
"@dnd-kit/core": "^3.0.4",
|
||||
"@dnd-kit/sortable": "^3.1.0",
|
||||
"@mitchemmc/dexie-export-import": "^1.0.1",
|
||||
"@msgpack/msgpack": "^2.4.1",
|
||||
"@sentry/react": "^6.2.2",
|
||||
|
@ -1,54 +0,0 @@
|
||||
import React from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
import Droppable from "../drag/Droppable";
|
||||
|
||||
import { ADD_TO_MAP_ID_PREFIX } from "../../contexts/TileDragContext";
|
||||
|
||||
function TilesAddDroppable({ containerSize }) {
|
||||
return createPortal(
|
||||
<div>
|
||||
<Droppable
|
||||
id={`${ADD_TO_MAP_ID_PREFIX}-1`}
|
||||
style={{
|
||||
width: "100vw",
|
||||
height: `calc(50vh - ${containerSize.height / 2}px)`,
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
}}
|
||||
/>
|
||||
<Droppable
|
||||
id={`${ADD_TO_MAP_ID_PREFIX}-2`}
|
||||
style={{
|
||||
width: "100vw",
|
||||
height: `calc(50vh - ${containerSize.height / 2}px)`,
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
<Droppable
|
||||
id={`${ADD_TO_MAP_ID_PREFIX}-3`}
|
||||
style={{
|
||||
width: `calc(50vw - ${containerSize.width / 2}px)`,
|
||||
height: "100vh",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
}}
|
||||
/>
|
||||
<Droppable
|
||||
id={`${ADD_TO_MAP_ID_PREFIX}-4`}
|
||||
style={{
|
||||
width: `calc(50vw - ${containerSize.width / 2}px)`,
|
||||
height: "100vh",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
}}
|
||||
/>
|
||||
</div>,
|
||||
document.body
|
||||
);
|
||||
}
|
||||
|
||||
export default TilesAddDroppable;
|
@ -3,9 +3,12 @@ import { Grid, useThemeUI } from "theme-ui";
|
||||
import SimpleBar from "simplebar-react";
|
||||
|
||||
import { useGroup } from "../../contexts/GroupContext";
|
||||
import { ADD_TO_MAP_ID } from "../../contexts/TileDragContext";
|
||||
|
||||
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
||||
|
||||
import Droppable from "../drag/Droppable";
|
||||
|
||||
function TilesContainer({ children }) {
|
||||
const { onGroupSelect } = useGroup();
|
||||
|
||||
@ -28,10 +31,19 @@ function TilesContainer({ children }) {
|
||||
sx={{
|
||||
borderRadius: "4px",
|
||||
overflow: "hidden",
|
||||
position: "relative",
|
||||
}}
|
||||
gap={2}
|
||||
columns={`repeat(${layout.tileGridColumns}, 1fr)`}
|
||||
>
|
||||
<Droppable
|
||||
id={ADD_TO_MAP_ID}
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
zIndex: -1,
|
||||
}}
|
||||
/>
|
||||
{children}
|
||||
</Grid>
|
||||
</SimpleBar>
|
||||
|
@ -5,8 +5,7 @@ import ReactResizeDetector from "react-resize-detector";
|
||||
import SimpleBar from "simplebar-react";
|
||||
|
||||
import { useGroup } from "../../contexts/GroupContext";
|
||||
|
||||
import TilesUngroupDroppable from "./TilesUngroupDroppable";
|
||||
import { UNGROUP_ID, ADD_TO_MAP_ID } from "../../contexts/TileDragContext";
|
||||
|
||||
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
||||
|
||||
@ -16,7 +15,9 @@ import GroupNameModal from "../../modals/GroupNameModal";
|
||||
|
||||
import { renameGroup } from "../../helpers/group";
|
||||
|
||||
function TilesOverlay({ children }) {
|
||||
import Droppable from "../drag/Droppable";
|
||||
|
||||
function TilesOverlay({ modalSize, children }) {
|
||||
const {
|
||||
groups,
|
||||
openGroupId,
|
||||
@ -41,11 +42,6 @@ function TilesOverlay({ children }) {
|
||||
setContinerSize({ width: size, height: size });
|
||||
}
|
||||
|
||||
const [overlaySize, setOverlaySize] = useState({ width: 0, height: 0 });
|
||||
function handleOverlayResize(width, height) {
|
||||
setOverlaySize({ width, height });
|
||||
}
|
||||
|
||||
const [isGroupNameModalOpen, setIsGroupNameModalOpen] = useState(false);
|
||||
function handleGroupNameChange(name) {
|
||||
onGroupsChange(renameGroup(groups, openGroupId, name));
|
||||
@ -57,28 +53,16 @@ function TilesOverlay({ children }) {
|
||||
return (
|
||||
<>
|
||||
{openGroupId && (
|
||||
<TilesUngroupDroppable
|
||||
innerContainerSize={containerSize}
|
||||
outerContainerSize={overlaySize}
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
top: 0,
|
||||
}}
|
||||
bg="overlay"
|
||||
/>
|
||||
)}
|
||||
{openGroupId && (
|
||||
<ReactResizeDetector
|
||||
handleWidth
|
||||
handleHeight
|
||||
onResize={handleOverlayResize}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
top: 0,
|
||||
}}
|
||||
bg="overlay"
|
||||
/>
|
||||
</ReactResizeDetector>
|
||||
)}
|
||||
<ReactResizeDetector
|
||||
handleWidth
|
||||
handleHeight
|
||||
@ -147,11 +131,38 @@ function TilesOverlay({ children }) {
|
||||
sx={{
|
||||
borderRadius: "4px",
|
||||
overflow: "hidden",
|
||||
position: "relative",
|
||||
}}
|
||||
gap={2}
|
||||
columns={`repeat(${layout.groupGridColumns}, 1fr)`}
|
||||
p={3}
|
||||
>
|
||||
<Droppable
|
||||
id={ADD_TO_MAP_ID}
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: modalSize.width,
|
||||
// height: modalSize.height,
|
||||
height: `calc(100% + ${
|
||||
modalSize.height - containerSize.height + 48
|
||||
}px)`,
|
||||
left: `-${
|
||||
(modalSize.width - containerSize.width) / 2 + 8
|
||||
}px`,
|
||||
top: `-${
|
||||
(modalSize.height - containerSize.height) / 2 + 48
|
||||
}px`,
|
||||
zIndex: -1,
|
||||
}}
|
||||
/>
|
||||
<Droppable
|
||||
id={UNGROUP_ID}
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
zIndex: -1,
|
||||
}}
|
||||
/>
|
||||
{children}
|
||||
</Grid>
|
||||
</SimpleBar>
|
||||
|
@ -1,59 +0,0 @@
|
||||
import React from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
import Droppable from "../drag/Droppable";
|
||||
|
||||
import { UNGROUP_ID_PREFIX } from "../../contexts/TileDragContext";
|
||||
|
||||
function TilesUngroupDroppable({ outerContainerSize, innerContainerSize }) {
|
||||
const width = (outerContainerSize.width - innerContainerSize.width) / 2;
|
||||
const height = (outerContainerSize.height - innerContainerSize.height) / 2;
|
||||
|
||||
return createPortal(
|
||||
<div>
|
||||
<Droppable
|
||||
id={`${UNGROUP_ID_PREFIX}-1`}
|
||||
style={{
|
||||
width: outerContainerSize.width,
|
||||
height,
|
||||
position: "absolute",
|
||||
top: `calc(50% - ${innerContainerSize.height / 2 + height}px)`,
|
||||
left: `calc(50% - ${outerContainerSize.width / 2}px)`,
|
||||
}}
|
||||
/>
|
||||
<Droppable
|
||||
id={`${UNGROUP_ID_PREFIX}-2`}
|
||||
style={{
|
||||
width: outerContainerSize.width,
|
||||
height,
|
||||
position: "absolute",
|
||||
top: `calc(50% + ${innerContainerSize.height / 2}px)`,
|
||||
left: `calc(50% - ${outerContainerSize.width / 2}px)`,
|
||||
}}
|
||||
/>
|
||||
<Droppable
|
||||
id={`${UNGROUP_ID_PREFIX}-3`}
|
||||
style={{
|
||||
width,
|
||||
height: outerContainerSize.height,
|
||||
position: "absolute",
|
||||
top: `calc(50% - ${outerContainerSize.height / 2}px)`,
|
||||
left: `calc(50% - ${innerContainerSize.width / 2 + width}px)`,
|
||||
}}
|
||||
/>
|
||||
<Droppable
|
||||
id={`${UNGROUP_ID_PREFIX}-4`}
|
||||
style={{
|
||||
width,
|
||||
height: outerContainerSize.height,
|
||||
position: "absolute",
|
||||
top: `calc(50% - ${outerContainerSize.height / 2}px)`,
|
||||
left: `calc(50% + ${innerContainerSize.width / 2}px)`,
|
||||
}}
|
||||
/>
|
||||
</div>,
|
||||
document.body
|
||||
);
|
||||
}
|
||||
|
||||
export default TilesUngroupDroppable;
|
@ -6,7 +6,6 @@ import {
|
||||
useSensor,
|
||||
useSensors,
|
||||
closestCenter,
|
||||
rectIntersection,
|
||||
} from "@dnd-kit/core";
|
||||
|
||||
import { useGroup } from "./GroupContext";
|
||||
@ -18,8 +17,26 @@ const TileDragContext = React.createContext();
|
||||
export const BASE_SORTABLE_ID = "__base__";
|
||||
export const GROUP_SORTABLE_ID = "__group__";
|
||||
export const GROUP_ID_PREFIX = "__group__";
|
||||
export const UNGROUP_ID_PREFIX = "__ungroup__";
|
||||
export const ADD_TO_MAP_ID_PREFIX = "__add__";
|
||||
export const UNGROUP_ID = "__ungroup__";
|
||||
export const ADD_TO_MAP_ID = "__add__";
|
||||
|
||||
// Custom rectIntersect that takes a point
|
||||
function rectIntersection(rects, point) {
|
||||
for (let rect of rects) {
|
||||
const [id, bounds] = rect;
|
||||
if (
|
||||
id &&
|
||||
bounds &&
|
||||
point.x > bounds.offsetLeft &&
|
||||
point.x < bounds.offsetLeft + bounds.width &&
|
||||
point.y > bounds.offsetTop &&
|
||||
point.y < bounds.offsetTop + bounds.height
|
||||
) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function TileDragProvider({ onDragAdd, children }) {
|
||||
const {
|
||||
@ -59,11 +76,11 @@ export function TileDragProvider({ onDragAdd, children }) {
|
||||
setOverId(over?.id);
|
||||
if (over) {
|
||||
if (
|
||||
over.id.startsWith(UNGROUP_ID_PREFIX) ||
|
||||
over.id.startsWith(UNGROUP_ID) ||
|
||||
over.id.startsWith(GROUP_ID_PREFIX)
|
||||
) {
|
||||
setDragCursor("alias");
|
||||
} else if (over.id.startsWith(ADD_TO_MAP_ID_PREFIX)) {
|
||||
} else if (over.id.startsWith(ADD_TO_MAP_ID)) {
|
||||
setDragCursor(onDragAdd ? "copy" : "no-drop");
|
||||
} else {
|
||||
setDragCursor("grabbing");
|
||||
@ -100,7 +117,7 @@ export function TileDragProvider({ onDragAdd, children }) {
|
||||
moveGroupsInto(activeGroups, overGroupIndex, selectedIndices),
|
||||
openGroupId
|
||||
);
|
||||
} else if (over.id.startsWith(UNGROUP_ID_PREFIX)) {
|
||||
} else if (over.id === UNGROUP_ID) {
|
||||
onGroupSelect();
|
||||
// Handle tile ungroup
|
||||
const newGroups = ungroup(groups, openGroupId, selectedIndices);
|
||||
@ -109,7 +126,7 @@ export function TileDragProvider({ onDragAdd, children }) {
|
||||
onGroupClose();
|
||||
}
|
||||
onGroupsChange(newGroups);
|
||||
} else if (over.id.startsWith(ADD_TO_MAP_ID_PREFIX)) {
|
||||
} else if (over.id === ADD_TO_MAP_ID) {
|
||||
onDragAdd && onDragAdd(selectedGroupIds, over.rect);
|
||||
} else if (!filter) {
|
||||
// Hanlde tile move only if we have no filter
|
||||
@ -124,27 +141,39 @@ export function TileDragProvider({ onDragAdd, children }) {
|
||||
}
|
||||
|
||||
function customCollisionDetection(rects, rect) {
|
||||
// Handle group rects
|
||||
if (openGroupId) {
|
||||
const ungroupRects = rects.filter(([id]) =>
|
||||
id.startsWith(UNGROUP_ID_PREFIX)
|
||||
);
|
||||
const intersectingGroupRect = rectIntersection(ungroupRects, rect);
|
||||
if (intersectingGroupRect) {
|
||||
return intersectingGroupRect;
|
||||
// Calculate rect bottom taking into account any scroll offset
|
||||
const rectBottom = rect.top + rect.bottom - rect.offsetTop;
|
||||
const rectCenter = {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rectBottom - rect.height / 2,
|
||||
};
|
||||
|
||||
// Find whether out rect center is outside our add to map rect
|
||||
const addRect = rects.find(([id]) => id === ADD_TO_MAP_ID);
|
||||
if (addRect) {
|
||||
const intersectingAddRect = rectIntersection([addRect], rectCenter);
|
||||
if (!intersectingAddRect) {
|
||||
return ADD_TO_MAP_ID;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle add to map rects
|
||||
const addRects = rects.filter(([id]) =>
|
||||
id.startsWith(ADD_TO_MAP_ID_PREFIX)
|
||||
);
|
||||
const intersectingAddRect = rectIntersection(addRects, rect);
|
||||
if (intersectingAddRect) {
|
||||
return intersectingAddRect;
|
||||
// Find whether out rect center is outside our ungroup rect
|
||||
if (openGroupId) {
|
||||
const ungroupRect = rects.find(([id]) => id === UNGROUP_ID);
|
||||
if (ungroupRect) {
|
||||
const intersectingGroupRect = rectIntersection(
|
||||
[ungroupRect],
|
||||
rectCenter
|
||||
);
|
||||
if (!intersectingGroupRect) {
|
||||
return UNGROUP_ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const otherRects = rects.filter(([id]) => id !== UNGROUP_ID_PREFIX);
|
||||
const otherRects = rects.filter(
|
||||
([id]) => id !== ADD_TO_MAP_ID && id !== UNGROUP_ID
|
||||
);
|
||||
|
||||
return closestCenter(otherRects, rect);
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import SelectMapSelectButton from "../components/map/SelectMapSelectButton";
|
||||
|
||||
import TilesOverlay from "../components/tile/TilesOverlay";
|
||||
import TilesContainer from "../components/tile/TilesContainer";
|
||||
import TilesAddDroppable from "../components/tile/TilesAddDroppable";
|
||||
import TileActionBar from "../components/tile/TileActionBar";
|
||||
|
||||
import { findGroup, getItemNames } from "../helpers/group";
|
||||
@ -231,7 +230,6 @@ function SelectMapModal({
|
||||
<TileActionBar onAdd={openImageDialog} addTitle="Import Map(s)" />
|
||||
<Box sx={{ position: "relative" }}>
|
||||
<TileDragProvider onDragAdd={canAddDraggedMap && handleDragAdd}>
|
||||
<TilesAddDroppable containerSize={modalSize} />
|
||||
<TilesContainer>
|
||||
<MapTiles
|
||||
maps={maps}
|
||||
@ -241,8 +239,7 @@ function SelectMapModal({
|
||||
</TilesContainer>
|
||||
</TileDragProvider>
|
||||
<TileDragProvider onDragAdd={canAddDraggedMap && handleDragAdd}>
|
||||
<TilesAddDroppable containerSize={modalSize} />
|
||||
<TilesOverlay>
|
||||
<TilesOverlay modalSize={modalSize}>
|
||||
<MapTiles
|
||||
maps={maps}
|
||||
onMapEdit={setEditingMapId}
|
||||
|
@ -16,7 +16,6 @@ import TokenEditBar from "../components/token/TokenEditBar";
|
||||
|
||||
import TilesOverlay from "../components/tile/TilesOverlay";
|
||||
import TilesContainer from "../components/tile/TilesContainer";
|
||||
import TilesAddDroppable from "../components/tile/TilesAddDroppable";
|
||||
import TileActionBar from "../components/tile/TileActionBar";
|
||||
|
||||
import { getGroupItems, getItemNames } from "../helpers/group";
|
||||
@ -234,7 +233,6 @@ function SelectTokensModal({ isOpen, onRequestClose, onMapTokensStateCreate }) {
|
||||
/>
|
||||
<Box sx={{ position: "relative" }}>
|
||||
<TileDragProvider onDragAdd={handleTokensAddToMap}>
|
||||
<TilesAddDroppable containerSize={modalSize} />
|
||||
<TilesContainer>
|
||||
<TokenTiles
|
||||
tokens={tokens}
|
||||
@ -243,8 +241,7 @@ function SelectTokensModal({ isOpen, onRequestClose, onMapTokensStateCreate }) {
|
||||
</TilesContainer>
|
||||
</TileDragProvider>
|
||||
<TileDragProvider onDragAdd={handleTokensAddToMap}>
|
||||
<TilesAddDroppable containerSize={modalSize} />
|
||||
<TilesOverlay>
|
||||
<TilesOverlay modalSize={modalSize}>
|
||||
<TokenTiles
|
||||
tokens={tokens}
|
||||
onTokenEdit={setEditingTokenId}
|
||||
|
17
yarn.lock
17
yarn.lock
@ -1803,21 +1803,20 @@
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@dnd-kit/core@3.0.2", "@dnd-kit/core@^3.0.0":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-3.0.2.tgz#e46ae11ef667aa5c31fddab21cf36ffd80d3ce5b"
|
||||
integrity sha512-L+rGnDYBb4BfYKDylzIBeODRIlJ+YVvo2iL9pVXsh317Nq7c9irCvi3XK8JnWD5QBw/3WZ5FmbPmTE91EKwKeA==
|
||||
"@dnd-kit/core@^3.0.4":
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-3.0.4.tgz#b10a0ffc9300a665108a4f7387b1c55dc6bf9c7f"
|
||||
integrity sha512-EoUNyRWnRm8l0okwqG0iUwB0zrPkqBfJrIPdb3kMrTjoG/+70i4RnIuAhdKDnOxldFynWahPfJV3YBoDgeudgg==
|
||||
dependencies:
|
||||
"@dnd-kit/accessibility" "^3.0.0"
|
||||
"@dnd-kit/utilities" "^2.0.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@dnd-kit/sortable@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@dnd-kit/sortable/-/sortable-3.0.1.tgz#a63c2bcecb57c48cd72abcc6364b7c35d3af351f"
|
||||
integrity sha512-fRflFwkj1hXkNZTy/nA6zlgLryZCDKm0OaJnzcFWu9TNZ7hZ0Ja6EMQwhOu6aGuHyCTUGTToBho9ZyyVN671qw==
|
||||
"@dnd-kit/sortable@^3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@dnd-kit/sortable/-/sortable-3.1.0.tgz#4638ea3d9202287ffb186679b593b3407c7e737d"
|
||||
integrity sha512-BwnNgqMTqwIASdu9/x5PdqnAaFakx4HrflDP/zHfZAnGm912+Y545PlztVtAdxK+U6L3pXPR1BQaZO7JQjDxmg==
|
||||
dependencies:
|
||||
"@dnd-kit/core" "^3.0.0"
|
||||
"@dnd-kit/utilities" "^2.0.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user