"use client"; import React, { CSSProperties, ReactNode, useEffect, useMemo, useState, } from "react"; import { Active, DndContext, DragEndEvent } from "@dnd-kit/core"; import { restrictToHorizontalAxis } from "@dnd-kit/modifiers"; import { SortableContext } from "@dnd-kit/sortable"; import { LexoRank } from "lexorank"; import { Box, Flex } from "@mantine/core"; import useDndSensors from "@/app/deals/hooks/useSensors"; import { SortableOverlay } from "@/components/dnd/SortableDnd/SortableOverlay"; import SortableItem from "@/components/dnd/SortableItem"; import { getNewLexorank, sortByLexorank } from "@/utils/lexorank"; type BaseItem = { id: number; lexorank: string; }; type Props = { initialItems: T[]; renderItem: ( item: T, renderDraggable?: (item: T) => ReactNode ) => ReactNode; renderDraggable?: (item: T) => ReactNode; // if not passed - the whole item renders as draggable dragHandleStyle?: CSSProperties; onDragEnd: (itemId: number, newLexorank: string) => void; onItemClick?: (item: T) => void; containerStyle?: CSSProperties; vertical?: boolean; disabled?: boolean; }; const SortableDnd = ({ initialItems, renderItem, renderDraggable, dragHandleStyle, onDragEnd, onItemClick, containerStyle, vertical, disabled = false, }: Props) => { const [active, setActive] = useState(null); const [items, setItems] = useState([]); 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 ( setActive(active)} onDragEnd={onDragEndLocal} onDragCancel={() => setActive(null)}> {items.map((item, index) => ( { if (!onItemClick) return; e.stopPropagation(); onItemClick(item); }}> renderItem(item, renderDraggable) } renderDraggable={ renderDraggable ? () => renderDraggable(item) : undefined } dragHandleStyle={dragHandleStyle} /> ))} {activeItem ? renderItem(activeItem, renderDraggable) : null} ); }; export default SortableDnd;