美文网首页
使用 react-beautiful-dnd 控制拖拽元素可以放

使用 react-beautiful-dnd 控制拖拽元素可以放

作者: VioletJack | 来源:发表于2020-07-18 16:53 被阅读0次

    在做看板的时候,会有某个元素只能拖拽到指定列的情况。比如有 todo、doing、done 三列状态,todo 只能往 doing 列拖拽,doing 只能往 done 拖拽。这个时候就需要控制 drag 元素是否能够 drop 了!

    上代码

    import React, { useState, useEffect } from 'react'
    import {
      DragDropContext,
      Droppable,
      Draggable,
      DropResult,
      DraggableProvided,
      DraggableStateSnapshot,
      DragUpdate,
    } from 'react-beautiful-dnd'
    import update from 'immutability-helper'
    import styles from './index.less'
    
    interface initialDataInferface {
      id: number
      name: string
      issues: {
        id: number
        name: string
      }[]
      acceptIds: number[]
    }
    
    interface ColumnProps {
      columnIndex: number
      activeColumn: initialDataInferface | null
      column: initialDataInferface
    }
    
    interface IssueProps {
      id: number
      issueIndex: number
      name: string
    }
    
    const InitialData: initialDataInferface[] = [
      {
        id: 100,
        name: 'todo',
        issues: [
          { id: 1, name: '吃饭' },
          { id: 2, name: '睡觉' },
          { id: 3, name: '打豆豆' },
        ],
        acceptIds: [200],
      },
      {
        id: 200,
        name: 'doing',
        issues: [
          { id: 4, name: '删库' },
          { id: 5, name: '跑路' },
        ],
        acceptIds: [300],
      },
      {
        id: 300,
        name: 'done',
        issues: [],
        acceptIds: [100, 200],
      },
    ]
    
    for (let i = 6; i < 100; i++) {
      InitialData[0].issues.push({ id: i, name: `uten${i}` })
    }
    
    const Issue = (props: IssueProps) => {
      const { id, issueIndex, name } = props
    
      return (
        <Draggable draggableId={`${id}`} index={issueIndex}>
          {(provided: DraggableProvided, snapshot: DraggableStateSnapshot) => (
            <div
              ref={provided.innerRef}
              className={snapshot.isDragging ? styles.issueDragging : styles.issue}
              {...provided.draggableProps}
              {...provided.dragHandleProps}
            >
              {name}
            </div>
          )}
        </Draggable>
      )
    }
    
    const Column = (props: ColumnProps) => {
      const { columnIndex, activeColumn, column } = props
      const { id, issues } = column
    
      return (
        <div className={styles.column}>
          <div className={styles.columnTitle}>
            {column.name}({column.issues.length})
          </div>
          <Droppable
            droppableId={`${columnIndex}`}
            mode='virtual'
            isDropDisabled={
              activeColumn
                ? !(activeColumn.acceptIds.includes(id) || id === activeColumn.id)
                : true
            }
          >
            {(provided, snapshot) => (
              <div
                ref={provided.innerRef}
                className={
                  snapshot.isDraggingOver
                    ? styles.columnContentActive
                    : styles.columnContent
                }
                {...provided.droppableProps}
              >
                {issues.map((issue, index) => (
                  <Issue
                    key={issue.id}
                    issueIndex={index}
                    id={issue.id}
                    name={issue.name}
                  />
                ))}
                {provided.placeholder}
              </div>
            )}
          </Droppable>
        </div>
      )
    }
    
    export default () => {
      const [data, setData] = useState(InitialData)
      const [activeColumn, setActiveColumn] = useState<initialDataInferface | null>(
        null,
      )
    
      const onDragStart = (result: DragUpdate) => {
        const { source } = result
        const columnIndex = Number(source.droppableId)
    
        setActiveColumn(data[columnIndex])
      }
    
      const onDragEnd = (result: DropResult) => {
        const { destination, source } = result
        if (!destination) {
          return
        }
    
        const fromColumnIndex = Number(source.droppableId)
        const fromIssueIndex = source.index
        const toColumnIndex = Number(destination.droppableId)
        const toIssueIndex = destination.index
    
        const TempIssue = data[fromColumnIndex].issues[fromIssueIndex]
    
        let TempData = update(data, {
          [fromColumnIndex]: {
            issues: issues =>
              update(issues, {
                $splice: [[fromIssueIndex, 1]],
              }),
          },
        })
    
        TempData = update(TempData, {
          [toColumnIndex]: {
            issues: issues =>
              update(issues, {
                $splice: [[toIssueIndex, 0, TempIssue]],
              }),
          },
        })
    
        setData(TempData)
        setActiveColumn(null)
      }
    
      return (
        <DragDropContext onDragEnd={onDragEnd} onDragStart={onDragStart}>
          <div className={styles.container}>
            {data.map((column, index) => {
              return (
                <Column
                  columnIndex={index}
                  key={column.id}
                  activeColumn={activeColumn}
                  column={column}
                />
              )
            })}
          </div>
        </DragDropContext>
      )
    }
    
    

    核心思想

    • 在数据中有一个 acceptIds 字段用于定义当前列只接收数组中 id 列的元素卡片。
    • 在最外层的 onDragStart 和 onDragEnd 事件中去记录当前正在拖拽的元素来自哪一列。
    • 在 Droppable 中有一个 isDropDisabled 属性可以控制当前列是否接收拖拽元素。
    • 对比当前列是否接收拖拽元素所在列的卡片,来决定 isDropDisabled 的值。

    所以核心就是:

    isDropDisabled={activeColumn ? !(activeColumn.acceptIds.includes(id) || id === activeColumn.id) : true }
    

    最后

    想看我做的看板项目的,可以到下面的 GitHub 地址。
    https://github.com/violetjack/react-dnd-demo

    相关文章

      网友评论

          本文标题:使用 react-beautiful-dnd 控制拖拽元素可以放

          本文链接:https://www.haomeiwen.com/subject/jawuhktx.html