swappable
v1.1.2
Published
Lightweight, high-performance drag and drop grid library with smooth animations and touch support. Create interactive, swappable grids with minimal overhead and maximum responsiveness.
Maintainers
Readme
Swappable
Swappable is a lightweight and performant JavaScript library for creating interactive, draggable, and swappable grid layouts. Built with modern web APIs, it offers smooth animations and a simple API for common use cases.
Features
- Touch-first Draggable Items: Seamless drag-and-drop functionality on both desktop and mobile devices.
- Smooth Animations: Utilizes the FLIP (First, Last, Invert, Play) animation technique for fluid layout transitions after a swap or item change.
- CSS-driven Layout: Leverages CSS Grid for a flexible and responsive layout.
- Lightweight & Performant: No external dependencies, ensuring a small footprint.
- Customizable: Easily configure class names, animation durations, and drag
behavior.
codepen
demo
Installation
$ npm i swappableimport Swappable from "swappable";
import "./node_modules/swappable/dist/swappable.css";Usage
Basic Setup
Create a container with grid items and initialize Swappable:
<div id="grid" class="swappable-grid">
<div class="grid-item">Item 1</div>
<div class="grid-item">Item 2</div>
<div class="grid-item">Item 3</div>
<div class="grid-item">Item 4</div>
</div>
<script type="module">
import Swappable from "swappable";
const grid = new Swappable("#grid", {
itemsPerRow: 4,
dragHandle: ".grid-item",
layoutDuration: 300,
swapDuration: 300,
longPressDelay: 100,
});
</script>Options
Customize Swappable with the following options:
| Option | Type | Default | Description |
| ---------------- | ---------------- | -------------- | ------------------------------------------------ |
| dragEnabled | boolean | true | Enable or disable dragging. |
| dragHandle | string \| null | ".grid-item" | Selector for drag handle (null for entire item). |
| classNames | ClassNames | See below | Custom class names for grid items and states. |
| layoutDuration | number | 300 | Duration of layout animations (ms). |
| swapDuration | number | 300 | Duration of swap animations (ms). |
| layoutEasing | string | "ease" | Easing function for animations. |
| itemsPerRow | number | 4 | Number of items per row in the grid. |
| longPressDelay | number | 100 | Delay (ms) for initiating drag on touch devices. |
Default classNames:
{
"item": "grid-item",
"drag": "dragging",
"placeholder": "placeholder",
"ghost": "ghost",
"hidden": "hidden"
}Events
Swappable supports the following events:
| Event | Data Type | Description |
| ------------- | ------------------------------------------------------------------------------------------ | ------------------------------------- |
| add | { items: HTMLElement[] } | Fired when items are added. |
| remove | { items: HTMLElement[] } | Fired when items are removed. |
| dragStart | { item: HTMLElement, event: PointerEvent } | Fired when dragging starts. |
| dragMove | { item: HTMLElement, event: PointerEvent } | Fired during dragging. |
| swap | { fromIndex: number, toIndex: number, fromElement: HTMLElement, toElement: HTMLElement } | Fired when items are swapped. |
| sort | { oldIndex: number, newIndex: number, items: SwappableItemData[] } | Fired after sorting items. |
| dragEnd | { item: HTMLElement, event: PointerEvent } | Fired when dragging ends. |
| layoutStart | void | Fired before layout animation starts. |
| layoutEnd | void | Fired after layout animation ends. |
Example of event handling:
grid.on("dragStart", ({ item, event }) => {
console.log("Dragging started on", item);
});
grid.on("swap", ({ fromIndex, toIndex }) => {
console.log(`Swapped item from ${fromIndex} to ${toIndex}`);
});Methods
| Method | Parameters | Description |
| --------- | ---------------------------------------- | ----------------------------------- |
| on | event: keyof SwappableEvents, callback | Attach an event listener. |
| off | event: keyof SwappableEvents | Remove an event listener. |
| layout | duration?: number | Recompute and animate layout. |
| add | element: HTMLElement, index?: number | Add a new item to the grid. |
| remove | target: HTMLElement \| number | Remove an item by element or index. |
| select | target: HTMLElement \| number | Select an item by element or index. |
| swap | fromIndex: number, toIndex: number | Swap two items by index. |
| refresh | None | Refresh the grid layout. |
| destroy | None | Destroy the instance and clean up. |
| detach | None | Detach event listeners. |
| enable | None | Enable dragging. |
| disable | None | Disable dragging. |
Example
A complete example with a 4x2 grid and event logging:
<!DOCTYPE html>
<html>
<head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/swappable/dist/swappable.css"
>
<style>
.grid-item {
background: #f0f0f0;
padding: 20px;
border: 1px solid #ccc;
text-align: center;
}
</style>
</head>
<body>
<div id="grid" class="swappable-grid">
<div class="grid-item">Item 1</div>
<div class="grid-item">Item 2</div>
<div class="grid-item">Item 3</div>
<div class="grid-item">Item 4</div>
<div class="grid-item">Item 5</div>
<div class="grid-item">Item 6</div>
<div class="grid-item">Item 7</div>
<div class="grid-item">Item 8</div>
</div>
<script type="module">
import Swappable from "https://cdn.jsdelivr.net/npm/swappable/dist/index.umd.js";
const grid = new Swappable("#grid", {
itemsPerRow: 4,
longPressDelay: 200,
});
grid.on("swap", ({ fromIndex, toIndex }) => {
console.log(`Swapped item from ${fromIndex} to ${toIndex}`);
});
grid.on("layoutEnd", () => {
console.log("Layout animation completed");
});
</script>
</body>
</html>Plugins
import Swappable, { withAutoResponsiveLayout, withHistory } from "swappable";
let swappableInstance = new Swappable("#my-grid");
// with the history plugin
let swappableWithHistory = withHistory(swappableInstance, {
maxHistorySize: 20,
});
// with the responsive layout plugin
let finalSwappable = withAutoResponsiveLayout(swappableWithHistory, {
breakpoints: [
{ breakpoint: 0, items: 1 }, // 1 column for smallest screens
{ breakpoint: 576, items: 2 }, // 2 columns for screens >= 576px
{ breakpoint: 768, items: 3 }, // 3 columns for screens >= 768px
{ breakpoint: 992, items: 4 }, // 4 columns for screens >= 992px
],
});Contributing
We welcome contributions! Feel free to open issues or pull requests on the GitHub repository.
License
This project is licensed under the MIT License.
