2021-05-13 16:26:59 +10:00
|
|
|
import React, { useState } from "react";
|
|
|
|
|
import { createPortal } from "react-dom";
|
|
|
|
|
import {
|
|
|
|
|
DndContext,
|
|
|
|
|
DragOverlay,
|
|
|
|
|
MouseSensor,
|
|
|
|
|
TouchSensor,
|
|
|
|
|
useSensor,
|
|
|
|
|
useSensors,
|
2021-05-21 15:12:25 +10:00
|
|
|
closestCenter,
|
2021-05-13 16:26:59 +10:00
|
|
|
} from "@dnd-kit/core";
|
|
|
|
|
import { SortableContext, arrayMove } from "@dnd-kit/sortable";
|
|
|
|
|
import { animated, useSpring, config } from "react-spring";
|
2021-05-24 13:34:21 +10:00
|
|
|
import { Grid } from "theme-ui";
|
2021-05-13 16:26:59 +10:00
|
|
|
|
2021-05-24 15:17:23 +10:00
|
|
|
import { combineGroups, moveGroups } from "../../helpers/group";
|
2021-05-21 15:12:25 +10:00
|
|
|
|
2021-05-24 13:34:21 +10:00
|
|
|
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
|
|
|
|
|
2021-05-21 15:12:25 +10:00
|
|
|
import SortableTile from "./SortableTile";
|
|
|
|
|
|
2021-05-21 16:12:09 +10:00
|
|
|
function SortableTiles({
|
|
|
|
|
groups,
|
|
|
|
|
onGroupChange,
|
|
|
|
|
renderTile,
|
|
|
|
|
onTileSelect,
|
2021-05-24 13:34:21 +10:00
|
|
|
disableGrouping,
|
2021-05-21 16:12:09 +10:00
|
|
|
}) {
|
2021-05-24 13:34:21 +10:00
|
|
|
const layout = useResponsiveLayout();
|
|
|
|
|
|
2021-05-13 16:26:59 +10:00
|
|
|
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();
|
2021-05-21 15:12:25 +10:00
|
|
|
const [overId, setOverId] = useState();
|
2021-05-13 16:26:59 +10:00
|
|
|
|
2021-05-21 15:12:25 +10:00
|
|
|
function handleDragStart({ active, over }) {
|
2021-05-13 16:26:59 +10:00
|
|
|
setDragId(active.id);
|
2021-05-21 15:12:25 +10:00
|
|
|
setOverId(over?.id);
|
2021-05-24 13:34:21 +10:00
|
|
|
onTileSelect(active.id);
|
2021-05-21 15:12:25 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleDragMove({ over }) {
|
|
|
|
|
setOverId(over?.id);
|
2021-05-13 16:26:59 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleDragEnd({ active, over }) {
|
|
|
|
|
setDragId();
|
2021-05-21 15:12:25 +10:00
|
|
|
setOverId();
|
|
|
|
|
if (!active || !over) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (over.id.startsWith("__group__")) {
|
2021-05-21 16:12:09 +10:00
|
|
|
const overId = over.id.slice(9);
|
|
|
|
|
if (overId === active.id) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const activeGroupIndex = groups.findIndex(
|
|
|
|
|
(group) => group.id === active.id
|
|
|
|
|
);
|
|
|
|
|
const overGroupIndex = groups.findIndex((group) => group.id === overId);
|
|
|
|
|
onGroupChange(moveGroups(groups, overGroupIndex, activeGroupIndex));
|
|
|
|
|
onTileSelect();
|
|
|
|
|
} else if (active.id !== over.id) {
|
2021-05-14 18:02:50 +10:00
|
|
|
const oldIndex = groups.findIndex((group) => group.id === active.id);
|
|
|
|
|
const newIndex = groups.findIndex((group) => group.id === over.id);
|
2021-05-13 16:26:59 +10:00
|
|
|
onGroupChange(arrayMove(groups, oldIndex, newIndex));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const dragBounce = useSpring({
|
2021-05-21 15:12:25 +10:00
|
|
|
transform: !!dragId ? "scale(0.9)" : "scale(1)",
|
2021-05-13 16:26:59 +10:00
|
|
|
config: config.wobbly,
|
|
|
|
|
});
|
|
|
|
|
|
2021-05-21 15:12:25 +10:00
|
|
|
const overGroupId =
|
|
|
|
|
overId && overId.startsWith("__group__") && overId.slice(9);
|
|
|
|
|
|
2021-05-24 13:34:21 +10:00
|
|
|
function renderSortableGroup(group) {
|
|
|
|
|
if (overGroupId === group.id && dragId && group.id !== dragId) {
|
|
|
|
|
// If dragging over a group render a preview of that group
|
|
|
|
|
return renderTile(
|
|
|
|
|
combineGroups(
|
|
|
|
|
group,
|
|
|
|
|
groups.find((group) => group.id === dragId)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return renderTile(group);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-13 16:26:59 +10:00
|
|
|
return (
|
|
|
|
|
<DndContext
|
|
|
|
|
onDragStart={handleDragStart}
|
2021-05-21 15:12:25 +10:00
|
|
|
onDragMove={handleDragMove}
|
2021-05-13 16:26:59 +10:00
|
|
|
onDragEnd={handleDragEnd}
|
|
|
|
|
sensors={sensors}
|
2021-05-21 15:12:25 +10:00
|
|
|
collisionDetection={closestCenter}
|
2021-05-13 16:26:59 +10:00
|
|
|
>
|
|
|
|
|
<SortableContext items={groups}>
|
2021-05-24 13:34:21 +10:00
|
|
|
<Grid
|
|
|
|
|
p={3}
|
|
|
|
|
pb={4}
|
|
|
|
|
sx={{
|
|
|
|
|
borderRadius: "4px",
|
|
|
|
|
overflow: "hidden",
|
|
|
|
|
}}
|
|
|
|
|
gap={2}
|
|
|
|
|
columns={layout.gridTemplate}
|
|
|
|
|
onClick={() => onTileSelect()}
|
|
|
|
|
>
|
|
|
|
|
{groups.map((group) => (
|
|
|
|
|
<SortableTile
|
|
|
|
|
id={group.id}
|
|
|
|
|
key={group.id}
|
|
|
|
|
disableGrouping={disableGrouping}
|
|
|
|
|
>
|
|
|
|
|
{renderSortableGroup(group)}
|
2021-05-21 15:12:25 +10:00
|
|
|
</SortableTile>
|
2021-05-24 13:34:21 +10:00
|
|
|
))}
|
|
|
|
|
</Grid>
|
2021-05-13 16:26:59 +10:00
|
|
|
{createPortal(
|
|
|
|
|
<DragOverlay dropAnimation={null}>
|
|
|
|
|
{dragId && (
|
|
|
|
|
<animated.div style={dragBounce}>
|
2021-05-14 18:02:50 +10:00
|
|
|
{renderTile(groups.find((group) => group.id === dragId))}
|
2021-05-13 16:26:59 +10:00
|
|
|
</animated.div>
|
|
|
|
|
)}
|
|
|
|
|
</DragOverlay>,
|
|
|
|
|
document.body
|
|
|
|
|
)}
|
|
|
|
|
</SortableContext>
|
|
|
|
|
</DndContext>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default SortableTiles;
|