React-beautiful-dnd 라이브러리

Ahyeon, Jung·2023년 10월 14일
0
post-thumbnail

그냥 파일을 Drop하기

input의 onDrop 속성을 이용
<input type="file" accept="image/*" alt="프로필 사진 업로드" onChange={handleImgChange} onDragOver={(e) => e.preventDefault()} onDrop={handleImgChange} />

React-beautiful-dnd

React 애플리케이션에서 드래그 앤 드롭 기능을 구현하는 오픈 소스 라이브러리
사용자가 목록 내에서 또는 목록 간에 항목을 재정렬하고 이동할 수 있는 상호 작용 및 사용자 친화적 인터페이스를 만들 때 사용

DragDropContext

드래그앤드롭 애플리케이션의 영역을 감싸는 최상위 컴포넌트

onDragStart
drag가 시작되었을 때
onDragUpdate
drag 진행 중 새로운 위치로 이동하는 등 새로운 변화가 생겼을 때
onDragEnd
drag가 끝났을 때(드래그앤드롭의 결과를 반영)

Droppable

드래그 앤 드롭에서 drop을 할 수 있는 영역, Draggable을 감싸는 컴포넌트

droppabledId prop
DragDropContext 내에서 유일해아함
innerREf
컴포넌트가 상호작용하는 것을 돕는 ref 콜백의 역할
placeholder
droppale의 면적이 변화해야할 일이 생기면, 해당부분을 처리

Draggable

드래그앤드롭에서 Drag가 가능한 컴포넌트를 감싸는 컴포넌트
draggabled

1차 시도

import React, { useEffect, useState } from 'react';
import styles from './index.module.scss';
import {
  addDays,
  endOfMonth,
  endOfWeek,
  startOfMonth,
  startOfWeek,
} from 'date-fns';
import { CalendarDiaryItemType } from '../../types/diaryType';
import DiaryItemShow from '../modal/DiaryItemShow';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

// 메인페이지 UI 미확정으로 배치만 해둠 추후 변경 예정
const Day = ({
  currentDate,
  data,
  handleIsOpenDiaryWriting,
}: {
  currentDate: { year: number; month: number };
  data: CalendarDiaryItemType[];
  handleIsOpenDiaryWriting?: () => void;
}) => {
  const week = ['일', '월', '화', '수', '목', '금', '토'];

  // 현재 날짜를 기준으로 날짜를 담아서 매핑
  const [days, setDays] = useState<Date[]>([]);

  const today = new Date();

  useEffect(() => {
    const monthStart = startOfMonth(
      new Date(currentDate.year, currentDate.month, 0),
    ); // 현재 월의 시작일
    const monthEnd = endOfMonth(monthStart); // currentDate의 종료일
    const startDate = startOfWeek(monthStart); // currentDate의 맨 앞칸
    const endDate = endOfWeek(monthEnd); // currentDate의 마지막 칸

    let currentDatePointer = startDate;
    const newDays = [];

    while (currentDatePointer <= endDate) {
      newDays.push(currentDatePointer);
      currentDatePointer = addDays(currentDatePointer, 1);
    }

    setDays(newDays);
  }, [currentDate]);

  const handleOnDragEnd = () => {
    return;
  };

  return (
    <div className={styles.dayContainer}>
      <div className={styles.weekBlock}>
        {week.map((el) => (
          <div key={el}>{el}</div>
        ))}
      </div>
      <DragDropContext onDragEnd={handleOnDragEnd}>
        <Droppable droppableId="days">
          {(provided) => (
            <div
              {...provided.droppableProps}
              ref={provided.innerRef}
              className={styles.dayBlock}
            >
              {days.map((day: Date, index) => (
                <Draggable
                  key={index}
                  draggableId={day.getDate().toString()}
                  index={index}
                >
                  {(provided, snapshot) => (
                    <div
                      ref={provided.innerRef}
                      {...provided.draggableProps}
                      {...provided.dragHandleProps}
                      className={
                        snapshot.isDragging
                          ? styles.dayItemDragging
                          : styles.dayItem
                      }
                    >
                      <div
                        className={
                          day.getFullYear() === today.getFullYear() &&
                          day.getMonth() === today.getMonth() &&
                          day.getDate() === today.getDate()
                            ? `${styles.dayItem} ${styles.today}`
                            : styles.dayItem
                        }
                      >
                        <div
                          className={
                            day.getFullYear() === currentDate.year &&
                            day.getMonth() + 1 === currentDate.month
                              ? styles.day
                              : `${styles.day} ${styles.otherMonth}`
                          }
                        >
                          {day.getDate()}
                        </div>
                        {/* 현재 년월이 같고, 오늘보다 과거이면 내용 추가 */}
                        {day.getFullYear() === currentDate.year &&
                          day.getMonth() + 1 === currentDate.month &&
                          day.getDate() <= today.getDate() && (
                            <DayItem
                              day={day.getDate()}
                              data={data}
                              handleIsOpenDiaryWriting={
                                handleIsOpenDiaryWriting
                              }
                            />
                          )}
                      </div>
                    </div>
                  )}
                </Draggable>
              ))}
            </div>
          )}
        </Droppable>
      </DragDropContext>
    </div>
  );
};

export default Day;

const DayItem = ({
  day,
  data,
  handleIsOpenDiaryWriting,
}: {
  day: number;
  data: CalendarDiaryItemType[];
  handleIsOpenDiaryWriting?: () => void;
}) => {
  const [isOpenDiary, setIsOpenDiary] = useState(false);

  const toggleIsOpenModal = () => {
    setIsOpenDiary((prev) => !prev);
  };

  // day.getDate()와 일치하는 데이터를 찾아서 반환
  // 매번 데이터를 돌아야 하는가? 생각
  const filteredData = data.filter(
    (item) => day === item.dateCreated.getDate(),
  );

  if (filteredData.length > 0) {
    const data = filteredData[0];
    return (
      <>
        <div className={styles.emoji} onClick={toggleIsOpenModal}>
          {data.emoji}
        </div>
        {isOpenDiary && (
          <DiaryItemShow toggleIsOpenModal={toggleIsOpenModal} id={data.id} />
        )}
      </>
    );
  } else {
    // 데이터가 없으면 게시글 작성 버튼
    return (
      <>
        {handleIsOpenDiaryWriting && (
          <button className={styles.addBtn} onClick={handleIsOpenDiaryWriting}>
            +
          </button>
        )}
      </>
    );
  }
};

남은 과제

content 단위로 Draggable 변경 => DayItem을 Draggable로 감싸기
다른 날짜의 경우 Draggable 불가능 => data가 없으면 Draggable로 감싸지 않기
미래인경우 불가능 => today.getDate() 이상이면 Draggable로 감싸지 않기
변경된 자리 고정하기 => api 요청하고, mutation으로 새로 데이터 받기 => 사실 그냥 useState관리하는게 낫지 않나

profile
https://a-honey.tistory.com/

0개의 댓글