LoginSignup
9
0
お題は不問!Qiita Engineer Festa 2023で記事投稿!

[dnd kit] ドラッグしている要素をドロップするまで、元の要素を表示し続ける方法

Last updated at Posted at 2023-06-27

これはなに

Reactのドラッグ&ドロップライブラリである dnd kit を使用してソートする際に、
ドラッグしている要素をドロップするまで、元の要素を表示し続けるようにするための実装方法の備忘録です。

デフォルトの挙動

公式サイトのドキュメントのコードに一部スタイルなどを追記したものです。

App.tsx
import React, { useState } from "react";
import {
  closestCenter,
  DndContext,
  DragOverlay,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors
} from "@dnd-kit/core";
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  verticalListSortingStrategy
} from "@dnd-kit/sortable";

import { SortableItem } from "./SortableItem";
import { Item } from "./Item";

export default function App() {
  const [activeId, setActiveId] = useState(null);
  const [items, setItems] = useState(["1", "2", "3"]);
  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates
    })
  );

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragStart={handleDragStart}
      onDragEnd={handleDragEnd}
    >
      <SortableContext items={items} strategy={verticalListSortingStrategy}>
        {items.map((id) => (
          <SortableItem key={id} id={id} />
        ))}
      </SortableContext>
      <DragOverlay>{activeId ? <Item id={activeId} /> : null}</DragOverlay>
    </DndContext>
  );

  function handleDragStart(event) {
    const { active } = event;

    setActiveId(active.id);
  }

  function handleDragEnd(event) {
    const { active, over } = event;

    if (active.id !== over.id) {
      setItems((items) => {
        const oldIndex = items.indexOf(active.id);
        const newIndex = items.indexOf(over.id);

        return arrayMove(items, oldIndex, newIndex);
      });
    }

    setActiveId(null);
  }
}
SortableItem.tsx
import React from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

export function SortableItem(props) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition
  } = useSortable({ id: props.id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    backgroundColor: "skyblue",
    border: "1px solid rgba(0, 0, 0, 0.12)",
    cursor: "grab",
    textAlign: "center",
    width: 150
  };

  return (
    <div ref={setNodeRef} style={style} {...attributes} {...listeners}>
      {props.id}
    </div>
  );
}
Item.tsx
import React, { forwardRef } from "react";

export const Item = forwardRef(({ id, ...props }, ref) => {
  return (
    <div {...props} ref={ref} style={style}>
      {id}
    </div>
  );
});

const style = {
  backgroundColor: "lightgreen",
  border: "1px solid rgba(0, 0, 0, 0.12)",
  cursor: "grab",
  opacity: 0.8,
  textAlign: "center",
  width: 150
};

dnd kitsortable を使用して要素を並び替えられるようにした際、
デフォルトでは要素をドラッグして他の要素の上に移動させた際、
元の要素は表示されなくなり、表示上ではその時点で位置が入れ替わったような見た目になります。

dnd_before.gif

元の要素を表示し続けるようにする

元の要素が表示されたままドラッグできるようにするには、
SortableItem.tsx 内で Style を適用している箇所に、以下のような変更を加えるだけで実現できます。

SortableItem.tsx
import React from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

export function SortableItem(props) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isSorting // Add!!
  } = useSortable({ id: props.id });

  const style = {
    transform: isSorting ? undefined : CSS.Transform.toString(transform), // Change!!
    transition,
    backgroundColor: "skyblue",
    border: "1px solid rgba(0, 0, 0, 0.12)",
    cursor: "grab",
    textAlign: "center",
    width: 150
  };

  return (
    <div ref={setNodeRef} style={style} {...attributes} {...listeners}>
      {props.id}
    </div>
  );
}

新たにインポートした isSorting は、現在ソートを行なっている要素かどうかを示す boolean 型の値です。
これを利用し、ソート中に本来適用される以下の transform を行わないようにすることで、
元の要素を残し続けたままドラッグすることができるようになります。

transform: CSS.Transform.toString(transform),

dnd_after.gif

9
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0