Beautiful dnd multi drag

£€€.T.$·2024년 6월 3일

📖 Function

1. 그룹화 및 자동 채번
2. 멀티 드래그


⚙️ Node

DraftTypeField.js
DraftBody.js


📂 DraftTypeField.js

import { useState } from 'react';
import { message } from 'antd';

import DraftHeader from './draft-header';
import StyledDraftTypeField from './styled';
import DraftBody from './draft-body';

export const DraftTypeField = ({ onChange, value: userList, pageMode }) => {
  const [selectedUsers, setSelectedUsers] = useState([]);
  const [isOpenBody, setIsOpenBody] = useState(false);

  const onChangeOpen = () => {
    setIsOpenBody(!isOpenBody);
  };

  const onChangeUserlist = (list) => {
    setIsOpenBody(true);
    onChange(list);
  };

  const handleClick = (_user) => {
    setSelectedUsers([_user]);
  };

  const selectRange = (startIndex, endIndex) => {
    const start = Math.min(startIndex, endIndex);
    const end = Math.max(startIndex, endIndex);
    const selectedRange = userList.slice(start, end + 1);
    setSelectedUsers(selectedRange);
  };

  const clearSelection = () => {
    setSelectedUsers([]);
  };

  const sortingUserList = (_userList, selectedList) => {
    const list = [..._userList];
    let sortSq = 0;
    let flag = false;
    list.forEach((user) => {
      if (selectedList?.includes(user.ACNT_ID) || user.DRAFT_TYPE === 'P') {
        user.SORT_SQ = sortSq;
        user.DRAFT_TYPE = 'P';
        flag = true;
      } else {
        if (flag) {
          sortSq++;
        }
        flag = false;
        user.SORT_SQ = sortSq++;
      }
    });
    return list;
  };

  const onParallel = () => {
    if (selectedUsers.length > 1) {
      const fSelectedUsers = selectedUsers.filter((user) => user.DRAFT_TYPE !== 'D');
      const parallelACNTIDs = fSelectedUsers.map((user) => user.ACNT_ID);
      const updatedUserList = sortingUserList(userList, parallelACNTIDs);

      onChange(updatedUserList);
    } else {
      message.info('선택된 직원이 2명이상이어야 합니다.');
    }
  };

  const onUnParallel = () => {
    if (selectedUsers.length > 0) {
      const fSelectedUsers = selectedUsers.filter((user) => user.DRAFT_TYPE !== 'D');
      const selectedAcntIds = fSelectedUsers.map((user) => user.ACNT_ID);
      // 기안자가 아닌 사용자들의 DRAFT_TYPE를 'A'로 변경
      const updatedUserList = userList.map((user) => ({
        ...user,
        DRAFT_TYPE: selectedAcntIds.includes(user.ACNT_ID) ? 'A' : user.DRAFT_TYPE,
      }));

      const _userList = sortingUserList(updatedUserList, []);
      onChange(_userList);
    } else {
      message.info('선택된 직원이 없습니다.');
    }
  };

  const onDragEnd = (result) => {
    const updateList = sortingUserList(result, []);
    onChange(updateList);
  };

  return (
    <StyledDraftTypeField>
      <DraftHeader
        userList={userList}
        onChangeOpen={onChangeOpen}
        isOpenBody={isOpenBody}
        onChangeUserlist={onChangeUserlist}
        onParallel={onParallel}
        onUnParallel={onUnParallel}
        pageMode={pageMode}
      />
      <DraftBody
        isOpenBody={isOpenBody}
        userList={userList}
        selectedUsers={selectedUsers}
        selectRange={selectRange}
        clearSelection={clearSelection}
        handleClick={handleClick}
        pageMode={pageMode}
        onDragEnd={onDragEnd}
      />
    </StyledDraftTypeField>
  );
};

📂 DraftBody.js

