refactor: separation of shared components

This commit is contained in:
2025-08-06 11:39:44 +04:00
parent 9a780e99ae
commit 96c53380e0
18 changed files with 12 additions and 12 deletions

View File

@ -0,0 +1,130 @@
"use client";
import React, {
CSSProperties,
ReactNode,
useEffect,
useMemo,
useState,
} from "react";
import { Active, DndContext, DragEndEvent } from "@dnd-kit/core";
import { SortableContext } from "@dnd-kit/sortable";
import { LexoRank } from "lexorank";
import { Box, Group } from "@mantine/core";
import useDndSensors from "@/app/deals/hooks/useSensors";
import { SortableItem } from "@/components/dnd/SortableDnd/SortableItem";
import { SortableOverlay } from "@/components/dnd/SortableDnd/SortableOverlay";
import { getNewLexorank, sortByLexorank } from "@/utils/lexorank";
type BaseItem = {
id: number;
lexorank: string;
};
type Props<T extends BaseItem> = {
initialItems: T[];
renderItem: (item: T) => ReactNode;
onDragEnd: (itemId: number, newLexorank: string) => void;
onItemClick: (item: T) => void;
rowStyle?: CSSProperties;
itemStyle?: CSSProperties;
};
const SortableDnd = <T extends BaseItem>({
initialItems,
renderItem,
onDragEnd,
onItemClick,
rowStyle,
itemStyle,
}: Props<T>) => {
const [active, setActive] = useState<Active | null>(null);
const [items, setItems] = useState<T[]>([]);
const activeItem = useMemo(
() => initialItems.find(item => item.id === active?.id),
[active, items]
);
useEffect(() => {
setItems(sortByLexorank(initialItems));
}, [initialItems]);
const sensors = useDndSensors();
const onDragEndLocal = ({ active, over }: DragEndEvent) => {
if (over && active.id !== over?.id && activeItem) {
const overIndex: number = items.findIndex(
({ id }) => id === over.id
);
const activeIndex: number = items.findIndex(
({ id }) => id === activeItem.id
);
let leftIndex = overIndex;
let rightIndex = overIndex + 1;
if (overIndex < activeIndex) {
leftIndex = overIndex - 1;
rightIndex = overIndex;
}
const leftLexorank: LexoRank | null =
leftIndex >= 0
? LexoRank.parse(items[leftIndex].lexorank)
: null;
const rightLexorank: LexoRank | null =
rightIndex < items.length
? LexoRank.parse(items[rightIndex].lexorank)
: null;
const newLexorank = getNewLexorank(
leftLexorank,
rightLexorank
).toString();
items[activeIndex].lexorank = newLexorank;
onDragEnd(items[activeIndex].id, newLexorank);
const sortedItems = sortByLexorank(items);
setItems([...sortedItems]);
}
setActive(null);
};
return (
<DndContext
sensors={sensors}
onDragStart={({ active }) => setActive(active)}
onDragEnd={onDragEndLocal}
onDragCancel={() => setActive(null)}>
<SortableContext items={items}>
<Group
gap={0}
style={rowStyle}
role="application">
{items.map((item, index) => (
<Box
key={index}
onClick={e => {
e.preventDefault();
e.stopPropagation();
onItemClick(item);
}}>
<SortableItem
dragHandleStyle={{ cursor: "pointer" }}
itemStyle={itemStyle}
id={item.id}>
{renderItem(item)}
</SortableItem>
</Box>
))}
</Group>
</SortableContext>
<SortableOverlay>
<div style={{ cursor: "grabbing" }}>
{activeItem ? renderItem(activeItem) : null}
</div>
</SortableOverlay>
</DndContext>
);
};
export default SortableDnd;