Ensure order of selected tiles is kept when moving them

This commit is contained in:
Mitchell McCaffrey 2021-05-27 17:19:36 +10:00
parent dd130a008e
commit 69eafb868c
8 changed files with 64 additions and 41 deletions

View File

@ -66,7 +66,6 @@ function MapTiles({ maps, onMapEdit, onMapSelect, subgroup, columns }) {
onTileSelect={onGroupSelect} onTileSelect={onGroupSelect}
disableGrouping={subgroup} disableGrouping={subgroup}
openGroupId={openGroupId} openGroupId={openGroupId}
columns={columns}
/> />
); );
} }

View File

@ -9,11 +9,11 @@ import {
useSensors, useSensors,
closestCenter, closestCenter,
} from "@dnd-kit/core"; } from "@dnd-kit/core";
import { SortableContext, arrayMove } from "@dnd-kit/sortable"; import { SortableContext } from "@dnd-kit/sortable";
import { animated, useSpring, config } from "react-spring"; import { animated, useSpring, config } from "react-spring";
import { Badge } from "theme-ui"; import { Badge } from "theme-ui";
import { moveGroups } from "../../helpers/group"; import { moveGroupsInto, moveGroups } from "../../helpers/group";
import { keyBy } from "../../helpers/shared"; import { keyBy } from "../../helpers/shared";
import Vector2 from "../../helpers/Vector2"; import Vector2 from "../../helpers/Vector2";
@ -27,7 +27,6 @@ function SortableTiles({
onTileSelect, onTileSelect,
disableGrouping, disableGrouping,
openGroupId, openGroupId,
columns,
}) { }) {
const mouseSensor = useSensor(MouseSensor, { const mouseSensor = useSensor(MouseSensor, {
activationConstraint: { delay: 250, tolerance: 5 }, activationConstraint: { delay: 250, tolerance: 5 },
@ -60,29 +59,28 @@ function SortableTiles({
return; return;
} }
let selectedIndices = selectedGroupIds.map((groupId) =>
groups.findIndex((group) => group.id === groupId)
);
// Maintain current group sorting
selectedIndices = selectedIndices.sort((a, b) => a - b);
if (over.id.startsWith("__group__")) { if (over.id.startsWith("__group__")) {
const overId = over.id.slice(9); const overId = over.id.slice(9);
if (overId === active.id) { if (overId === active.id) {
return; return;
} }
let newGroups = groups;
const overGroupIndex = groups.findIndex((group) => group.id === overId); const overGroupIndex = groups.findIndex((group) => group.id === overId);
const selectedGroupIndices = selectedGroupIds.map((groupId) => onGroupChange(moveGroupsInto(groups, overGroupIndex, selectedIndices));
groups.findIndex((group) => group.id === groupId)
);
onGroupChange(
moveGroups(newGroups, overGroupIndex, selectedGroupIndices)
);
onTileSelect(); onTileSelect();
} else if (active.id !== over.id) { } else {
let newGroups = groups; if (active.id === over.id) {
for (let groupId of selectedGroupIds) { return;
const oldIndex = newGroups.findIndex((group) => group.id === groupId);
const newIndex = newGroups.findIndex((group) => group.id === over.id);
newGroups = arrayMove(newGroups, oldIndex, newIndex);
} }
onGroupChange(newGroups);
const overGroupIndex = groups.findIndex((group) => group.id === over.id);
onGroupChange(moveGroups(groups, overGroupIndex, selectedIndices));
} }
} }
@ -98,7 +96,7 @@ function SortableTiles({
function renderSortableGroup(group, selectedGroups) { function renderSortableGroup(group, selectedGroups) {
if (overGroupId === group.id && dragId && group.id !== dragId) { if (overGroupId === group.id && dragId && group.id !== dragId) {
// If dragging over a group render a preview of that group // If dragging over a group render a preview of that group
const previewGroup = moveGroups( const previewGroup = moveGroupsInto(
[group, ...selectedGroups], [group, ...selectedGroups],
0, 0,
selectedGroups.map((_, i) => i + 1) selectedGroups.map((_, i) => i + 1)

View File

@ -6,7 +6,7 @@ import { useGroup } from "../../contexts/GroupContext";
import useResponsiveLayout from "../../hooks/useResponsiveLayout"; import useResponsiveLayout from "../../hooks/useResponsiveLayout";
function TilesContainer({ columns, children }) { function TilesContainer({ children }) {
const { onGroupSelect } = useGroup(); const { onGroupSelect } = useGroup();
const layout = useResponsiveLayout(); const layout = useResponsiveLayout();
@ -31,7 +31,7 @@ function TilesContainer({ columns, children }) {
overflow: "hidden", overflow: "hidden",
}} }}
gap={2} gap={2}
columns={`repeat(${columns}, 1fr)`} columns={`repeat(${layout.tileGridColumns}, 1fr)`}
> >
{children} {children}
</Grid> </Grid>

View File

@ -6,9 +6,13 @@ import SimpleBar from "simplebar-react";
import { useGroup } from "../../contexts/GroupContext"; import { useGroup } from "../../contexts/GroupContext";
function TilesOverlay({ columns, children }) { import useResponsiveLayout from "../../hooks/useResponsiveLayout";
function TilesOverlay({ children }) {
const { openGroupId, onGroupClose, onGroupSelect } = useGroup(); const { openGroupId, onGroupClose, onGroupSelect } = useGroup();
const layout = useResponsiveLayout();
const openAnimation = useSpring({ const openAnimation = useSpring({
opacity: openGroupId ? 1 : 0, opacity: openGroupId ? 1 : 0,
transform: openGroupId ? "scale(1)" : "scale(0.99)", transform: openGroupId ? "scale(1)" : "scale(0.99)",
@ -88,7 +92,7 @@ function TilesOverlay({ columns, children }) {
overflow: "hidden", overflow: "hidden",
}} }}
gap={2} gap={2}
columns={`repeat(${columns}, 1fr)`} columns={`repeat(${layout.groupGridColumns}, 1fr)`}
px={3} px={3}
> >
{children} {children}

View File

@ -9,7 +9,7 @@ import { getGroupItems } from "../../helpers/group";
import { useGroup } from "../../contexts/GroupContext"; import { useGroup } from "../../contexts/GroupContext";
function TokenTiles({ tokens, onTokenEdit, subgroup, columns }) { function TokenTiles({ tokens, onTokenEdit, subgroup }) {
const { const {
groups, groups,
selectedGroupIds, selectedGroupIds,
@ -71,7 +71,6 @@ function TokenTiles({ tokens, onTokenEdit, subgroup, columns }) {
onTileSelect={onGroupSelect} onTileSelect={onGroupSelect}
disableGrouping={subgroup} disableGrouping={subgroup}
openGroupId={openGroupId} openGroupId={openGroupId}
columns={columns}
/> />
); );
} }

View File

@ -99,28 +99,28 @@ export function combineGroups(a, b) {
} }
/** /**
* Immutably move group at indices `from` into index `to` * Immutably move group at indices `indices` into group at index `into`
* @param {Group[]} groups * @param {Group[]} groups
* @param {number} to * @param {number} into
* @param {number[]} from * @param {number[]} indices
* @returns {Group[]} * @returns {Group[]}
*/ */
export function moveGroups(groups, to, from) { export function moveGroupsInto(groups, into, indices) {
const newGroups = cloneDeep(groups); const newGroups = cloneDeep(groups);
const toGroup = newGroups[to]; const intoGroup = newGroups[into];
let fromGroups = []; let fromGroups = [];
for (let i of from) { for (let i of indices) {
fromGroups.push(newGroups[i]); fromGroups.push(newGroups[i]);
} }
let combined = toGroup; let combined = intoGroup;
for (let fromGroup of fromGroups) { for (let fromGroup of fromGroups) {
combined = combineGroups(combined, fromGroup); combined = combineGroups(combined, fromGroup);
} }
// Replace and remove old groups // Replace and remove old groups
newGroups[to] = combined; newGroups[into] = combined;
for (let fromGroup of fromGroups) { for (let fromGroup of fromGroups) {
const i = newGroups.findIndex((group) => group.id === fromGroup.id); const i = newGroups.findIndex((group) => group.id === fromGroup.id);
newGroups.splice(i, 1); newGroups.splice(i, 1);
@ -129,6 +129,33 @@ export function moveGroups(groups, to, from) {
return newGroups; return newGroups;
} }
/**
* Immutably move group at indices `indices` to index `to`
* @param {Group[]} groups
* @param {number} into
* @param {number[]} indices
* @returns {Group[]}
*/
export function moveGroups(groups, to, indices) {
const newGroups = cloneDeep(groups);
let fromGroups = [];
for (let i of indices) {
fromGroups.push(newGroups[i]);
}
// Remove old groups
for (let fromGroup of fromGroups) {
const i = newGroups.findIndex((group) => group.id === fromGroup.id);
newGroups.splice(i, 1);
}
// Add back at new index
newGroups.splice(to, 0, ...fromGroups);
return newGroups;
}
/** /**
* Recursively find a group within a group array * Recursively find a group within a group array
* @param {Group[]} groups * @param {Group[]} groups

View File

@ -259,21 +259,19 @@ function SelectMapModal({
onGroupsSelect={setSelectedGroupIds} onGroupsSelect={setSelectedGroupIds}
disabled={!isOpen} disabled={!isOpen}
> >
<TilesContainer columns={layout.tileGridColumns}> <TilesContainer>
<MapTiles <MapTiles
maps={maps} maps={maps}
onMapEdit={() => setIsEditModalOpen(true)} onMapEdit={() => setIsEditModalOpen(true)}
onMapSelect={handleMapSelect} onMapSelect={handleMapSelect}
columns={layout.tileGridColumns}
/> />
</TilesContainer> </TilesContainer>
<TilesOverlay columns={layout.groupGridColumns}> <TilesOverlay>
<MapTiles <MapTiles
maps={maps} maps={maps}
onMapEdit={() => setIsEditModalOpen(true)} onMapEdit={() => setIsEditModalOpen(true)}
onMapSelect={handleMapSelect} onMapSelect={handleMapSelect}
subgroup subgroup
columns={layout.groupGridColumns}
/> />
</TilesOverlay> </TilesOverlay>
</GroupProvider> </GroupProvider>

View File

@ -200,19 +200,17 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
onGroupsSelect={setSelectedGroupIds} onGroupsSelect={setSelectedGroupIds}
disabled={!isOpen} disabled={!isOpen}
> >
<TilesContainer columns={layout.tileGridColumns}> <TilesContainer>
<TokenTiles <TokenTiles
tokens={tokens} tokens={tokens}
onTokenEdit={() => setIsEditModalOpen(true)} onTokenEdit={() => setIsEditModalOpen(true)}
columns={layout.tileGridColumns}
/> />
</TilesContainer> </TilesContainer>
<TilesOverlay columns={layout.groupGridColumns}> <TilesOverlay>
<TokenTiles <TokenTiles
tokens={tokens} tokens={tokens}
onTokenEdit={() => setIsEditModalOpen(true)} onTokenEdit={() => setIsEditModalOpen(true)}
subgroup subgroup
columns={layout.groupGridColumns}
/> />
</TilesOverlay> </TilesOverlay>
</GroupProvider> </GroupProvider>