React
May 1, 2022

Осваиваем react-beautiful-dnd (часть 2)

В прошлый раз мы с вами реализовали базовую дран-н-дроп функциональность для списка, и сейчас наше приложение выглядит примерно так:

А что если мы хотим сделать наш список более кастомизируемым? Именно на этот вопрос и ответит данная статья, мы разберем с вами как нам при помощи библиотеки react-beautiful-dnd сделать наш список таким, чтобы он радовал глаз!

Внешний вид во время перетаскивания

Самым простым способом показать, какой в данный момент элемент перетаскивается - это немного изменить его фон, для этого нам нужно открыть наш компонент Task и добавить в функцию колбека snapshot:

const Container = styled.div`
    padding: 8px;
    border: 1px solid lightgray;
    border-radius: 4px;
    margin-bottom: 8px;
    background-color: ${props => (props.isDragging ? '#a699f2' : 'white')};
`;

export const Task = ({ index, ...props }) => {
    const { id, content } = props.taskData;

    return (
        <Draggable draggableId={id} index={index}>
            {(provided, snapshot /* Добавляем snapshot */) => (
                <Container
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                    ref={provided.innerRef}
                    isDragging={snapshot.isDragging} // Прокидываем в качестве пропса в styled-components
                >
                    {content}
                </Container>
            )}
        </Draggable>
    );
}

snapshot - содержит в себе свойства, которые мы можем использовать для добавления стилизации к нашему компоненту при перетаскивании.

Вот пример snapshot объекта:

const draggableSnapshot = {
    isDragging: true,
    draggingOver: 'column-1',
};

  • isDragging - флаг, которые позволяет нам понять, перетаскивается ли объект сейчас
  • draggingOver - показывает относительно какой droppable области перетаскивается объект

И вот что мы получаем:

Но останавливаться на этом я не хочу, давайте еще добавим стилей при активном перетаскивании и колонке, для этого мы воспользуемся так же объектом snapshot, но для области дропа, этот объект выглядит примерно так:

const draggableSnapshot = {
    isDraggingOver: true,
    draggingOverWith: 'task-1',
};

  • isDraggingOver - флаг, которые позволяет нам понять, перетаскивается ли над областью объект сейчас
  • draggingOverWith - показывает объект перетаскивается над областью

Далее нам с вами нужно немного доработать компонент Column, чтобы воспользоваться этими данными:

const Container = styled.div`
    margin: 12px;
    padding: 4px;
    border: 1px solid lightgray;
    border-radius: 4px;
`;

const Title = styled.h2`
    padding: 8px;
`;

const TaskList = styled.div`
    padding: 8px;
    transition: background-color 0.2s ease;
    background-color: ${props => props.isDraggingOver ? '#e6e6fa' : 'white'}
`;

export const Column = ({tasks, ...props}) => {
    const {id, title} = props.columnData;

    return (
        <Container>
            <Title>{title}</Title>
            <Droppable droppableId={id}>
                {(provided, snapshot /* Добавляем snapshot */) => (
                    <TaskList
                        ref={provided.innerRef}
                        {...provided.droppableProps}
                        isDraggingOver={snapshot.isDraggingOver} // Прокидываем в качестве пропса в styled-components
                    >
                        {tasks.map((task, idx) => <Task key={task.id} taskData={task} index={idx} />)}
                        {provided.placeholder}
                    </TaskList>
                )}
            </Droppable>
        </Container>
    );
}

И вот что мы получаем:

Внешний вид с помощью onDragStart и onDragEnd

Так же у нас есть и другая информация, которую мы можем использовать для добавления стилизации нашего приложения. Напомню, что у DragDropContext есть три колбека:

  • onDragEnd
  • onDragStart
  • onDragUpdate

Если объект, который прилетает в onDragEnd мы разобрали раньше, то давайте посмотрим на то, что прилетает в два оставшихся колбека:

// onDragStart
const start = {
    draggableId: 'task-1',
    type: 'TYPE',
    source: {
        droppableId: 'column-1',
        index: 0,
    },
};

// onDragUpdate
const update = {
    draggableId: 'task-1',
    type: 'TYPE',
    source: {
        droppableId: 'column-1',
        index: 0,
    },
    destination: {
        droppableId: 'column-1',
        index: 1,
    },
};

Давайте просто поиграемся с цветом и будем на обновление менять цвет фона документа, а на начало драга - цвет текста, выглядеть эти функции будут примерно так:

  const handleDragStart = result => {
    // Меняем цвет шрифта
    document.body.style.color = 'purple';
    
    // Накинем плавное изменение цвета
    document.body.style.transition = 'background-color 0.2s ease'
  };

  const handleDragUpdate = result => {
    const { destination } = result;
    
    // Вычислим прозрачность фона исходя из
    // текущего положение перетаскиваемого элемента
    const opacity = destination
      ? destination.index / Object.keys(data.tasks).length
      : 0;
 
    // Покрасим фон
    document.body.style.background = `rgba(153, 141, 217, ${opacity})`;
  };

Но так же как мы добавляем какие-то стили - их надо убрать, поэтому давайте добавим их сброс в handleDragEnd:

  const handleDragEnd = result => {
    document.body.style.color = 'inherit';
    document.body.style.backgroundColor = 'inherit';
    
    // Остальной код функции...
  };

И вот что мы получаем (я добавили еще немного элементов, чтобы было наглядней изменение фона):

Как видите такой подход тоже возможен, но мне больше по душе тот, который мы реализовали в первую очередь через объекты snapshot, поэтому второй вариант я уберу из своего кода)

Итоги

Вот мы с вами и разобрали как стилизовать компонент перетаскивани при помощи библиотеки react-beautiful-dnd, надеюсь вам это было полезно, я планирую дальше выпустить еще пару статей, где мы разберем другие интересные нюансы этой библиотеки!

Подпишитесь на телеграм канал, чтобы не пропустить выход новых интересных заметок