diff --git a/src/components/Draggable.js b/src/components/drag/Draggable.js similarity index 100% rename from src/components/Draggable.js rename to src/components/drag/Draggable.js diff --git a/src/components/Droppable.js b/src/components/drag/Droppable.js similarity index 100% rename from src/components/Droppable.js rename to src/components/drag/Droppable.js diff --git a/src/components/Sortable.js b/src/components/drag/Sortable.js similarity index 90% rename from src/components/Sortable.js rename to src/components/drag/Sortable.js index 128a0e2..24f6783 100644 --- a/src/components/Sortable.js +++ b/src/components/drag/Sortable.js @@ -13,11 +13,10 @@ function Sortable({ id, children }) { const style = { cursor: "pointer", - touchAction: "none", opacity: isDragging ? 0.25 : undefined, transform: transform && `translate3d(${transform.x}px, ${transform.y}px, 0px)`, - zIndex: isDragging ? 100 : 0, + zIndex: isDragging ? 100 : undefined, transition, }; diff --git a/src/components/drag/SortableTiles.js b/src/components/drag/SortableTiles.js new file mode 100644 index 0000000..e8f794e --- /dev/null +++ b/src/components/drag/SortableTiles.js @@ -0,0 +1,67 @@ +import React, { useState } from "react"; +import { createPortal } from "react-dom"; +import { + DndContext, + DragOverlay, + MouseSensor, + TouchSensor, + useSensor, + useSensors, +} from "@dnd-kit/core"; +import { SortableContext, arrayMove } from "@dnd-kit/sortable"; +import { animated, useSpring, config } from "react-spring"; + +function SortableTiles({ groups, onGroupChange, renderTile, children }) { + const mouseSensor = useSensor(MouseSensor, { + activationConstraint: { delay: 250, tolerance: 5 }, + }); + const touchSensor = useSensor(TouchSensor, { + activationConstraint: { delay: 250, tolerance: 5 }, + }); + + const sensors = useSensors(mouseSensor, touchSensor); + + const [dragId, setDragId] = useState(); + + function handleDragStart({ active }) { + setDragId(active.id); + } + + function handleDragEnd({ active, over }) { + setDragId(); + if (active && over && active.id !== over.id) { + const oldIndex = groups.indexOf(active.id); + const newIndex = groups.indexOf(over.id); + onGroupChange(arrayMove(groups, oldIndex, newIndex)); + } + } + + const dragBounce = useSpring({ + transform: !!dragId ? "scale(1.05)" : "scale(1)", + config: config.wobbly, + }); + + return ( + + + {children} + {createPortal( + + {dragId && ( + + {renderTile(dragId)} + + )} + , + document.body + )} + + + ); +} + +export default SortableTiles; diff --git a/src/components/map/MapTiles.js b/src/components/map/MapTiles.js index 811467f..14f08d0 100644 --- a/src/components/map/MapTiles.js +++ b/src/components/map/MapTiles.js @@ -1,9 +1,6 @@ -import React, { useState } from "react"; -import { createPortal } from "react-dom"; +import React from "react"; import { Flex, Box, Text, IconButton, Close, Grid } from "theme-ui"; import SimpleBar from "simplebar-react"; -import { DndContext, DragOverlay } from "@dnd-kit/core"; -import { SortableContext, arrayMove } from "@dnd-kit/sortable"; import RemoveMapIcon from "../../icons/RemoveMapIcon"; import ResetMapIcon from "../../icons/ResetMapIcon"; @@ -11,7 +8,9 @@ import ResetMapIcon from "../../icons/ResetMapIcon"; import MapTile from "./MapTile"; import Link from "../Link"; import FilterBar from "../FilterBar"; -import Sortable from "../Sortable"; + +import Sortable from "../drag/Sortable"; +import SortableTiles from "../drag/SortableTiles"; import { useDatabase } from "../../contexts/DatabaseContext"; @@ -36,7 +35,6 @@ function MapTiles({ }) { const { databaseStatus } = useDatabase(); const layout = useResponsiveLayout(); - const [dragId, setDragId] = useState(); let hasMapState = false; for (let state of selectedMapStates) { @@ -55,7 +53,8 @@ function MapTiles({ (map) => map.type === "default" ); - function mapToTile(map) { + function mapToTile(mapId) { + const map = maps.find((map) => map.id === mapId); const isSelected = selectedMaps.includes(map); return ( 1; - function handleDragStart({ active }) { - setDragId(active.id); - } - - function handleDragEnd({ active, over }) { - setDragId(); - if (active && over && active.id !== over.id) { - const oldIndex = groups.indexOf(active.id); - const newIndex = groups.indexOf(over.id); - onMapsGroup(arrayMove(groups, oldIndex, newIndex)); - } - } - return ( - - - - onMapSelect()} - search={search} - onSearchChange={onSearchChange} - selectMode={selectMode} - onSelectModeChange={onSelectModeChange} - onAdd={onMapAdd} - addTitle="Add Map" - /> - + + onMapSelect()} + search={search} + onSearchChange={onSearchChange} + selectMode={selectMode} + onSelectModeChange={onSelectModeChange} + onAdd={onMapAdd} + addTitle="Add Map" + /> + + onMapSelect()} > - onMapSelect()} - > - {groups.map((mapId) => ( - - {mapToTile(maps.find((map) => map.id === mapId))} - - ))} - - - {databaseStatus === "disabled" && ( - - - Map saving is unavailable. See FAQ{" "} - for more information. - - - )} - {selectedMaps.length > 0 && ( - - onMapSelect()} - /> - - onMapsReset()} - disabled={!hasMapState} - > - - - onMapsRemove()} - disabled={hasSelectedDefaultMap} - > - - - - - )} - - {createPortal( - - {dragId && mapToTile(maps.find((maps) => maps.id === dragId))} - , - document.body + {groups.map((mapId) => ( + + {mapToTile(mapId)} + + ))} + + + {databaseStatus === "disabled" && ( + + + Map saving is unavailable. See FAQ{" "} + for more information. + + )} - - + {selectedMaps.length > 0 && ( + + onMapSelect()} + /> + + onMapsReset()} + disabled={!hasMapState} + > + + + onMapsRemove()} + disabled={hasSelectedDefaultMap} + > + + + + + )} + + ); } diff --git a/src/components/token/TokenBar.js b/src/components/token/TokenBar.js index d103402..25a0fb5 100644 --- a/src/components/token/TokenBar.js +++ b/src/components/token/TokenBar.js @@ -6,7 +6,7 @@ import { DragOverlay, DndContext } from "@dnd-kit/core"; import ListToken from "./ListToken"; import SelectTokensButton from "./SelectTokensButton"; -import Draggable from "../Draggable"; +import Draggable from "../drag/Draggable"; import useSetting from "../../hooks/useSetting"; diff --git a/src/components/token/TokenTiles.js b/src/components/token/TokenTiles.js index a35ee87..16f09b0 100644 --- a/src/components/token/TokenTiles.js +++ b/src/components/token/TokenTiles.js @@ -1,9 +1,6 @@ -import React, { useState } from "react"; -import { createPortal } from "react-dom"; +import React from "react"; import { Flex, Box, Text, IconButton, Close, Grid } from "theme-ui"; import SimpleBar from "simplebar-react"; -import { DndContext, DragOverlay } from "@dnd-kit/core"; -import { SortableContext, arrayMove } from "@dnd-kit/sortable"; import RemoveTokenIcon from "../../icons/RemoveTokenIcon"; import TokenHideIcon from "../../icons/TokenHideIcon"; @@ -12,7 +9,9 @@ import TokenShowIcon from "../../icons/TokenShowIcon"; import TokenTile from "./TokenTile"; import Link from "../Link"; import FilterBar from "../FilterBar"; -import Sortable from "../Sortable"; + +import Sortable from "../drag/Sortable"; +import SortableTiles from "../drag/SortableTiles"; import { useDatabase } from "../../contexts/DatabaseContext"; @@ -35,14 +34,14 @@ function TokenTiles({ }) { const { databaseStatus } = useDatabase(); const layout = useResponsiveLayout(); - const [dragId, setDragId] = useState(); let hasSelectedDefaultToken = selectedTokens.some( (token) => token.type === "default" ); let allTokensVisible = selectedTokens.every((token) => !token.hideInSidebar); - function tokenToTile(token) { + function tokenToTile(tokenId) { + const token = tokens.find((token) => token.id === tokenId); const isSelected = selectedTokens.includes(token); return ( - - - onTokenSelect()} - search={search} - onSearchChange={onSearchChange} - selectMode={selectMode} - onSelectModeChange={onSelectModeChange} - onAdd={onTokenAdd} - addTitle="Add Token" - /> - + + onTokenSelect()} + search={search} + onSearchChange={onSearchChange} + selectMode={selectMode} + onSelectModeChange={onSelectModeChange} + onAdd={onTokenAdd} + addTitle="Add Token" + /> + + onTokenSelect()} > - onTokenSelect()} - > - {groups.map((tokenId) => ( - - {tokenToTile(tokens.find((token) => token.id === tokenId))} - - ))} - - - {databaseStatus === "disabled" && ( - - - Token saving is unavailable. See{" "} - FAQ for more information. - - - )} - {selectedTokens.length > 0 && ( - - onTokenSelect()} - /> - - onTokensHide(allTokensVisible)} - > - {allTokensVisible ? : } - - onTokensRemove()} - disabled={hasSelectedDefaultToken} - > - - - - - )} - - {createPortal( - - {dragId && tokenToTile(tokens.find((token) => token.id === dragId))} - , - document.body + {groups.map((tokenId) => ( + + {tokenToTile(tokenId)} + + ))} + + + {databaseStatus === "disabled" && ( + + + Token saving is unavailable. See FAQ{" "} + for more information. + + )} - - + {selectedTokens.length > 0 && ( + + onTokenSelect()} + /> + + onTokensHide(allTokensVisible)} + > + {allTokensVisible ? : } + + onTokensRemove()} + disabled={hasSelectedDefaultToken} + > + + + + + )} + + ); }