🔧 Refactored the DnD grid scaffold a bit
This commit is contained in:
@@ -21,24 +21,24 @@ import dayjs from "dayjs";
|
|||||||
const prettyBytes = require("pretty-bytes");
|
const prettyBytes = require("pretty-bytes");
|
||||||
// https://codesandbox.io/s/dndkit-sortable-image-grid-py6ve?file=/src/Grid.jsx
|
// https://codesandbox.io/s/dndkit-sortable-image-grid-py6ve?file=/src/Grid.jsx
|
||||||
import {
|
import {
|
||||||
closestCenter,
|
|
||||||
DndContext,
|
DndContext,
|
||||||
DragOverlay,
|
closestCenter,
|
||||||
KeyboardSensor,
|
MouseSensor,
|
||||||
PointerSensor,
|
|
||||||
TouchSensor,
|
TouchSensor,
|
||||||
|
DragOverlay,
|
||||||
useSensor,
|
useSensor,
|
||||||
useSensors,
|
useSensors,
|
||||||
} from "@dnd-kit/core";
|
} from "@dnd-kit/core";
|
||||||
import {
|
import {
|
||||||
arrayMove,
|
arrayMove,
|
||||||
SortableContext,
|
SortableContext,
|
||||||
sortableKeyboardCoordinates,
|
rectSortingStrategy,
|
||||||
verticalListSortingStrategy,
|
|
||||||
} from "@dnd-kit/sortable";
|
} from "@dnd-kit/sortable";
|
||||||
|
|
||||||
import { SortableItem } from "./SortableItem";
|
import { Grid } from "./Grid";
|
||||||
import { Item } from "./Item";
|
import { SortableCover } from "./SortableCover";
|
||||||
|
import { Cover } from "./Cover";
|
||||||
|
import photos from "./photos.json";
|
||||||
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
@@ -62,15 +62,9 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
|
|||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [slidingPanelContentId, setSlidingPanelContentId] = useState("");
|
const [slidingPanelContentId, setSlidingPanelContentId] = useState("");
|
||||||
|
|
||||||
|
const [items, setItems] = useState(photos);
|
||||||
const [activeId, setActiveId] = useState(null);
|
const [activeId, setActiveId] = useState(null);
|
||||||
const [items, setItems] = useState(["1", "2", "3"]);
|
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor));
|
||||||
const sensors = useSensors(
|
|
||||||
useSensor(PointerSensor),
|
|
||||||
useSensor(KeyboardSensor, {
|
|
||||||
coordinateGetter: sortableKeyboardCoordinates,
|
|
||||||
}),
|
|
||||||
useSensor(TouchSensor),
|
|
||||||
);
|
|
||||||
|
|
||||||
const comicVineSearchResults = useSelector(
|
const comicVineSearchResults = useSelector(
|
||||||
(state: RootState) => state.comicInfo.searchResults,
|
(state: RootState) => state.comicInfo.searchResults,
|
||||||
@@ -165,9 +159,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function handleDragStart(event) {
|
function handleDragStart(event) {
|
||||||
const { active } = event;
|
setActiveId(event.active.id);
|
||||||
|
|
||||||
setActiveId(active.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDragEnd(event) {
|
function handleDragEnd(event) {
|
||||||
@@ -181,7 +173,11 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
|
|||||||
return arrayMove(items, oldIndex, newIndex);
|
return arrayMove(items, oldIndex, newIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
console.log(items);
|
||||||
|
setActiveId(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragCancel() {
|
||||||
setActiveId(null);
|
setActiveId(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +262,31 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
|
|||||||
id: 2,
|
id: 2,
|
||||||
icon: <i className="fas fa-file-archive"></i>,
|
icon: <i className="fas fa-file-archive"></i>,
|
||||||
name: "Archive Operations",
|
name: "Archive Operations",
|
||||||
content: <div key={2}></div>,
|
content: (
|
||||||
|
<div key={2}>
|
||||||
|
<DndContext
|
||||||
|
sensors={sensors}
|
||||||
|
collisionDetection={closestCenter}
|
||||||
|
onDragStart={handleDragStart}
|
||||||
|
onDragEnd={handleDragEnd}
|
||||||
|
onDragCancel={handleDragCancel}
|
||||||
|
>
|
||||||
|
<SortableContext items={items} strategy={rectSortingStrategy}>
|
||||||
|
<Grid columns={4}>
|
||||||
|
{items.map((url, index) => (
|
||||||
|
<SortableCover key={url} url={url} index={index} />
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</SortableContext>
|
||||||
|
|
||||||
|
<DragOverlay adjustScale={true}>
|
||||||
|
{activeId ? (
|
||||||
|
<Cover url={activeId} index={items.indexOf(activeId)} />
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
@@ -505,19 +525,6 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
|
|||||||
return (
|
return (
|
||||||
<section className="container">
|
<section className="container">
|
||||||
<div className="section">
|
<div className="section">
|
||||||
<DndContext
|
|
||||||
sensors={sensors}
|
|
||||||
collisionDetection={closestCenter}
|
|
||||||
onDragStart={handleDragStart}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
>
|
|
||||||
<SortableContext items={items} strategy={verticalListSortingStrategy}>
|
|
||||||
{items.map((id) => (
|
|
||||||
<SortableItem key={id} id={id} />
|
|
||||||
))}
|
|
||||||
</SortableContext>
|
|
||||||
<DragOverlay>{activeId ? <Item id={activeId} /> : null}</DragOverlay>
|
|
||||||
</DndContext>
|
|
||||||
{!isNil(comicBookDetailData) && !isEmpty(comicBookDetailData) && (
|
{!isNil(comicBookDetailData) && !isEmpty(comicBookDetailData) && (
|
||||||
<>
|
<>
|
||||||
<h1 className="title">{comicBookTitle}</h1>
|
<h1 className="title">{comicBookTitle}</h1>
|
||||||
|
|||||||
23
src/client/components/Cover.tsx
Normal file
23
src/client/components/Cover.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React, { forwardRef } from "react";
|
||||||
|
|
||||||
|
export const Cover = forwardRef(
|
||||||
|
({ url, index, faded, style, ...props }, ref) => {
|
||||||
|
const inlineStyles = {
|
||||||
|
opacity: faded ? "0.2" : "1",
|
||||||
|
transformOrigin: "0 0",
|
||||||
|
height: index === 0 ? 410 : 200,
|
||||||
|
gridRowStart: index === 0 ? "span 2" : null,
|
||||||
|
gridColumnStart: index === 0 ? "span 2" : null,
|
||||||
|
backgroundImage: `url("${url}")`,
|
||||||
|
backgroundSize: "cover",
|
||||||
|
backgroundPosition: "center",
|
||||||
|
backgroundColor: "grey",
|
||||||
|
borderRadius: "10px",
|
||||||
|
...style,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <div ref={ref} style={inlineStyles} {...props} />;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Cover.displayName = "Cover";
|
||||||
16
src/client/components/Grid.tsx
Normal file
16
src/client/components/Grid.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export function Grid({ children, columns }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
||||||
|
gridGap: 10,
|
||||||
|
padding: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import React, { forwardRef } from "react";
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
export const Item = forwardRef(({ id, ...props }, ref) => {
|
|
||||||
return <div {...props} ref={ref}></div>;
|
|
||||||
});
|
|
||||||
32
src/client/components/SortableCover.tsx
Normal file
32
src/client/components/SortableCover.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
|
|
||||||
|
import { Cover } from "./Cover";
|
||||||
|
|
||||||
|
export const SortableCover = (props) => {
|
||||||
|
const sortable = useSortable({ id: props.url });
|
||||||
|
const {
|
||||||
|
attributes,
|
||||||
|
listeners,
|
||||||
|
isDragging,
|
||||||
|
setNodeRef,
|
||||||
|
transform,
|
||||||
|
transition,
|
||||||
|
} = sortable;
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
transform: CSS.Transform.toString(transform),
|
||||||
|
transition,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Cover
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
{...props}
|
||||||
|
{...attributes}
|
||||||
|
{...listeners}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { useSortable } from "@dnd-kit/sortable";
|
|
||||||
import { CSS } from "@dnd-kit/utilities";
|
|
||||||
|
|
||||||
import { Item } from "./Item";
|
|
||||||
|
|
||||||
export function SortableItem(props) {
|
|
||||||
const { attributes, listeners, setNodeRef, transform, transition } =
|
|
||||||
useSortable({ id: props.id });
|
|
||||||
|
|
||||||
const backgroundColor =
|
|
||||||
props.id == 1 ? "Orange" : props.id == 2 ? "pink" : "gray";
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
transform: CSS.Transform.toString(transform),
|
|
||||||
transition,
|
|
||||||
height: 100,
|
|
||||||
width: 100,
|
|
||||||
border: "2px solid black",
|
|
||||||
backgroundColor,
|
|
||||||
borderRadius: 10,
|
|
||||||
touchAction: "none",
|
|
||||||
margin: 20,
|
|
||||||
};
|
|
||||||
|
|
||||||
return <Item ref={setNodeRef} style={style} {...attributes} {...listeners} />;
|
|
||||||
}
|
|
||||||
11
src/client/components/photos.json
Normal file
11
src/client/components/photos.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
"https://source.unsplash.com/WLUHO9A_xik/900x900",
|
||||||
|
"https://source.unsplash.com/R4K8S77qtwI/900x900",
|
||||||
|
"https://source.unsplash.com/jJGc21mEh8Q/900x900",
|
||||||
|
"https://source.unsplash.com/omNxg6JP6Fo/900x900",
|
||||||
|
"https://source.unsplash.com/-M1gkucIqkQ/900x900",
|
||||||
|
"https://source.unsplash.com/czOuPVsfHDw/900x900",
|
||||||
|
"https://source.unsplash.com/-vr0gMUM6Fk/900x900",
|
||||||
|
"https://source.unsplash.com/TsMuMM_qVec/900x900"
|
||||||
|
]
|
||||||
|
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"outDir": "./dist/",
|
"outDir": "./dist/",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
|
|||||||
Reference in New Issue
Block a user