mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
163 lines
6.0 KiB
TypeScript
163 lines
6.0 KiB
TypeScript
import {
|
|
attachClosestEdge,
|
|
type Edge,
|
|
extractClosestEdge,
|
|
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
|
import {
|
|
BaseEventPayload,
|
|
CleanupFn,
|
|
ElementDragType,
|
|
} from '@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types';
|
|
import {
|
|
draggable,
|
|
dropTargetForElements,
|
|
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview';
|
|
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
|
|
import { useEffect, useRef, useState } from 'react';
|
|
import { createRoot } from 'react-dom/client';
|
|
|
|
import { DragPreview } from '/@/shared/components/drag-preview/drag-preview';
|
|
import { LibraryItem } from '/@/shared/types/domain-types';
|
|
import { dndUtils, DragData, DragOperation, DragTarget } from '/@/shared/types/drag-and-drop';
|
|
|
|
interface UseDraggableProps {
|
|
drag?: {
|
|
getId: () => string[];
|
|
getItem: () => unknown[];
|
|
itemType?: LibraryItem;
|
|
onDragStart?: () => void;
|
|
onDrop?: () => void;
|
|
onGenerateDragPreview?: (data: BaseEventPayload<ElementDragType>) => void;
|
|
operation: DragOperation[];
|
|
target: DragTarget | string;
|
|
};
|
|
drop?: {
|
|
canDrop: (args: { source: DragData }) => boolean;
|
|
getData: () => DragData;
|
|
onDrag: (args: { edge: Edge | null }) => void;
|
|
onDragLeave: () => void;
|
|
onDrop: (args: { edge: Edge | null; self: DragData; source: DragData }) => void;
|
|
};
|
|
isEnabled: boolean;
|
|
}
|
|
|
|
export const useDragDrop = <TElement extends HTMLElement>({
|
|
drag,
|
|
drop,
|
|
isEnabled,
|
|
}: UseDraggableProps) => {
|
|
const ref = useRef<null | TElement>(null);
|
|
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
const [isDraggedOver, setIsDraggedOver] = useState<Edge | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!ref.current || !isEnabled) return;
|
|
|
|
const functions: CleanupFn[] = [];
|
|
|
|
if (drag) {
|
|
functions.push(
|
|
draggable({
|
|
element: ref.current,
|
|
getInitialData: () => {
|
|
const id = drag.getId();
|
|
const item = drag.getItem();
|
|
|
|
const data = dndUtils.generateDragData({
|
|
id,
|
|
item,
|
|
itemType: drag.itemType,
|
|
operation: drag.operation,
|
|
type: drag.target,
|
|
});
|
|
return data;
|
|
},
|
|
onDragStart: () => {
|
|
setIsDragging(true);
|
|
drag.onDragStart?.();
|
|
},
|
|
onDrop: () => {
|
|
setIsDragging(false);
|
|
drag.onDrop?.();
|
|
},
|
|
onGenerateDragPreview: (data) => {
|
|
if (drag.onGenerateDragPreview) {
|
|
return drag.onGenerateDragPreview(data);
|
|
}
|
|
|
|
const dragData = dndUtils.generateDragData({
|
|
id: drag.getId(),
|
|
item: drag.getItem(),
|
|
itemType: drag.itemType,
|
|
operation: drag.operation,
|
|
type: drag.target,
|
|
}) as DragData;
|
|
|
|
disableNativeDragPreview({ nativeSetDragImage: data.nativeSetDragImage });
|
|
setCustomNativeDragPreview({
|
|
nativeSetDragImage: data.nativeSetDragImage,
|
|
render: ({ container }) => {
|
|
const root = createRoot(container);
|
|
root.render(<DragPreview data={dragData} />);
|
|
},
|
|
});
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
if (drop) {
|
|
functions.push(
|
|
dropTargetForElements({
|
|
canDrop: (args) => {
|
|
return (
|
|
drop.canDrop?.({ source: args.source.data as unknown as DragData }) ||
|
|
false
|
|
);
|
|
},
|
|
element: ref.current,
|
|
getData: (args) => {
|
|
const dropData = drop.getData();
|
|
|
|
const data = dndUtils.generateDragData(dropData);
|
|
|
|
return attachClosestEdge(data, {
|
|
allowedEdges: ['top', 'bottom'],
|
|
element: args.element,
|
|
input: args.input,
|
|
});
|
|
},
|
|
onDrag: (args) => {
|
|
const closestEdgeOfTarget: Edge | null = extractClosestEdge(args.self.data);
|
|
drop.onDrag?.({ edge: closestEdgeOfTarget });
|
|
setIsDraggedOver(closestEdgeOfTarget);
|
|
},
|
|
onDragLeave: () => {
|
|
setIsDraggedOver(null);
|
|
},
|
|
onDrop: (args) => {
|
|
const closestEdgeOfTarget: Edge | null = extractClosestEdge(args.self.data);
|
|
drop.onDrop?.({
|
|
edge: closestEdgeOfTarget,
|
|
self: args.self.data as unknown as DragData,
|
|
source: args.source.data as unknown as DragData,
|
|
});
|
|
setIsDraggedOver(null);
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
return combine(...functions);
|
|
}, [drag, drop, isDragging, isDraggedOver, isEnabled]);
|
|
|
|
return {
|
|
isDraggedOver,
|
|
isDragging,
|
|
ref,
|
|
};
|
|
};
|