Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

📢 Hello everyone, I am Xiao Cheng, a sophomore front-end enthusiast

📢 this series of articles is a learning summary of the jIRA task management system in action

📢 Thank you very much for reading

📢 May you be true to yourself and love life

In the last article, we wrote the task group page, and now our project is almost complete, with all the CRUD operations, routing jumps, and page layout implemented. In this article, we will optimize our project again by adding a drag-and-drop feature to my Kanban page

This content is not very understand, a little water, understand and then change

💡 Knowledge points look first

  • Add drag and drop to kanban
  • In HTML5drop å’Œ drag

Add drag and drop function to kanban

This article is just one part, as the title says, adding a drag and drop feature

The implementation looks something like this

We use a library called React-beautiful-DND to implement this function, which can be found on the NPM website

First of all, we need to define a Droppable component to wrap our Draggable elements, which means that we can drag and drop the contents of this area. Second, we need to add a Draggable component to wrap our elements in the place where we put them. Used to indicate that this area is an area that can be laid down

In this case, the Drop and Drag components are overwritten

This part is a bit difficult, I don’t understand it very well, so let me mention a few points

  • In this case we want to pull one awaychildrenProperty, do not use nativechildrenattribute
  • Due to API requirements, we need to reserve receptionref, here we use the way of forwarding to achieve, throughforwardRefTo achieve
export const DropChild = React.forwardRef<HTMLDivElement, DropChildProps>(({ children, ... props }, ref) = >
    <div ref={ref} {. props} >{children} {/* API require props. Provided? .placeholder}</div>
)
Copy the code

1. The implementationDropcomponent

// This file is equivalent to refactoring the drop native component
// Define a type, do not want to use the native children, use their own
type DropProps = Omit<DroppableProps, 'children'> & { children: ReactNode }
export const Drop = ({ children, ... props }: DropProps) = > {
    return <Droppable {. props} >Provided => {if (React. IsValidElement (children)) {return React. CloneElement (children, {... provided.droppableProps, ref: provided.innerRef, provided }) } return<div />})}</Droppable>
}
Copy the code

2. ImplementDragcomponent

type DragProps = Omit<DraggableProps, 'children'> & { children: ReactNode }
export const Drag = ({ children, ... props }: DragProps) = > {
    return <Draggable {. props} >{ provided => { if (React.isValidElement(children)) { return React.cloneElement(children, { ... provided.draggableProps, ... provided.dragHandleProps, ref: provided.innerRef }) } return<div />}}</Draggable>
}
Copy the code

3. Drag persistence

Write two components, although difficult, can directly CV this part of the code.

  • It makes sense. Use itDropComponent package drag location, useDragLocation of component packages
  • Finally, we need to persist our state, which is native to the componentonDragEndMethod to implement

Here we need to implement another hook to implement this function, which is difficult

Here we use if to determine whether it’s currently dragging a kanban or a task, determine whether it’s dragging left and right or dragging up and down, use the built-in method in the component to calculate the id that’s dropped and the ID that’s picked up and insert it into this Kanban task

When the drag is complete, the source and Destination objects are returned with information about the drag

If column is a drag between kanban, we need to persist it by calling one of our newly wrapped useReorderKanban methods

In the case of row, the inter-task persistence method useRecordTask is called for persistence

export const useDragEnd = () = > {
    // Get the kanban first
    const { data: kanbans } = useKanbans(useKanbanSearchParams())
    const { mutate: reorderKanban } = useReorderKanban(useKanbansQueryKey())
    // Get task information
    const { data: allTasks = []} = useTasks(useTasksSearchParams())
    const { mutate: reorderTask } = useReorderTask(useTasksQueryKey())
    return useCallback(({ source, destination, type }: DropResult) = > {
        if(! destination) {return
        }
        // Kanban sort
        if (type= = ='COLUMN') {
            constfromId = kanbans? .[source.index].idconsttoId = kanbans? .[destination.index].id// Return if there is no change
            if(! fromId || ! toId || fromId === toId) {return
            }
            // Determine where the drop position is on the target
            const type = destination.index > source.index ? 'after' : 'before'
            reorderKanban({ fromId, referenceId: toId, type})}if (type= = ='ROW') {
            // Convert to a number by +
            const fromKanbanId = +source.droppableId
            const toKanbanId = +destination.droppableId
            // Cross-version sorting is not allowed
            if(fromKanbanId ! == toKanbanId) {return
            }
            // Get the dragged element
            const fromTask = allTasks.filter(task= > task.kanbanId === fromKanbanId)[source.index]
            const toTask = allTasks.filter(task= > task.kanbanId === fromKanbanId)[destination.index]
            //
            if(fromTask? .id === toTask? .id) {return
            }
            reorderTask({
                fromId: fromTask? .id,referenceId: toTask? .id, fromKanbanId, toKanbanId,type: fromKanbanId === toKanbanId && destination.index > source.index ? 'after' : 'before'
            })
        }
    }, [allTasks, kanbans, reorderKanban, reorderTask])
}
Copy the code

4. useReorderKanban

By passing in a set of data, including the start position, insert position, before or after the insert position, these data, the background interface judgment, to persist, here using useMutation is mentioned in front of, the use of methods are very skilled

// Persistent data interface
export const useReorderKanban = (queryKey:QueryKey) = > {
    const client = useHttp()
    return useMutation(
        (params: SortProps) = > {
            return client('kanbans/reorder', {
                data: params,
                method: "POST"
            })
        },
        useReorderKanbanConfig(queryKey)
    )
}
Copy the code

5. New Drop and Drag additions in HTML5

When we want to make an element draggable, just set draggable to true

<img draggable="true">
Copy the code

Ondragstart and setData() occur when drag and drop executes

Executing onDragStart calls a function, the drag function, which specifies the data to be dragged

function drag(event)
{
    event.dataTransfer.setData("Text",ev.target.id);
}
Copy the code

Where Text is the data type we need to add to the Drag Object

Where to place the dragged data

By default, data/elements cannot be placed in other elements. If we want to set to allow placement, we must prevent the default handling of elements.

This is done by calling the event.preventDefault() method of the ondragover event:

event.preventDefault()
Copy the code

The drop event occurs when preventing

function drop(ev)
{
    ev.preventDefault();
    var data=ev.dataTransfer.getData("Text");
    ev.target.appendChild(document.getElementById(data));
}
Copy the code

Code explanation:

  • callpreventDefault()To avoid default browser processing of data (dropThe default behavior of the event is to open as a link.)
  • throughdataTransfer.getData("Text")Method to retrieve the dragged data. The method will return insetData()Any data in the method set to the same type.
  • The dragged data belongs to the dragged elementid ("drag1")
  • Appends the dragged element to the placed element (the target element)

(Refer to the rookie tutorial)

Try it for yourself: an online demo

📌 summary

  1. Have a general understanding of how to usereact-beautiful-dnd
  2. You have a general idea of drag persistence
  3. You’ve learned about HTML5drop å’Œ drag

Finally, I may not be clear enough in many places, please forgive me

💌 if the article has any mistakes, or have any questions, welcome to leave a message, also welcome private letter exchange