import React, { FC, useState } from 'react'; import { DndContext, DragOverlay, DragEndEvent, DragMoveEvent, } from '@dnd-kit/core'; import dayjs from 'dayjs'; import { Draggable } from '../dndComponents/Draggable'; import { ECalendarMode, ECalendarView, IDndProviderProps, ISlot, } from '../types'; import { calculateNewDate, getMinutesDifferenceByStep } from '../utils'; import { oneHourHeight, oneHourWidth } from '../utils/timeUtils'; import { useMiddlewareContext } from '../hooks/useMiddlewareContext'; export const DndProvider: FC = ({ children, DragOverlayComponent, onChange, }) => { const { options, handleDrop } = useMiddlewareContext(); const { view = ECalendarView.VERTICAL, mode = ECalendarMode.DAILY } = options; const [draggableItem, setDraggableItem] = useState(); const [deltas, setDeltas] = useState(0); const [didMove, setDidMove] = useState(false); const [movingDelta, setMovingDelta] = useState(0); const debouncedDelta = movingDelta - deltas; const handleDragEnd = ({ delta, active, over }: DragEndEvent) => { if (!draggableItem) return; let startDate: string | undefined; let endDate: string | undefined; let overId = over?.id || 0; if (mode === ECalendarMode.DAILY) { const currentDelta = view === ECalendarView.VERTICAL ? delta.y : delta.x; const size = view === ECalendarView.VERTICAL ? oneHourHeight : oneHourWidth; let minutesDifference = Math.round( Number(currentDelta - deltas) * (60 / size) ); minutesDifference = getMinutesDifferenceByStep( draggableItem.startDate, minutesDifference, options.dndStepLength ); startDate = calculateNewDate(draggableItem.startDate, minutesDifference); endDate = calculateNewDate(draggableItem.endDate, minutesDifference); } else { const newDate = over?.id.split('_')[1]; const date = dayjs.utc(newDate); startDate = dayjs .utc(draggableItem.startDate) .set('date', date.get('date')) .set('month', date.get('month')) .set('year', date.get('year')) .toISOString(); endDate = dayjs .utc(draggableItem.endDate) .set('date', date.get('date')) .set('month', date.get('month')) .set('year', date.get('year')) .toISOString(); overId = over?.id.split('_')[0] || 0; } let result = true; if (onChange) { const onChangeData = { type: 'SLOT_DROP', data: { startDate, endDate, startTime: dayjs.utc(startDate).format('HH:mm'), endTime: dayjs.utc(endDate).format('HH:mm'), overId: String(overId), parentId: String(draggableItem.spaceId), slotId: String(draggableItem.id), }, }; const answer = onChange(onChangeData); result = typeof answer === 'boolean' ? answer : true; } if (result) { handleDrop( active.id, active.data.current?.weeklyView ? +active.data.current.parentId.split('_')[0] : active.data.current?.parentId, active.data.current?.weeklyView && over ? +over?.id.split('_')[0] || 0 : over?.id || 0, over?.id.split('_')[1] || startDate || '', endDate ); } setDidMove(false); setDraggableItem(undefined); }; const handleDragMove = ({ delta }: DragMoveEvent) => { const isVertical = view === ECalendarView.VERTICAL; if (!didMove && mode === ECalendarMode.DAILY) { setDeltas(isVertical ? delta.y : delta.x); setDidMove(true); } setMovingDelta(isVertical ? delta.y : delta.x); }; return ( setDraggableItem(active?.data?.current?.event) } onDragEnd={handleDragEnd} onDragMove={handleDragMove} onDragCancel={() => setDraggableItem(undefined)} > {children} {draggableItem && ( )} ); };