极速微网站建设cms神马推广
安装拖拽库:
npm i @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
里面有一些组件:
-
DndContext : 拖拽功能最外面的容器
- onDragEnd : 拖拽完成之后的回调函数
- collisionDetection : 碰撞函数
- sensors : 定义传感器,默认的传感器只有PointerSensor,,如果想要监听键盘,或者手机端的touch事件,就需要配置别的传感器
-
SortableContext: 告诉dnd-kit,哪一个容器是可以被拖拽和放置的区域
- items :表示dnd-kit要跟踪哪些项
- strategy : 排序策略
-
const {attributes,listeners,setNodeRef,transform,transition} = useSortable({id});
setNodeRef
: 绑定每个拖拽的盒子
import React, {PropsWithChildren, useState} from 'react';
import logo from './logo.svg';
import './App.css';
import {closestCorners,DndContext,DragEndEvent, KeyboardSensor, PointerSensor, TouchSensor,useDraggable,useDroppable,useSensor,useSensors
} from "@dnd-kit/core";
import {arrayMove,SortableContext,sortableKeyboardCoordinates,useSortable,verticalListSortingStrategy
} from "@dnd-kit/sortable";
import {CSS} from "@dnd-kit/utilities"interface taskType {id:number,title:string
}function Task({id,title}:taskType){// 告诉 dnd-kit 这个是拖拽元素const {attributes,listeners,setNodeRef,transform,transition} = useSortable({id});const style = {transition,transform:CSS.Transform.toString(transform)}// dnd-kit控制元素外观的方式,,是创建一个style对象return (// 给盒子添加引用,,dnd-kit可以跟踪他<div className="task" ref={setNodeRef} {...attributes} {...listeners} style={style}><input type="checkbox" className="checkbox" />{title}</div>)
}type propType = {tasks:Array<taskType>
}
function Column({tasks}:propType){return (<div>{/* 告诉 dnd-kit 哪一个容器是可以被拖动和放置的区域 用SortableContext包裹===> items表示要跟踪哪些项 ===> strategy:排序策略*/}<SortableContext items={tasks} strategy={verticalListSortingStrategy}>{tasks.map(item=>(<Task id={item.id} title={item.title} key={item.id} />))}</SortableContext></div>)
}
type InputProp={onSubmit:(val:string)=>void
}
function Input({onSubmit}:InputProp){const [inputValue, setInputValue] = useState("")function handleSubmit(){if (!inputValue){return}onSubmit(inputValue)setInputValue("")}return (<div className="py-2 flex items-center justify-between"><input type="text" value={inputValue}placeholder="请输入task"onChange={(e)=>setInputValue(e.target.value)}/><button onClick={handleSubmit} className="px-2 rounded bg-yellow-200">添加</button></div>)
}function App() {const [tasks, setTasks] = useState([{id:1,title:"hehe"},{id:2,title:"222"},{id:3,title:"333"},])// 其他传感器,比如手机的touch,,键盘按键const sensors = useSensors(// 指针传感器useSensor(PointerSensor),// 默认的senseruseSensor(TouchSensor),useSensor(KeyboardSensor,{// enter+ 上下键coordinateGetter: sortableKeyboardCoordinates}))const [inputValue, setInputValue] = useState("")const addTask = (title:string)=>{setTasks(prevTasks=>{return [...prevTasks,{id:prevTasks.length+2,title: title}]})}return (<div><Input onSubmit={addTask} /><DndContext collisionDetection={closestCorners} onDragEnd={handleDragEnd} sensors={sensors}><Column tasks={tasks}/></DndContext></div>);function handleDragEnd(event: DragEndEvent) {const {active, over} = eventif (active.id === over?.id) returnsetTasks(prevTasks => {var activePos = prevTasks.findIndex(task=>task.id===active.id);var newPos = prevTasks.findIndex(task=>task.id===over?.id);return arrayMove(prevTasks,activePos,newPos)})}
}export default App;
封装拖拽组件,将拖拽的元素动态传入
import {JSX} from "react";
import {closestCenter,DndContext,DragEndEvent,KeyboardSensor,MouseSensor,PointerSensor,useSensor,useSensors
} from "@dnd-kit/core";
import {SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy} from "@dnd-kit/sortable";type PropsType = {children: JSX.Element | JSX.Element[], // 传入子组件,,拖拽的组件items: Array<{id:string,[key:string]:any}>, // 扩展key是任何string的属性onDragEnd:(oldIndex:number,newIndex:number)=>void // drag的回调,,传入组件不同,拖拽回调也不同
}function SortableContainer(props:PropsType){const {children,items,onDragEnd} = propsconst sensors = useSensors(// useSensor(PointerSensor),// useSensor(KeyboardSensor,{// coordinateGetter:sortableKeyboardCoordinates// }),useSensor(MouseSensor,{activationConstraint:{distance:8 // 8px,,移动超过8px才算拖拽}}))function handleDragEnd(event:DragEndEvent){// const {} = eventconst {active,over} = eventif (over == null){return}if (active.id === over.id){return;}var oldIndex = items.findIndex(c=>c.id===active.id);var newIndex = items.findIndex(c=>c.id===over.id);onDragEnd(oldIndex,newIndex)}return (<div><DndContext onDragEnd={handleDragEnd} sensors={sensors} collisionDetection={closestCenter}><SortableContext items={items} strategy={verticalListSortingStrategy}>{/* 怎么循环不管,,,直接引入子组件*/}{children}</SortableContext></DndContext></div>)
}export default SortableContainer
import {useSortable} from "@dnd-kit/sortable";
import {JSX} from "react";
import {CSS} from "@dnd-kit/utilities"
type PropsType={id:string,children:JSX.Element
}
function SortableItem({id,children}:PropsType){const {attributes,listeners,setNodeRef,transition,transform} = useSortable({id});const style = {transition,transform:CSS.Transform.toString(transform)}return (<div ref={setNodeRef} {...attributes} {...listeners} style={style}>{children}</div>)
}export default SortableItem
使用:
var componentListWithId = componentList.map(item => ({...item, id: item.fe_id}));function handleDragEnd(oldIndex: number, newIndex: number) {console.log(oldIndex, newIndex)dispatch(moveComponent({oldIndex,newIndex}))}return (<SortableContainer onDragEnd={handleDragEnd} items={componentListWithId}>{componentList.map(c => {const {fe_id, title, isHidden, isLocked,} = cconst titleDefaultClassName = styles.titleconst selectedClassName = styles.selectedconst titleClassName = classNames({[titleDefaultClassName]: true,[selectedClassName]: fe_id === selectedId})return (<SortableItem key={fe_id} id={fe_id}><div className={styles.wrapper}><div className={titleClassName} onClick={() => handleTitleClick(fe_id)}>{fe_id !== changingTitleId && title}{fe_id === changingTitleId && <Input value={title}onChange={(e) => changeTitle(e)}onPressEnter={() => setChangingTitleId("")}onBlur={() => setChangingTitleId("")}/>}</div><div className={styles.handler}><Space><ButtononClick={() => changeHidden(fe_id, !isHidden)}size="small"className={!isHidden ? styles.btn : ""} icon={<EyeInvisibleOutlined/>}type={isHidden ? "primary" : "default"} shape="circle"></Button><ButtononClick={() => changeLocked(fe_id)}size="small"className={!isLocked ? styles.btn : ""} icon={<LockOutlined/>}type={isLocked ? "primary" : "default"} shape="circle"></Button></Space></div></div></SortableItem>)})}</SortableContainer>)