Fix tile drag cancel with modal open

This commit is contained in:
Mitchell McCaffrey 2021-06-09 10:33:47 +10:00
parent 63f77059f1
commit a8c355f251
4 changed files with 109 additions and 58 deletions

View File

@ -6,6 +6,7 @@ import {
DragOverlay,
DndContext,
PointerSensor,
KeyboardSensor,
useSensor,
useSensors,
} from "@dnd-kit/core";
@ -45,7 +46,8 @@ function TokenBar({ onMapTokensStateCreate }) {
const pointerSensor = useSensor(PointerSensor, {
activationConstraint: { distance: 5 },
});
const sensors = useSensors(pointerSensor);
const keyboardSensor = useSensor(KeyboardSensor);
const sensors = useSensors(pointerSensor, keyboardSensor);
function handleDragStart({ active }) {
setDragId(active.id);
@ -93,6 +95,10 @@ function TokenBar({ onMapTokensStateCreate }) {
}
}
function handleDragCancel() {
setDragId(null);
}
function renderToken(group, draggable = true) {
if (group.type === "item") {
const token = tokensById[group.id];
@ -132,6 +138,7 @@ function TokenBar({ onMapTokensStateCreate }) {
<DndContext
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDragCancel={handleDragCancel}
autoScroll={false}
sensors={sensors}
>

View File

@ -1,8 +1,8 @@
import React, { useState, useContext } from "react";
import {
DndContext,
MouseSensor,
TouchSensor,
PointerSensor,
KeyboardSensor,
useSensor,
useSensors,
closestCenter,
@ -38,7 +38,13 @@ function rectIntersection(rects, point) {
return null;
}
export function TileDragProvider({ onDragAdd, children }) {
export function TileDragProvider({
onDragAdd,
onDragStart,
onDragEnd,
onDragCancel,
children,
}) {
const {
groups,
activeGroups,
@ -50,29 +56,32 @@ export function TileDragProvider({ onDragAdd, children }) {
filter,
} = useGroup();
const mouseSensor = useSensor(MouseSensor, {
activationConstraint: { delay: 250, tolerance: 5 },
});
const touchSensor = useSensor(TouchSensor, {
const pointerSensor = useSensor(PointerSensor, {
activationConstraint: { delay: 250, tolerance: 5 },
});
const keyboardSensor = useSensor(KeyboardSensor);
const sensors = useSensors(mouseSensor, touchSensor);
const sensors = useSensors(pointerSensor, keyboardSensor);
const [dragId, setDragId] = useState();
const [overId, setOverId] = useState();
const [dragCursor, setDragCursor] = useState("pointer");
function handleDragStart({ active, over }) {
function handleDragStart(event) {
const { active, over } = event;
setDragId(active.id);
setOverId(over?.id);
if (!selectedGroupIds.includes(active.id)) {
onGroupSelect(active.id);
}
setDragCursor("grabbing");
onDragStart && onDragStart(event);
}
function handleDragOver({ over }) {
function handleDragOver(event) {
const { over } = event;
setOverId(over?.id);
if (over) {
if (
@ -88,56 +97,64 @@ export function TileDragProvider({ onDragAdd, children }) {
}
}
function handleDragEnd({ active, over }) {
function handleDragEnd(event) {
const { active, over } = event;
setDragId();
setOverId();
setDragCursor("pointer");
if (!active || !over || active.id === over.id) {
return;
if (active && over && active.id !== over.id) {
let selectedIndices = selectedGroupIds.map((groupId) =>
activeGroups.findIndex((group) => group.id === groupId)
);
// Maintain current group sorting
selectedIndices = selectedIndices.sort((a, b) => a - b);
if (over.id.startsWith(GROUP_ID_PREFIX)) {
onGroupSelect();
// Handle tile group
const overId = over.id.slice(9);
if (overId !== active.id) {
const overGroupIndex = activeGroups.findIndex(
(group) => group.id === overId
);
onGroupsChange(
moveGroupsInto(activeGroups, overGroupIndex, selectedIndices),
openGroupId
);
}
} else if (over.id === UNGROUP_ID) {
onGroupSelect();
// Handle tile ungroup
const newGroups = ungroup(groups, openGroupId, selectedIndices);
// Close group if it was removed
if (!newGroups.find((group) => group.id === openGroupId)) {
onGroupClose();
}
onGroupsChange(newGroups);
} 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
const overGroupIndex = activeGroups.findIndex(
(group) => group.id === over.id
);
onGroupsChange(
moveGroups(activeGroups, overGroupIndex, selectedIndices),
openGroupId
);
}
}
let selectedIndices = selectedGroupIds.map((groupId) =>
activeGroups.findIndex((group) => group.id === groupId)
);
// Maintain current group sorting
selectedIndices = selectedIndices.sort((a, b) => a - b);
onDragEnd && onDragEnd(event);
}
if (over.id.startsWith(GROUP_ID_PREFIX)) {
onGroupSelect();
// Handle tile group
const overId = over.id.slice(9);
if (overId === active.id) {
return;
}
function handleDragCancel(event) {
setDragId();
setOverId();
setDragCursor("pointer");
const overGroupIndex = activeGroups.findIndex(
(group) => group.id === overId
);
onGroupsChange(
moveGroupsInto(activeGroups, overGroupIndex, selectedIndices),
openGroupId
);
} else if (over.id === UNGROUP_ID) {
onGroupSelect();
// Handle tile ungroup
const newGroups = ungroup(groups, openGroupId, selectedIndices);
// Close group if it was removed
if (!newGroups.find((group) => group.id === openGroupId)) {
onGroupClose();
}
onGroupsChange(newGroups);
} 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
const overGroupIndex = activeGroups.findIndex(
(group) => group.id === over.id
);
onGroupsChange(
moveGroups(activeGroups, overGroupIndex, selectedIndices),
openGroupId
);
}
onDragCancel && onDragCancel(event);
}
function customCollisionDetection(rects, rect) {
@ -183,6 +200,7 @@ export function TileDragProvider({ onDragAdd, children }) {
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDragOver={handleDragOver}
onDragCancel={handleDragCancel}
sensors={sensors}
collisionDetection={customCollisionDetection}
>

View File

@ -168,6 +168,8 @@ function SelectMapModal({
const [editingMapId, setEditingMapId] = useState();
const [isDraggingMap, setIsDraggingMap] = useState(false);
const [canAddDraggedMap, setCanAddDraggedMap] = useState(false);
function handleGroupsSelect(groupIds) {
if (groupIds.length === 1) {
@ -197,6 +199,7 @@ function SelectMapModal({
isOpen={isOpen}
onRequestClose={handleClose}
style={{ maxWidth: layout.modalSize, width: "calc(100% - 16px)" }}
shouldCloseOnEsc={!isDraggingMap}
>
<ImageDrop onDrop={handleImagesUpload} dropText="Drop map to import">
<input
@ -229,7 +232,12 @@ function SelectMapModal({
</Label>
<TileActionBar onAdd={openImageDialog} addTitle="Import Map(s)" />
<Box sx={{ position: "relative" }}>
<TileDragProvider onDragAdd={canAddDraggedMap && handleDragAdd}>
<TileDragProvider
onDragAdd={canAddDraggedMap && handleDragAdd}
onDragStart={() => setIsDraggingMap(true)}
onDragEnd={() => setIsDraggingMap(false)}
onDragCancel={() => setIsDraggingMap(false)}
>
<TilesContainer>
<MapTiles
maps={maps}
@ -238,7 +246,12 @@ function SelectMapModal({
/>
</TilesContainer>
</TileDragProvider>
<TileDragProvider onDragAdd={canAddDraggedMap && handleDragAdd}>
<TileDragProvider
onDragAdd={canAddDraggedMap && handleDragAdd}
onDragStart={() => setIsDraggingMap(true)}
onDragEnd={() => setIsDraggingMap(false)}
onDragCancel={() => setIsDraggingMap(false)}
>
<TilesOverlay modalSize={modalSize}>
<MapTiles
maps={maps}

View File

@ -139,6 +139,8 @@ function SelectTokensModal({ isOpen, onRequestClose, onMapTokensStateCreate }) {
*/
const [editingTokenId, setEditingTokenId] = useState();
const [isDraggingToken, setIsDraggingToken] = useState(false);
const mapStageRef = useMapStage();
function handleTokensAddToMap(groupIds, rect) {
let clientPosition = new Vector2(
@ -198,6 +200,7 @@ function SelectTokensModal({ isOpen, onRequestClose, onMapTokensStateCreate }) {
isOpen={isOpen}
onRequestClose={onRequestClose}
style={{ maxWidth: layout.modalSize, width: "calc(100% - 16px)" }}
shouldCloseOnEsc={!isDraggingToken}
>
<ImageDrop onDrop={handleImagesUpload} dropText="Drop token to import">
<input
@ -232,7 +235,12 @@ function SelectTokensModal({ isOpen, onRequestClose, onMapTokensStateCreate }) {
addTitle="Import Token(s)"
/>
<Box sx={{ position: "relative" }}>
<TileDragProvider onDragAdd={handleTokensAddToMap}>
<TileDragProvider
onDragAdd={handleTokensAddToMap}
onDragStart={() => setIsDraggingToken(true)}
onDragEnd={() => setIsDraggingToken(false)}
onDragCancel={() => setIsDraggingToken(false)}
>
<TilesContainer>
<TokenTiles
tokens={tokens}
@ -240,7 +248,12 @@ function SelectTokensModal({ isOpen, onRequestClose, onMapTokensStateCreate }) {
/>
</TilesContainer>
</TileDragProvider>
<TileDragProvider onDragAdd={handleTokensAddToMap}>
<TileDragProvider
onDragAdd={handleTokensAddToMap}
onDragStart={() => setIsDraggingToken(true)}
onDragEnd={() => setIsDraggingToken(false)}
onDragCancel={() => setIsDraggingToken(false)}
>
<TilesOverlay modalSize={modalSize}>
<TokenTiles
tokens={tokens}