194 lines
6.7 KiB
TypeScript
194 lines
6.7 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, Pagination, Scrollbar } from "swiper/modules";
|
|
import { Swiper, SwiperRef, SwiperSlide } from "swiper/react";
|
|
import { Box } from "@mantine/core";
|
|
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,
|
|
index: number
|
|
) => 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, index) => {
|
|
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!,
|
|
index
|
|
)
|
|
}
|
|
renderDraggable={() => renderContainerHeader(container)}
|
|
/>
|
|
</SwiperSlide>
|
|
);
|
|
});
|
|
|
|
const renderBody = () => {
|
|
if (isMobile) {
|
|
return (
|
|
<Box>
|
|
<Swiper
|
|
ref={swiperRef}
|
|
onTouchStart={swiper => {
|
|
swiper.allowTouchMove = !activeItem;
|
|
}}
|
|
onTouchMove={swiper => {
|
|
swiper.allowTouchMove = !activeItem;
|
|
}}
|
|
className={classes["swiper-container"]}
|
|
slidesPerView={1.1}
|
|
spaceBetween={10}
|
|
style={{ paddingLeft: "10vw" }}
|
|
modules={[Pagination]}
|
|
freeMode={{ enabled: false }}
|
|
pagination={{ enabled: true, clickable: true }}>
|
|
{renderContainers()}
|
|
{isCreatingContainerEnabled && (
|
|
<SwiperSlide style={{ width: 250 }}>
|
|
<CreateStatusButton />
|
|
</SwiperSlide>
|
|
)}
|
|
</Swiper>
|
|
</Box>
|
|
);
|
|
}
|
|
return (
|
|
<Swiper
|
|
ref={swiperRef}
|
|
className={classes["swiper-container"]}
|
|
modules={[Scrollbar, FreeMode]}
|
|
spaceBetween={15}
|
|
slidesPerView={"auto"}
|
|
scrollbar={{ hide: false }}
|
|
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;
|