diff --git a/src/components/map/MapTiles.js b/src/components/map/MapTiles.js
index 044575e..811bb0c 100644
--- a/src/components/map/MapTiles.js
+++ b/src/components/map/MapTiles.js
@@ -4,6 +4,7 @@ import MapTile from "./MapTile";
import MapTileGroup from "./MapTileGroup";
import SortableTiles from "../tile/SortableTiles";
+import SortableTilesDragOverlay from "../tile/SortableTilesDragOverlay";
import { getGroupItems } from "../../helpers/group";
@@ -57,7 +58,12 @@ function MapTiles({ mapsById, onMapEdit, onMapSelect, subgroup }) {
}
}
- return ;
+ return (
+ <>
+
+
+ >
+ );
}
export default MapTiles;
diff --git a/src/components/tile/SortableTiles.js b/src/components/tile/SortableTiles.js
index 257cc6e..404102f 100644
--- a/src/components/tile/SortableTiles.js
+++ b/src/components/tile/SortableTiles.js
@@ -1,26 +1,24 @@
import React from "react";
-import { createPortal } from "react-dom";
-import { DragOverlay } from "@dnd-kit/core";
import { SortableContext } from "@dnd-kit/sortable";
-import { animated, useSpring, config } from "react-spring";
-import { Badge } from "theme-ui";
import { moveGroupsInto } from "../../helpers/group";
import { keyBy } from "../../helpers/shared";
-import Vector2 from "../../helpers/Vector2";
import SortableTile from "./SortableTile";
import {
- useTileDrag,
+ useTileDragId,
+ useTileDragCursor,
+ useTileOverGroupId,
BASE_SORTABLE_ID,
GROUP_SORTABLE_ID,
- GROUP_ID_PREFIX,
} from "../../contexts/TileDragContext";
import { useGroup } from "../../contexts/GroupContext";
function SortableTiles({ renderTile, subgroup }) {
- const { dragId, overId, dragCursor } = useTileDrag();
+ const dragId = useTileDragId();
+ const dragCursor = useTileDragCursor();
+ const overGroupId = useTileOverGroupId();
const {
groups,
selectedGroupIds: allSelectedIds,
@@ -46,15 +44,6 @@ function SortableTiles({ renderTile, subgroup }) {
const disableSorting = (openGroupId && !subgroup) || filter;
const disableGrouping = subgroup || disableSorting || filter;
- const dragBounce = useSpring({
- transform: !!dragId ? "scale(0.9)" : "scale(1)",
- config: config.wobbly,
- position: "relative",
- });
-
- const overGroupId =
- overId && overId.startsWith(GROUP_ID_PREFIX) && overId.slice(9);
-
function renderSortableGroup(group, selectedGroups) {
if (overGroupId === group.id && dragId && group.id !== dragId) {
// If dragging over a group render a preview of that group
@@ -68,57 +57,6 @@ function SortableTiles({ renderTile, subgroup }) {
return renderTile(group);
}
- function renderDragOverlays() {
- let selectedIndices = selectedGroupIds.map((groupId) =>
- activeGroups.findIndex((group) => group.id === groupId)
- );
- const activeIndex = activeGroups.findIndex((group) => group.id === dragId);
- // Sort so the draging tile is the first element
- selectedIndices = selectedIndices.sort((a, b) =>
- a === activeIndex ? -1 : b === activeIndex ? 1 : 0
- );
-
- selectedIndices = selectedIndices.slice(0, 5);
-
- let coords = selectedIndices.map(
- (_, index) => new Vector2(5 * index, 5 * index)
- );
-
- // Reverse so the first element is rendered on top
- selectedIndices = selectedIndices.reverse();
- coords = coords.reverse();
-
- const selectedGroups = selectedIndices.map((index) => activeGroups[index]);
-
- return selectedGroups.map((group, index) => (
-
-
-
- {renderTile(group)}
- {index === selectedIndices.length - 1 &&
- selectedGroupIds.length > 1 && (
-
- {selectedGroupIds.length}
-
- )}
-
-
-
- ));
- }
-
function renderTiles() {
const groupsByIds = keyBy(activeGroups, "id");
const selectedGroupIdsSet = new Set(selectedGroupIds);
@@ -156,7 +94,6 @@ function SortableTiles({ renderTile, subgroup }) {
return (
{renderTiles()}
- {createPortal(dragId && renderDragOverlays(), document.body)}
);
}
diff --git a/src/components/tile/SortableTilesDragOverlay.js b/src/components/tile/SortableTilesDragOverlay.js
new file mode 100644
index 0000000..848b793
--- /dev/null
+++ b/src/components/tile/SortableTilesDragOverlay.js
@@ -0,0 +1,93 @@
+import React from "react";
+import { createPortal } from "react-dom";
+import { DragOverlay } from "@dnd-kit/core";
+import { animated, useSpring, config } from "react-spring";
+import { Badge } from "theme-ui";
+
+import Vector2 from "../../helpers/Vector2";
+
+import { useTileDragId } from "../../contexts/TileDragContext";
+import { useGroup } from "../../contexts/GroupContext";
+
+function SortableTilesDragOverlay({ renderTile, subgroup }) {
+ const dragId = useTileDragId();
+ const {
+ groups,
+ selectedGroupIds: allSelectedIds,
+ filter,
+ openGroupId,
+ openGroupItems,
+ filteredGroupItems,
+ } = useGroup();
+
+ const activeGroups = subgroup
+ ? openGroupItems
+ : filter
+ ? filteredGroupItems
+ : groups;
+
+ // Only populate selected groups if needed
+ let selectedGroupIds = [];
+ if ((subgroup && openGroupId) || (!subgroup && !openGroupId)) {
+ selectedGroupIds = allSelectedIds;
+ }
+ const dragBounce = useSpring({
+ transform: !!dragId ? "scale(0.9)" : "scale(1)",
+ config: config.wobbly,
+ position: "relative",
+ });
+
+ function renderDragOverlays() {
+ let selectedIndices = selectedGroupIds.map((groupId) =>
+ activeGroups.findIndex((group) => group.id === groupId)
+ );
+ const activeIndex = activeGroups.findIndex((group) => group.id === dragId);
+ // Sort so the draging tile is the first element
+ selectedIndices = selectedIndices.sort((a, b) =>
+ a === activeIndex ? -1 : b === activeIndex ? 1 : 0
+ );
+
+ selectedIndices = selectedIndices.slice(0, 5);
+
+ let coords = selectedIndices.map(
+ (_, index) => new Vector2(5 * index, 5 * index)
+ );
+
+ // Reverse so the first element is rendered on top
+ selectedIndices = selectedIndices.reverse();
+ coords = coords.reverse();
+
+ const selectedGroups = selectedIndices.map((index) => activeGroups[index]);
+
+ return selectedGroups.map((group, index) => (
+
+
+
+ {renderTile(group)}
+ {index === selectedIndices.length - 1 &&
+ selectedGroupIds.length > 1 && (
+
+ {selectedGroupIds.length}
+
+ )}
+
+
+
+ ));
+ }
+
+ return createPortal(dragId && renderDragOverlays(), document.body);
+}
+
+export default SortableTilesDragOverlay;
diff --git a/src/components/tile/Tile.js b/src/components/tile/Tile.js
index 9977dc8..115a648 100644
--- a/src/components/tile/Tile.js
+++ b/src/components/tile/Tile.js
@@ -16,17 +16,14 @@ function Tile({
children,
}) {
const [ref, inView] = useInView({ triggerOnce: true });
-
return (
-
)}
-
+
);
}
diff --git a/src/components/token/TokenTiles.js b/src/components/token/TokenTiles.js
index e61277c..55b5948 100644
--- a/src/components/token/TokenTiles.js
+++ b/src/components/token/TokenTiles.js
@@ -5,6 +5,7 @@ import TokenTileGroup from "./TokenTileGroup";
import TokenHiddenBadge from "./TokenHiddenBadge";
import SortableTiles from "../tile/SortableTiles";
+import SortableTilesDragOverlay from "../tile/SortableTilesDragOverlay";
import { getGroupItems } from "../../helpers/group";
@@ -61,7 +62,12 @@ function TokenTiles({ tokensById, onTokenEdit, subgroup }) {
}
}
- return ;
+ return (
+ <>
+
+
+ >
+ );
}
export default TokenTiles;
diff --git a/src/contexts/TileDragContext.js b/src/contexts/TileDragContext.js
index a146846..3663f5a 100644
--- a/src/contexts/TileDragContext.js
+++ b/src/contexts/TileDragContext.js
@@ -1,4 +1,4 @@
-import React, { useState, useContext } from "react";
+import React, { useState, useContext, useEffect } from "react";
import {
MouseSensor,
TouchSensor,
@@ -16,7 +16,9 @@ import { moveGroupsInto, moveGroups, ungroup } from "../helpers/group";
import usePreventSelect from "../hooks/usePreventSelect";
-const TileDragContext = React.createContext();
+const TileDragIdContext = React.createContext();
+const TileOverGroupIdContext = React.createContext();
+const TileDragCursorContext = React.createContext();
export const BASE_SORTABLE_ID = "__base__";
export const GROUP_SORTABLE_ID = "__group__";
@@ -61,7 +63,7 @@ export function TileDragProvider({
} = useGroup();
const mouseSensor = useSensor(MouseSensor, {
- activationConstraint: { distance: 5 },
+ activationConstraint: { distance: 3 },
});
const touchSensor = useSensor(TouchSensor, {
activationConstraint: { delay: 250, tolerance: 5 },
@@ -70,16 +72,23 @@ export function TileDragProvider({
const sensors = useSensors(mouseSensor, touchSensor, keyboardSensor);
- const [dragId, setDragId] = useState();
- const [overId, setOverId] = useState();
+ const [dragId, setDragId] = useState(null);
+ const [overId, setOverId] = useState(null);
const [dragCursor, setDragCursor] = useState("pointer");
const [preventSelect, resumeSelect] = usePreventSelect();
+ const [overGroupId, setOverGroupId] = useState(null);
+ useEffect(() => {
+ setOverGroupId(
+ (overId && overId.startsWith(GROUP_ID_PREFIX) && overId.slice(9)) || null
+ );
+ }, [overId]);
+
function handleDragStart(event) {
const { active, over } = event;
setDragId(active.id);
- setOverId(over?.id);
+ setOverId(over?.id || null);
if (!selectedGroupIds.includes(active.id)) {
onGroupSelect(active.id);
}
@@ -93,7 +102,7 @@ export function TileDragProvider({
function handleDragOver(event) {
const { over } = event;
- setOverId(over?.id);
+ setOverId(over?.id || null);
if (over) {
if (
over.id.startsWith(UNGROUP_ID) ||
@@ -111,8 +120,8 @@ export function TileDragProvider({
function handleDragEnd(event) {
const { active, over, overlayNodeClientRect } = event;
- setDragId();
- setOverId();
+ setDragId(null);
+ setOverId(null);
setDragCursor("pointer");
if (active && over && active.id !== over.id) {
let selectedIndices = selectedGroupIds.map((groupId) =>
@@ -165,8 +174,8 @@ export function TileDragProvider({
}
function handleDragCancel(event) {
- setDragId();
- setOverId();
+ setDragId(null);
+ setOverId(null);
setDragCursor("pointer");
resumeSelect();
@@ -210,8 +219,6 @@ export function TileDragProvider({
return closestCenter(otherRects, rect);
}
- const value = { dragId, overId, dragCursor };
-
return (
-
- {children}
-
+
+
+
+ {children}
+
+
+
);
}
-export function useTileDrag() {
- const context = useContext(TileDragContext);
+export function useTileDragId() {
+ const context = useContext(TileDragIdContext);
if (context === undefined) {
throw new Error("useTileDrag must be used within a TileDragProvider");
}
return context;
}
-export default TileDragContext;
+export function useTileOverGroupId() {
+ const context = useContext(TileOverGroupIdContext);
+ if (context === undefined) {
+ throw new Error("useTileDrag must be used within a TileDragProvider");
+ }
+ return context;
+}
+
+export function useTileDragCursor() {
+ const context = useContext(TileDragCursorContext);
+ if (context === undefined) {
+ throw new Error("useTileDrag must be used within a TileDragProvider");
+ }
+ return context;
+}