はじめに
「カードの並べ替え機能を付けたい」——そう思ってドラッグ&ドロップライブラリを探し始めると、途端に選択肢の多さと設定の複雑さに頭を抱えることになります。React専用のライブラリを選べばVueのプロジェクトでは使えませんし、多機能なライブラリを選べば今度はAPIの学習コストが重くのしかかってきます。
そんな悩みを一気に解決してくれるのが、今回紹介するSwapyです。HTMLにdata属性を数個書くだけで、どんなレイアウトもドラッグでスワップできるようになる、シンプルさが売りのライブラリです。
Swapyとは
Swapyは、"A framework-agnostic tool that converts any layout into a drag-to-swap one with just a few lines of code"(フレームワークに依存せず、数行のコードでレイアウトをドラッグ・スワップ対応にするツール)を謳うJavaScriptライブラリです。GitHub上で8,500以上のスターを集めており、React・Vue・Svelteそれぞれの統合ガイドも公式に用意されています。
一般的なドラッグ&ドロップライブラリが「並べ替え(ソート)」を主眼に置いているのに対し、Swapyは「スワップ(入れ替え)」に特化しているのが特徴です。要素をドラッグして別の場所にドロップすると、両者の位置がそのまま入れ替わる、という直感的な挙動を実現します。
主な特徴
- フレームワーク非依存 - Vanilla JSはもちろん、React・Vue・Svelteでも同じ考え方で組み込める
- HTML属性ベースの宣言的API -
data-swapy-slotとdata-swapy-itemを書くだけで対象を指定できる - アニメーション設定が簡単 -
dynamic・spring・noneの3種類から切り替え可能 - 豊富なイベントとメソッド -
onSwapなどのイベントに加え、update()やdestroy()で状態管理もしやすい
インストール
npm・pnpm・yarnのいずれでもインストールできます。
npm install swapy
pnpm install swapy
yarn add swapy
ビルドツールを使わない場合は、CDN経由での読み込みも可能です。
<script src="https://unpkg.com/swapy/dist/swapy.min.js"></script>
<script>
const swapy = Swapy.createSwapy(container)
</script>
基本的な使い方
Swapyでは、入れ替え可能な「枠」をdata-swapy-slot、入れ替えられる「中身」をdata-swapy-itemとして定義します。
<div class="container">
<!-- Slot A -->
<div data-swapy-slot="a">
<!-- Item A -->
<div data-swapy-item="a">
<div>A</div>
</div>
</div>
<!-- Slot B -->
<div data-swapy-slot="b">
<!-- Item B -->
<div data-swapy-item="b">
<div>B</div>
</div>
</div>
<!-- Slot C -->
<div data-swapy-slot="c">
<!-- Item C -->
<div data-swapy-item="c">
<div>C</div>
</div>
</div>
</div>
HTMLを用意したら、コンテナ要素を取得してcreateSwapy()に渡すだけです。
import { createSwapy } from 'swapy'
const container = document.querySelector('.container')
const swapy = createSwapy(container, {
animation: 'dynamic' // 'spring' や 'none' も選択可能
})
入れ替えが発生したタイミングを検知したい場合は、onSwapイベントを利用します。
swapy.onSwap((event) => {
console.log(event.newSlotItemMap.asObject)
// { a: 'a', b: 'c', c: 'b' } のような形式で新しい配置が取得できる
console.log(event.fromSlot) // ドラッグ元のスロット
console.log(event.toSlot) // ドラッグ先のスロット
})
コンポーネントの破棄時には、必ずdestroy()を呼び出してイベントリスナーを解放しましょう。
swapy.destroy()
実践的なユースケース
Reactでのダッシュボードウィジェット並べ替え
実際の開発でよくあるのが、Reactコンポーネント内でSwapyを使うケースです。DOMが確実にマウントされた後にインスタンスを生成する必要があるため、useEffectとクリーンアップ処理を組み合わせます。
import { createSwapy, Swapy } from 'swapy'
import { useEffect, useRef } from 'react'
function Dashboard() {
const swapy = useRef<Swapy | null>(null)
const container = useRef<HTMLDivElement>(null)
useEffect(() => {
if (container.current) {
swapy.current = createSwapy(container.current, {
animation: 'dynamic'
})
swapy.current.onSwap((event) => {
// 並び順をlocalStorageに保存しておけば、次回アクセス時にも復元できる
localStorage.setItem(
'dashboard-layout',
JSON.stringify(event.newSlotItemMap.asObject)
)
})
}
return () => {
swapy.current?.destroy()
}
}, [])
return (
<div ref={container} className="dashboard-grid">
<div data-swapy-slot="chart">
<div data-swapy-item="chart">
<div className="widget">売上グラフ</div>
</div>
</div>
<div data-swapy-slot="table">
<div data-swapy-item="table">
<div className="widget">注文一覧</div>
</div>
</div>
<div data-swapy-slot="summary">
<div data-swapy-item="summary">
<div className="widget">サマリー</div>
</div>
</div>
</div>
)
}
このように、ユーザーが自由にウィジェットの配置を入れ替えられるダッシュボードや、タスク管理ツールのカラム内でカードの位置を入れ替えるカンバンボードなど、「決まった枠の中身を入れ替えたい」場面で威力を発揮します。
動的に要素の追加・削除を行う場合は、swapy.update()を呼び出してSwapyに変更を再認識させることも忘れないようにしましょう。
function afterAddWidget() {
swapy.update()
}
まとめ
Swapyは、複雑な設定なしにドラッグ&ドロップによる「入れ替えUI」を実現できる、非常にとっつきやすいライブラリです。data-swapy-slotとdata-swapy-itemという2つの属性さえ覚えてしまえば、Vanilla JSでもReactでも同じ感覚で実装できます。
ダッシュボードのカスタマイズ機能やカンバンボードなど、「要素の位置を入れ替えたい」という要件に直面したときは、まずSwapyを試してみてはいかがでしょうか。公式ドキュメントには他にもハンドル要素の指定やドラッグ軸の制限など、細かなカスタマイズオプションが用意されているので、あわせて確認してみてください。
