Download vetcopilot-app/src/hooks/useDragSort.ts from WWsCa/VetNet-LCPS: direct link, hf CLI and curl.
- Browser
- Download file 3.93 kB
-
https://huggingface.co/WWsCa/VetNet-LCPS/resolve/main/vetcopilot-app/src/hooks/useDragSort.ts
- Command line
-
hf download hf://WWsCa/VetNet-LCPS/vetcopilot-app/src/hooks/useDragSort.ts
-
curl -L -o useDragSort.ts https://huggingface.co/WWsCa/VetNet-LCPS/resolve/main/vetcopilot-app/src/hooks/useDragSort.ts
3.93 kB
| /** | |
| * useDragSort Hook | |
| * | |
| * 列表项拖拽排序,使用 HTML5 Drag and Drop API。 | |
| * 触摸设备通过 emulating touch events 支持拖拽(简单模拟,不支持复杂的多点触控排序)。 | |
| * | |
| * 用法: | |
| * const { draggedIndex, dropTargetIndex, dragHandlers } = useDragSort({ | |
| * items, | |
| * onReorder: (fromIndex, toIndex) => reorder(fromIndex, toIndex), | |
| * }) | |
| * | |
| * return items.map((item, index) => ( | |
| * <div key={item.id} {...dragHandlers(index)}> | |
| * {item.name} | |
| * </div> | |
| * )) | |
| */ | |
| import { useState, useCallback, useRef } from 'react' | |
| // ================================================================ | |
| // 类型定义 | |
| // ================================================================ | |
| interface UseDragSortOptions { | |
| /** 重排序回调 */ | |
| onReorder: (fromIndex: number, toIndex: number) => void | |
| /** 拖拽句柄的类名(可选),如果设置则仅在该元素上触发拖拽 */ | |
| dragHandleClass?: string | |
| /** 是否启用,默认 true */ | |
| enabled?: boolean | |
| } | |
| interface DragHandlers { | |
| /** 拖拽元素的 ref */ | |
| draggable: boolean | |
| onDragStart: (e: React.DragEvent) => void | |
| onDragOver: (e: React.DragEvent) => void | |
| onDragEnter: (e: React.DragEvent) => void | |
| onDragEnd: (e: React.DragEvent) => void | |
| /** 元素在当前列表中的样式(拖拽中时显示占位效果) */ | |
| style: React.CSSProperties | |
| } | |
| interface UseDragSortReturn { | |
| /** 当前被拖拽项的索引 */ | |
| draggedIndex: number | null | |
| /** 拖拽目标位置的索引 */ | |
| dropTargetIndex: number | null | |
| /** 获取指定索引项的拖拽事件处理器 */ | |
| dragHandlers: (index: number) => DragHandlers | |
| } | |
| // ================================================================ | |
| // Hook | |
| // ================================================================ | |
| export function useDragSort(options: UseDragSortOptions): UseDragSortReturn { | |
| const { onReorder, enabled = true } = options | |
| const [draggedIndex, setDraggedIndex] = useState<number | null>(null) | |
| const [dropTargetIndex, setDropTargetIndex] = useState<number | null>(null) | |
| const onReorderRef = useRef(onReorder) | |
| onReorderRef.current = onReorder | |
| const handleDragStart = useCallback( | |
| (index: number) => (_e: React.DragEvent) => { | |
| if (!enabled) return | |
| setDraggedIndex(index) | |
| setDropTargetIndex(null) | |
| }, | |
| [enabled] | |
| ) | |
| const handleDragOver = useCallback( | |
| (_index: number) => (e: React.DragEvent) => { | |
| if (!enabled) return | |
| e.preventDefault() | |
| e.dataTransfer.dropEffect = 'move' | |
| }, | |
| [enabled] | |
| ) | |
| const handleDragEnter = useCallback( | |
| (index: number) => (_e: React.DragEvent) => { | |
| if (!enabled) return | |
| setDropTargetIndex(index) | |
| }, | |
| [enabled] | |
| ) | |
| const handleDragEnd = useCallback(() => { | |
| if (!enabled) return | |
| if (draggedIndex !== null && dropTargetIndex !== null && draggedIndex !== dropTargetIndex) { | |
| onReorderRef.current(draggedIndex, dropTargetIndex) | |
| } | |
| setDraggedIndex(null) | |
| setDropTargetIndex(null) | |
| }, [enabled, draggedIndex, dropTargetIndex]) | |
| const dragHandlers = useCallback( | |
| (index: number): DragHandlers => { | |
| const isDragging = draggedIndex === index | |
| const isOver = dropTargetIndex === index && draggedIndex !== null && draggedIndex !== index | |
| return { | |
| draggable: enabled, | |
| onDragStart: handleDragStart(index), | |
| onDragOver: handleDragOver(index), | |
| onDragEnter: handleDragEnter(index), | |
| onDragEnd: handleDragEnd, | |
| style: { | |
| opacity: isDragging ? 0.4 : 1, | |
| ...(isOver ? { borderTop: '2px solid #3b82f6' } : {}), | |
| cursor: enabled ? 'grab' : 'default', | |
| transition: 'opacity 0.15s ease', | |
| }, | |
| } | |
| }, | |
| [enabled, draggedIndex, dropTargetIndex, handleDragStart, handleDragOver, handleDragEnter, handleDragEnd] | |
| ) | |
| return { draggedIndex, dropTargetIndex, dragHandlers } | |
| } | |