当前位置: 首页 > news >正文

react拖拽库dnd-kit

安装拖拽库:

 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),// 默认的senser
        useSensor(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} = event

        if (active.id === over?.id) return

        setTasks(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} = props

    const sensors = useSensors(
        // useSensor(PointerSensor),
        // useSensor(KeyboardSensor,{
        //     coordinateGetter:sortableKeyboardCoordinates
        // }),
        useSensor(MouseSensor,{
            activationConstraint:{
                distance:8 // 8px,,移动超过8px才算拖拽
            }
        })
    )


    function handleDragEnd(event:DragEndEvent){
        // const {} = event

        const {active,over} = event
        if (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,} = c

                    const titleDefaultClassName = styles.title
                    const selectedClassName = styles.selected
                    const 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>
                                        <Button
                                            onClick={() => changeHidden(fe_id, !isHidden)}
                                            size="small"
                                            className={!isHidden ? styles.btn : ""} icon={<EyeInvisibleOutlined/>}
                                            type={isHidden ? "primary" : "default"} shape="circle"></Button>
                                        <Button
                                            onClick={() => changeLocked(fe_id)}
                                            size="small"
                                            className={!isLocked ? styles.btn : ""} icon={<LockOutlined/>}
                                            type={isLocked ? "primary" : "default"} shape="circle"></Button>
                                    </Space>

                                </div>
                            </div>
                        </SortableItem>

                    )
                })
            }
        </SortableContainer>
    )

相关文章:

  • 【jQuery】插件
  • 【C++初阶】----模板初阶
  • Flask登录页面后点击按钮在远程CentOS上自动执行一条命令
  • 极速版:栈的内存/局部变量表/堆的内存细分
  • 从 select 到 epoll:拆解 I/O 多路复用的演进与实战
  • Teleport场景及原理
  • Python给对象数组排序
  • J2EE框架技术第七章 反射机制与设计模式
  • 什么是大数据
  • J2EE框架技术 第十章 事务管理技术的应用
  • C语言常用的字符串函数
  • CS(载波监听)机制
  • 【考研】计算机网络
  • “屏幕“的实现_程序中如何将数据映射到硬件_C++实战
  • Linux的shell的前置任务
  • Java网络编程演进:从NIO到Netty的UDP实践全解析
  • Java HMAC加密/解密详解
  • 优化程序命名:提升专业感与用户体验
  • 《孟婆汤的SHA-256加密》
  • 51单片机的五类指令(一)——数据传送类指令
  • cdr 做网站页面/网络推广怎么做方案
  • 网站建设 收费明细/梅州网络推广
  • 如何在分类信息网站做推广/百度网站提交收录入口
  • 网上做预算有哪些网站/加盟教育培训哪个好
  • 企业网络安全设计方案/seo推广策略
  • 商标注册网站官网/怎么推广游戏叫别人玩