Add support for group visual with sortable tiles
This commit is contained in:
@@ -4,11 +4,7 @@ import { useDroppable } from "@dnd-kit/core";
|
||||
function Droppable({ id, children, disabled }) {
|
||||
const { setNodeRef } = useDroppable({ id, disabled });
|
||||
|
||||
return (
|
||||
<div style={{ width: "100%", height: "100%" }} ref={setNodeRef}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
return <div ref={setNodeRef}>{children}</div>;
|
||||
}
|
||||
|
||||
Droppable.defaultProps = {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import React from "react";
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
|
||||
function Sortable({ id, children }) {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
isDragging,
|
||||
transform,
|
||||
transition,
|
||||
} = useSortable({ id });
|
||||
|
||||
const style = {
|
||||
cursor: "pointer",
|
||||
opacity: isDragging ? 0.25 : undefined,
|
||||
transform:
|
||||
transform && `translate3d(${transform.x}px, ${transform.y}px, 0px)`,
|
||||
zIndex: isDragging ? 100 : undefined,
|
||||
transition,
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={setNodeRef} style={style} {...listeners} {...attributes}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Sortable;
|
||||
@@ -0,0 +1,65 @@
|
||||
import React from "react";
|
||||
import { Box } from "theme-ui";
|
||||
import { useDroppable } from "@dnd-kit/core";
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
|
||||
function Sortable({ id, children, showDropGutter }) {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
isDragging,
|
||||
transition,
|
||||
setDroppableNodeRef,
|
||||
setDraggableNodeRef,
|
||||
} = useSortable({ id });
|
||||
const { setNodeRef: setGroupNodeRef } = useDroppable({
|
||||
id: `__group__${id}`,
|
||||
});
|
||||
|
||||
const dragStyle = {
|
||||
cursor: "pointer",
|
||||
opacity: isDragging ? 0.25 : undefined,
|
||||
zIndex: isDragging ? 100 : undefined,
|
||||
transition,
|
||||
};
|
||||
|
||||
// Sort div left aligned
|
||||
const sortDropStyle = {
|
||||
position: "absolute",
|
||||
left: "-5px",
|
||||
top: 0,
|
||||
width: "2px",
|
||||
height: "100%",
|
||||
borderRadius: "2px",
|
||||
};
|
||||
|
||||
// Group div center aligned
|
||||
const groupDropStyle = {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: "50%",
|
||||
width: "1px",
|
||||
height: "100%",
|
||||
};
|
||||
|
||||
return (
|
||||
<Box style={{ position: "relative" }}>
|
||||
<Box
|
||||
ref={setDraggableNodeRef}
|
||||
style={dragStyle}
|
||||
{...listeners}
|
||||
{...attributes}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
<Box
|
||||
ref={setDroppableNodeRef}
|
||||
style={sortDropStyle}
|
||||
bg={showDropGutter && "primary"}
|
||||
/>
|
||||
<Box ref={setGroupNodeRef} style={groupDropStyle} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Sortable;
|
||||
@@ -7,11 +7,16 @@ import {
|
||||
TouchSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
closestCenter,
|
||||
} from "@dnd-kit/core";
|
||||
import { SortableContext, arrayMove } from "@dnd-kit/sortable";
|
||||
import { animated, useSpring, config } from "react-spring";
|
||||
|
||||
function SortableTiles({ groups, onGroupChange, renderTile, children }) {
|
||||
import { combineGroups } from "../../helpers/select";
|
||||
|
||||
import SortableTile from "./SortableTile";
|
||||
|
||||
function SortableTiles({ groups, onGroupChange, renderTile, renderTiles }) {
|
||||
const mouseSensor = useSensor(MouseSensor, {
|
||||
activationConstraint: { delay: 250, tolerance: 5 },
|
||||
});
|
||||
@@ -22,14 +27,29 @@ function SortableTiles({ groups, onGroupChange, renderTile, children }) {
|
||||
const sensors = useSensors(mouseSensor, touchSensor);
|
||||
|
||||
const [dragId, setDragId] = useState();
|
||||
const [overId, setOverId] = useState();
|
||||
|
||||
function handleDragStart({ active }) {
|
||||
function handleDragStart({ active, over }) {
|
||||
setDragId(active.id);
|
||||
setOverId(over?.id);
|
||||
}
|
||||
|
||||
function handleDragMove({ over }) {
|
||||
setOverId(over?.id);
|
||||
}
|
||||
|
||||
function handleDragEnd({ active, over }) {
|
||||
setDragId();
|
||||
if (active && over && active.id !== over.id) {
|
||||
setOverId();
|
||||
if (!active || !over) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (over.id.startsWith("__group__")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (active.id !== over.id) {
|
||||
const oldIndex = groups.findIndex((group) => group.id === active.id);
|
||||
const newIndex = groups.findIndex((group) => group.id === over.id);
|
||||
onGroupChange(arrayMove(groups, oldIndex, newIndex));
|
||||
@@ -37,18 +57,41 @@ function SortableTiles({ groups, onGroupChange, renderTile, children }) {
|
||||
}
|
||||
|
||||
const dragBounce = useSpring({
|
||||
transform: !!dragId ? "scale(1.05)" : "scale(1)",
|
||||
transform: !!dragId ? "scale(0.9)" : "scale(1)",
|
||||
config: config.wobbly,
|
||||
});
|
||||
|
||||
const overGroupId =
|
||||
overId && overId.startsWith("__group__") && overId.slice(9);
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
onDragStart={handleDragStart}
|
||||
onDragMove={handleDragMove}
|
||||
onDragEnd={handleDragEnd}
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
>
|
||||
<SortableContext items={groups}>
|
||||
{children}
|
||||
{renderTiles(
|
||||
groups.map((group) => (
|
||||
<SortableTile
|
||||
id={group.id}
|
||||
key={group.id}
|
||||
showDropGutter={overId === group.id}
|
||||
>
|
||||
{dragId && overGroupId === group.id && group.id !== dragId
|
||||
? // If over a group render a preview of that group
|
||||
renderTile(
|
||||
combineGroups(
|
||||
group,
|
||||
groups.find((group) => group.id === dragId)
|
||||
)
|
||||
)
|
||||
: renderTile(group)}
|
||||
</SortableTile>
|
||||
))
|
||||
)}
|
||||
{createPortal(
|
||||
<DragOverlay dropAnimation={null}>
|
||||
{dragId && (
|
||||
|
||||
@@ -10,7 +10,6 @@ import MapTileGroup from "./MapTileGroup";
|
||||
import Link from "../Link";
|
||||
import FilterBar from "../FilterBar";
|
||||
|
||||
import Sortable from "../drag/Sortable";
|
||||
import SortableTiles from "../drag/SortableTiles";
|
||||
|
||||
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
||||
@@ -69,7 +68,7 @@ function MapTiles({
|
||||
setHasMapState(_hasMapState);
|
||||
}, [selectedGroupIds, maps, mapStates, groups]);
|
||||
|
||||
function groupToMapTile(group) {
|
||||
function renderTile(group) {
|
||||
if (group.type === "item") {
|
||||
const map = maps.find((map) => map.id === group.id);
|
||||
const isSelected = selectedGroupIds.includes(group.id);
|
||||
@@ -107,12 +106,8 @@ function MapTiles({
|
||||
|
||||
const multipleSelected = selectedGroupIds.length > 1;
|
||||
|
||||
return (
|
||||
<SortableTiles
|
||||
groups={groups}
|
||||
onGroupChange={onMapsGroup}
|
||||
renderTile={groupToMapTile}
|
||||
>
|
||||
function renderTiles(tiles) {
|
||||
return (
|
||||
<Box sx={{ position: "relative" }}>
|
||||
<FilterBar
|
||||
onFocus={() => onTileSelect()}
|
||||
@@ -142,11 +137,7 @@ function MapTiles({
|
||||
columns={layout.gridTemplate}
|
||||
onClick={() => onTileSelect()}
|
||||
>
|
||||
{groups.map((group) => (
|
||||
<Sortable id={group.id} key={group.id}>
|
||||
{groupToMapTile(group)}
|
||||
</Sortable>
|
||||
))}
|
||||
{tiles}
|
||||
</Grid>
|
||||
</SimpleBar>
|
||||
{databaseDisabled && (
|
||||
@@ -205,7 +196,16 @@ function MapTiles({
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
</SortableTiles>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SortableTiles
|
||||
groups={groups}
|
||||
onGroupChange={onMapsGroup}
|
||||
renderTile={renderTile}
|
||||
renderTiles={renderTiles}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import TokenTileGroup from "./TokenTileGroup";
|
||||
import Link from "../Link";
|
||||
import FilterBar from "../FilterBar";
|
||||
|
||||
import Sortable from "../drag/Sortable";
|
||||
import SortableTiles from "../drag/SortableTiles";
|
||||
|
||||
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
||||
@@ -49,7 +48,7 @@ function TokenTiles({
|
||||
setAllTokensVisisble(selectedTokens.every((token) => !token.hideInSidebar));
|
||||
}, [selectedGroupIds, tokens, groups]);
|
||||
|
||||
function groupToTokenTile(group) {
|
||||
function renderTile(group) {
|
||||
if (group.type === "item") {
|
||||
const token = tokens.find((token) => token.id === group.id);
|
||||
const isSelected = selectedGroupIds.includes(group.id);
|
||||
@@ -102,12 +101,8 @@ function TokenTiles({
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<SortableTiles
|
||||
groups={groups}
|
||||
onGroupChange={onTokensGroup}
|
||||
renderTile={groupToTokenTile}
|
||||
>
|
||||
function renderTiles(tiles) {
|
||||
return (
|
||||
<Box sx={{ position: "relative" }}>
|
||||
<FilterBar
|
||||
onFocus={() => onTileSelect()}
|
||||
@@ -136,11 +131,7 @@ function TokenTiles({
|
||||
columns={layout.gridTemplate}
|
||||
onClick={() => onTileSelect()}
|
||||
>
|
||||
{groups.map((group) => (
|
||||
<Sortable id={group.id} key={group.id}>
|
||||
{groupToTokenTile(group)}
|
||||
</Sortable>
|
||||
))}
|
||||
{tiles}
|
||||
</Grid>
|
||||
</SimpleBar>
|
||||
{databaseDisabled && (
|
||||
@@ -199,7 +190,16 @@ function TokenTiles({
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
</SortableTiles>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SortableTiles
|
||||
groups={groups}
|
||||
onGroupChange={onTokensGroup}
|
||||
renderTile={renderTile}
|
||||
renderTiles={renderTiles}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user