[dnd kit]ソート可能な要素のクリックイベントを動くようにする

## これはなに
React のドラッグ&ドロップライブラリである `dnd kit` を使用する際に、
a タグや button タグの要素をソート可能にしつつ、クリック時の遷移やイベント発火をさせられるようにする方法の備忘録です。

## デフォルトの挙動
[公式サイトのドキュメント](https://docs.dndkit.com/presets/sortable#drag-overlay)のコードにスタイルやクリックイベント等を追加したものです。

```react:App.tsx
import React, { useState } from "react";
import {
  closestCenter,
  DndContext,
  DragOverlay
} from "@dnd-kit/core";
import {
  arrayMove,
  SortableContext,
  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"]);

  return (
    <DndContext
      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);
  }
}
```

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

export function SortableItem(props) {
  const [clicked, setClicked] = useState<boolean>(false);

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

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    backgroundColor: clicked ? "pink" : "skyblue",
    border: "1px solid rgba(0, 0, 0, 0.12)",
    cursor: "grab",
    display: "block",
    fontSize: 16,
    padding: 4,
    textAlign: "center",
    width: 150
  };

  return (
    <button
      onClick={() => setClicked((prevState) => !prevState)}
      ref={setNodeRef}
      style={style}
      {...attributes}
      {...listeners}
    >
      {props.id}
      {clicked && " : clicked"}
    </button>
  );
}
```

```react: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",
  display: "flex",
  flexDirection: "column",
  opacity: 0.8,
  textAlign: "center",
  width: 150
};
```
a タグや button タグの要素をソートできるようにした場合、
デフォルトではその要素をクリックしてもソートの処理が優先されて、
本来クリック時に起きるリンク先への遷移やイベントが起こらないようになっています。

上記コードでは要素をクリックした際に、
背景色をピンクにして要素内の文字列に変化が起きるように onClick イベントを仕込んでいますが、
下の画像のようにソート処理が優先されているのがわかります。

![dnd_before.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247154/01473932-4bcb-bd02-c507-418126b9ef80.gif)

## クリック時のイベントを動くようにする

`sensors` を利用することで実現ができます。
`sensors` は、様々な入力デバイスからドラッグイベントの発生を感知するための仕組みで、
今回の例ではマウスからの入力を感知することができる `MouseSensor` を使用します。

https://docs.dndkit.com/api-documentation/sensors

```react:App.tsx
import React, { useState } from "react";
import {
  closestCenter,
  DndContext,
  DragOverlay,
  useSensor,  // Add!!
  useSensors, // Add!!
  MouseSensor // Add!!
} from "@dnd-kit/core";
import {
  arrayMove,
  SortableContext,
  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(  // Add!!
    useSensor(MouseSensor, { activationConstraint: { distance: 5 } })  // Add!!
  );  // Add!!

  return (
    <DndContext
      sensors={sensors}  // Add!!
      collisionDetection={closestCenter}
      onDragStart={handleDragStart}
      onDragEnd={handleDragEnd}
    >
```

`@dnd-kit/core` から新たに、
`useSensor` `useSensors` `MouseSensor` をimport するようにしました。
そしてそれらを用いた以下のコードを追加しています。

```react
  const sensors = useSensors(
    useSensor(MouseSensor, { activationConstraint: { distance: 5 } })
  );
```

マウスを用いてソート可能要素をクリックした際、そのまま 5px 以上ドラッグしないと
ソート処理が動かないように設定しています。
それにより、本来のクリック時のイベントが動作するようになります。

![dnd_after.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247154/dc5517da-88f3-f28a-673e-35b7493cd64a.gif)