import React, { useEffect, useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

import StyeldDraftBody from './styled';
import update from 'immutability-helper';
import { message } from 'antd';

const DraftBody = ({
  isOpenBody,
  userList,
  selectedUsers,
  selectRange,
  clearSelection,
  handleClick,
  pageMode,
  onDragEnd,
}) => {
  const [selectedSortSq, setSelectedSortSq] = useState(null);
  const [selectedList, setSelectedList] = useState([]);

  const handleItemClick = (sortSq) => {
    setSelectedSortSq(sortSq);
    setSelectedList(userList.filter((user) => user.SORT_SQ === sortSq));
  };

  const _onDragEnd = (_result) => {
    if (!_result.destination) {
      return;
    }
    const dragIdx = _result.source.index;
    const destIdx = _result.destination.index;

    if (destIdx === 0) {
      message.info('기안자는 바꿀 수 없습니다.');
      return;
    }

    const resultList = [...userList];
    if ([0, 1].includes(selectedList.length)) {
      const nTab = userList[dragIdx];
      resultList.splice(dragIdx, 1);
      resultList.splice(destIdx, 0, nTab);
    } else {
      selectedList.forEach((user) => {
        const idx = resultList.findIndex((f) => f.ACNT_ID === user.ACNT_ID);
        resultList.splice(idx, 1);
        resultList.splice(destIdx, 0, user);
      });
    }

    onDragEnd(resultList);
  };

  return (
    <StyeldDraftBody>
      <div className={`draft-body ${isOpenBody ? 'open' : ''}`}>
        <DragDropContext onDragEnd={_onDragEnd}>
          <Droppable droppableId="droppable">
            {(provided) => (
              <div {...provided.droppableProps} ref={provided.innerRef}>
                {userList &&
                  userList.length > 0 &&
                  userList.map((user, i) => {
                    //draft_type을 선언
                    //type : 기안  / 병렬합의 / (결재 / 합의 / 통보 )
                    const { ACNT_NM, DEPT_NM, PSTN_NM, DRAFT_TYPE, SORT_SQ } = user || {};

                    const isSelected = selectedUsers.includes(user);

                    const handleUserClick = (event) => {
                      if (!event.shiftKey) {
                        handleClick(user);
                      } else {
                        const lastIndex = userList.findIndex((u) => u === selectedUsers[selectedUsers.length - 1]);
                        selectRange(lastIndex, i);
                      }
                    };

                    return (
                      <Draggable key={`i-${user.ACNT_ID}`} draggableId={`i-${user.ACNT_ID}`} index={i}>
                        {(provided) => (
                          <div
                            ref={provided.innerRef}
                            {...provided.draggableProps}
                            {...provided.dragHandleProps}
                            onMouseDown={() => handleItemClick(user.SORT_SQ)}
                            style={{
                              background:
                                selectedSortSq === user.SORT_SQ && user.DRAFT_TYPE === 'P' ? 'lightblue' : 'white',
                              ...provided.draggableProps.style,
                            }}
                          >
                            <div
                              className={`user ${isSelected ? 'selected' : ''}`}
                              key={i}
                              onClick={handleUserClick}
                              onDoubleClick={clearSelection}
                            >
                              <div className="user-no">{SORT_SQ}</div>
                              <div className="user-state">{DRAFT_TYPE}</div>
                              <div className="user-nm">
                                {ACNT_NM} / {DEPT_NM} / {PSTN_NM}
                              </div>
                            </div>
                          </div>
                        )}
                      </Draggable>
                    );
                  })}
                {provided.placeholder}
              </div>
            )}
          </Droppable>
        </DragDropContext>
      </div>
    </StyeldDraftBody>
  );
};

export default DraftBody;

📑 자동 채번

 const sortingUserList = (_userList, selectedList) => {
    const list = [..._userList];
    let sortSq = 0;
    let flag = false;
    list.forEach((user) => {
      if (selectedList?.includes(user.ACNT_ID) || user.DRAFT_TYPE === 'P') {
        user.SORT_SQ = sortSq;
        user.DRAFT_TYPE = 'P';
        flag = true;
      } else {
        if (flag) {
          sortSq++;
        }
        flag = false;
        user.SORT_SQ = sortSq++;
      }
    });
    return list;
  };

flag를 걸어서 조건식이 끝난 뒤의 채번도 ++ 할 수 있게 한다.


📑 멀티 드래그

 const handleItemClick = (sortSq) => {
    setSelectedSortSq(sortSq);
    setSelectedList(userList.filter((user) => user.SORT_SQ === sortSq));
  };

다중 선택이 아닌 조건에 의한 여러 값을 자동으로 선택되게 했다.

 style={{ background: selectedSortSq === user.SORT_SQ &&
        user.DRAFT_TYPE === 'P' ? 'lightblue' : 'white',
        ...provided.draggableProps.style,}}

선택된 항목들은 background를 lightblue로 변경하여 구분하였다.

const resultList = [...userList];
    if ([0, 1].includes(selectedList.length)) {
      const nTab = userList[dragIdx];
      resultList.splice(dragIdx, 1);
      resultList.splice(destIdx, 0, nTab);
    } else {
      selectedList.forEach((user) => {
        const idx = resultList.findIndex((f) => f.ACNT_ID === user.ACNT_ID);
        resultList.splice(idx, 1);
        resultList.splice(destIdx, 0, user);
      });
    }

선택된 항목이 없거나 1개일 때는 일반적인 드래그를 실행
선택된 항목이 2개 이상일 때는 선택된 항목의 배열을 돌면서 계속 변경해준다.


다중 선택 및 드래그 관련 문서
공식 링크
샘플

profile
Be {Nice} Be {Kind}

0개의 댓글