Files
Crm-Frontend/src/components/dnd/FunnelDnd/FunnelDnd.tsx
2025-08-23 19:01:21 +04:00

193 lines
6.6 KiB
TypeScript

"use client";
import React, { ReactNode, RefObject } from "react";
import {
DndContext,
DragEndEvent,
DragOverEvent,
DragStartEvent,
} from "@dnd-kit/core";
import {
horizontalListSortingStrategy,
SortableContext,
} from "@dnd-kit/sortable";
import { FreeMode, Mousewheel, Pagination, Scrollbar } from "swiper/modules";
import { Swiper, SwiperRef, SwiperSlide } from "swiper/react";
import CreateStatusButton from "@/app/deals/components/shared/CreateStatusButton/CreateStatusButton";
import useDndSensors from "@/app/deals/hooks/useSensors";
import FunnelColumn from "@/components/dnd/FunnelDnd/FunnelColumn";
import FunnelOverlay from "@/components/dnd/FunnelDnd/FunnelOverlay";
import { BaseDraggable } from "@/components/dnd/types/types";
import useIsMobile from "@/hooks/utils/useIsMobile";
import SortableItem from "../SortableItem";
import classes from "./FunnelDnd.module.css";
type Props<TContainer, TItem> = {
containers: TContainer[];
items: TItem[];
onDragStart: (event: DragStartEvent) => void;
onDragOver: (event: DragOverEvent) => void;
onDragEnd: (event: DragEndEvent) => void;
swiperRef: RefObject<SwiperRef | null>;
renderContainer: (
container: TContainer,
children: ReactNode,
renderDraggable: () => ReactNode
) => ReactNode;
renderContainerHeader: (container: TContainer) => ReactNode;
renderContainerOverlay: (
container: TContainer,
children: ReactNode
) => ReactNode;
renderItem: (item: TItem) => ReactNode;
renderItemOverlay: (item: TItem) => ReactNode;
getContainerId: (container: TContainer) => string;
getItemsByContainer: (container: TContainer, items: TItem[]) => TItem[];
activeContainer: TContainer | null;
activeItem: TItem | null;
isCreatingContainerEnabled?: boolean;
disabledColumns?: boolean;
};
const FunnelDnd = <
TContainer extends BaseDraggable,
TItem extends BaseDraggable,
>({
containers,
items,
onDragStart,
onDragOver,
onDragEnd,
swiperRef,
renderContainer,
renderContainerHeader,
renderContainerOverlay,
renderItem,
renderItemOverlay,
getContainerId,
getItemsByContainer,
activeContainer,
activeItem,
isCreatingContainerEnabled = true,
disabledColumns = false,
}: Props<TContainer, TItem>) => {
const sensors = useDndSensors();
const isMobile = useIsMobile();
const renderContainers = () =>
containers.map(container => {
const containerItems = getItemsByContainer(container, items);
const containerId = getContainerId(container);
return (
<SwiperSlide
style={{ width: 250 }}
key={containerId}>
<SortableItem
key={containerId}
id={containerId}
disabled={disabledColumns}
renderItem={renderDraggable =>
renderContainer(
container,
<FunnelColumn
id={containerId}
items={containerItems}
renderItem={renderItem}
/>,
renderDraggable!
)
}
renderDraggable={() => renderContainerHeader(container)}
/>
</SwiperSlide>
);
});
const renderBody = () => {
if (isMobile) {
return (
<Swiper
ref={swiperRef}
onTouchStart={swiper => {
swiper.allowTouchMove = !activeItem;
}}
onTouchMove={swiper => {
swiper.allowTouchMove = !activeItem;
}}
className={classes["swiper-container"]}
slidesPerView={1.1}
style={{ paddingLeft: "10vw", paddingRight: "2vw" }}
modules={[Pagination]}
freeMode={{ enabled: false }}
pagination={{ enabled: true, clickable: true }}>
{renderContainers()}
{isCreatingContainerEnabled && (
<SwiperSlide>
<CreateStatusButton />
</SwiperSlide>
)}
</Swiper>
);
}
return (
<Swiper
ref={swiperRef}
className={classes["swiper-container"]}
modules={[Scrollbar, Mousewheel, FreeMode]}
spaceBetween={15}
slidesPerView={"auto"}
scrollbar={{ hide: false }}
mousewheel={{
enabled: true,
sensitivity: 0.2,
releaseOnEdges: true,
}}
freeMode={{ enabled: true }}
grabCursor>
{renderContainers()}
{isCreatingContainerEnabled && (
<SwiperSlide style={{ width: 50 }}>
<CreateStatusButton />
</SwiperSlide>
)}
</Swiper>
);
};
return (
<DndContext
sensors={sensors}
onDragStart={onDragStart}
onDragOver={onDragOver}
onDragEnd={onDragEnd}>
<SortableContext
items={containers.map(getContainerId)}
strategy={horizontalListSortingStrategy}>
{renderBody()}
<FunnelOverlay
activeContainer={activeContainer}
activeItem={activeItem}
renderContainer={container => {
const containerItems = getItemsByContainer(
container,
items
);
const containerId = getContainerId(container);
return renderContainerOverlay(
container,
<FunnelColumn
id={containerId}
items={containerItems}
renderItem={renderItem}
/>
);
}}
renderItem={renderItemOverlay}
/>
</SortableContext>
</DndContext>
);
};
export default FunnelDnd;