I’m creating a sequence quiz and chose @hello-pangea/dnd package (which is rewritten react-beautiful-dnd and I succeeded to create a simple component but I faced a problem that when I grab draggable item it breaks other items styles:
Normal state:
While dragging:
Here are 2 columns:
Left column – simple div with indexes as I received not to create 4 droppable
columns but create 1 column where I’ll just reorder items
Right Column – droppable
column
UPD: I found out that this container has flex with justify-content: center
and not full height. When I set it to height: 100%
and justify-content: flex-start
it slightly improved situation but I want buttons which are not dragging to remain their place until they are affected by dragging button
Components
sequence.tsx:
'use client';
import { UUID } from 'crypto';
import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd';
import { useState } from 'react';
import { Button, CardLayout } from '@/components';
import { SequenceOptions } from './sequence-options';
import s from './sequence.module.scss';
export const Sequence = ({
options: opts,
title,
description,
footerColor,
headerColor,
}: TSequenceProps) => {
const [options, setOptions] = useState<{ id: UUID | string; label: string }[]>(
opts.map((opt, index) => ({
id: `${index}`,
label: opt,
})),
);
const onDragEnd: OnDragEndResponder = ({ destination, source }) => {
if (!destination) return;
if (destination.index === source.index) return;
if (destination.droppableId !== source.droppableId) return;
const newOptions = Array.from(options);
const [removed] = newOptions.splice(source.index, 1);
newOptions.splice(destination.index, 0, removed);
setOptions(newOptions);
};
return (
<DragDropContext onDragEnd={onDragEnd}>
<CardLayout footerColor={footerColor} headerColor={headerColor}>
<section className={s.wrapper}>
<section className={s.textWrapper}>
<h1 className={s.title}>{title}</h1>
<p className={s.title}>{description}</p>
</section>
<section className={s.sequenceWrapper}>
<div className={s.indexes}>
{options.map((_, index) => (
<p className={s.index} key={index}>
<span>{index + 1}</span>
</p>
))}
</div>
<SequenceOptions options={options} />
</section>
<Button onClick={() => {}} size="s">
Confirm
</Button>
</section>
</CardLayout>
</DragDropContext>
);
};
sequence-options.module.scss:
@import 'styles';
.wrapper {
@include flex(column, center, center, 12px);
}
sequence-options.tsx:
'use client';
import { UUID } from 'crypto';
import { Droppable } from '@hello-pangea/dnd';
import { DraggableItem } from '@/components';
import s from './sequence-options.module.scss';
export const SequenceOptions = ({
options,
}: {
options: { label: string; id: UUID | string }[];
}) => {
return (
<Droppable droppableId={'sequence'}>
{provided => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className={s.wrapper}
>
{options.map((option, index) => (
<DraggableItem
label={option.label}
id={option.id}
index={index}
key={option.id}
/>
))}
</div>
)}
</Droppable>
);
};
draggable-item.tsx:
'use client';
import { Draggable } from '@hello-pangea/dnd';
import clsx from 'clsx';
import { RubicSquareIcon } from '@/icons';
import s from './draggable-item.module.scss';
export const DraggableItem = ({
id,
index = 0,
label = '',
disabled = false,
dragging = false,
}: TDraggableItemProps) => {
return (
<Draggable isDragDisabled={disabled} draggableId={id} index={index}>
{provided => (
<div
{...provided.dragHandleProps}
{...provided.draggableProps}
ref={provided.innerRef}
className={clsx(s.item, dragging && s.dragging, disabled && s.disabled)}
>
<RubicSquareIcon className={s.icon} />
<p className={s.label}>{label}</p>
</div>
)}
</Draggable>
);
};
draggable-item.module.scss:
@import 'styles';
.item {
@include flex($align: center, $gap: 8px);
width: 220px;
height: 48px;
box-sizing: border-box;
padding: 16px;
border-radius: 4px;
background-color: $primary;
cursor: grab;
.icon {
@include set-size(16px);
path {
fill: rgba($white, 0.4);
transition: fill 0.15s ease-in-out;
}
}
.label {
text-transform: capitalize;
font-family: $soraRegular;
font-size: 16px;
color: $white;
}
}
/* STATES */
.dragging {
.icon {
path {
fill: $white;
}
}
}
.disabled {
opacity: 0.4;
cursor: not-allowed;
}