Files
grungnet/src/components/tile/SortableTiles.js

156 lines
4.6 KiB
JavaScript
Raw Normal View History

import React from "react";
2021-05-13 16:26:59 +10:00
import { createPortal } from "react-dom";
import { DragOverlay } from "@dnd-kit/core";
import { SortableContext } from "@dnd-kit/sortable";
2021-05-13 16:26:59 +10:00
import { animated, useSpring, config } from "react-spring";
import { Badge } from "theme-ui";
2021-05-13 16:26:59 +10:00
import { moveGroupsInto } from "../../helpers/group";
2021-05-25 15:47:52 +10:00
import { keyBy } from "../../helpers/shared";
import Vector2 from "../../helpers/Vector2";
import SortableTile from "./SortableTile";
import {
useTileDrag,
BASE_SORTABLE_ID,
GROUP_SORTABLE_ID,
GROUP_ID_PREFIX,
} from "../../contexts/TileDragContext";
import { useGroup } from "../../contexts/GroupContext";
function SortableTiles({ renderTile, subgroup }) {
const { dragId, overId } = useTileDrag();
const {
groups: allGroups,
selectedGroupIds: allSelectedIds,
openGroupId,
openGroupItems,
} = useGroup();
const sortableId = subgroup ? GROUP_SORTABLE_ID : BASE_SORTABLE_ID;
const groups = subgroup ? openGroupItems : allGroups;
// Only populate selected groups if needed
let selectedGroupIds = [];
if ((subgroup && openGroupId) || (!subgroup && !openGroupId)) {
selectedGroupIds = allSelectedIds;
2021-05-13 16:26:59 +10:00
}
const disableSorting = openGroupId && !subgroup;
const disableGrouping = subgroup || disableSorting;
2021-05-13 16:26:59 +10:00
const dragBounce = useSpring({
transform: !!dragId ? "scale(0.9)" : "scale(1)",
2021-05-13 16:26:59 +10:00
config: config.wobbly,
position: "relative",
2021-05-13 16:26:59 +10:00
});
const overGroupId =
overId && overId.startsWith(GROUP_ID_PREFIX) && overId.slice(9);
2021-05-25 15:47:52 +10:00
function renderSortableGroup(group, selectedGroups) {
if (overGroupId === group.id && dragId && group.id !== dragId) {
// If dragging over a group render a preview of that group
const previewGroup = moveGroupsInto(
2021-05-25 15:47:52 +10:00
[group, ...selectedGroups],
0,
selectedGroups.map((_, i) => i + 1)
)[0];
return renderTile(previewGroup);
}
return renderTile(group);
}
2021-05-25 15:47:52 +10:00
function renderDragOverlays() {
let selectedIndices = selectedGroupIds.map((groupId) =>
groups.findIndex((group) => group.id === groupId)
);
const activeIndex = groups.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) => groups[index]);
return selectedGroups.map((group, index) => (
<DragOverlay dropAnimation={null} key={group.id}>
2021-05-25 15:47:52 +10:00
<div
style={{
transform: `translate(${coords[index].x}%, ${coords[index].y}%)`,
2021-05-25 15:47:52 +10:00
}}
>
<animated.div style={dragBounce}>
{renderTile(group)}
{index === selectedIndices.length - 1 &&
selectedGroupIds.length > 1 && (
<Badge
sx={{
position: "absolute",
top: 0,
right: 0,
transform: "translate(25%, -25%)",
}}
>
{selectedGroupIds.length}
</Badge>
)}
2021-05-25 15:47:52 +10:00
</animated.div>
</div>
</DragOverlay>
));
}
function renderTiles() {
const groupsByIds = keyBy(groups, "id");
const selectedGroupIdsSet = new Set(selectedGroupIds);
let selectedGroups = [];
let hasSelectedContainerGroup = false;
for (let groupId of selectedGroupIds) {
const group = groupsByIds[groupId];
if (group) {
selectedGroups.push(group);
if (group.type === "group") {
hasSelectedContainerGroup = true;
}
}
}
return groups.map((group) => {
const isDragging = dragId && selectedGroupIdsSet.has(group.id);
const disableTileGrouping =
disableGrouping || isDragging || hasSelectedContainerGroup;
return (
<SortableTile
id={group.id}
key={group.id}
disableGrouping={disableTileGrouping}
disableSorting={disableSorting}
2021-05-25 15:47:52 +10:00
hidden={group.id === openGroupId}
isDragging={isDragging}
>
{renderSortableGroup(group, selectedGroups)}
</SortableTile>
);
});
}
2021-05-13 16:26:59 +10:00
return (
<SortableContext items={groups} id={sortableId}>
{renderTiles()}
{createPortal(dragId && renderDragOverlays(), document.body)}
</SortableContext>
2021-05-13 16:26:59 +10:00
);
}
export default SortableTiles;