dnd-kit-solid
v0.1.0
Published
Bringing the power of dnd-kit to SolidJS applications. A modern, lightweight, performant, accessible and extensible drag & drop toolkit.
Maintainers
Readme
dnd-kit-solid
Bringing the power of dnd-kit to SolidJS applications. A modern, lightweight, performant, accessible and extensible drag & drop toolkit with the same great API and features.
Install
npm i dnd-kit-solid @dnd-kit/domUsage
DragDropProvider
The DragDropProvider component creates a context that enables drag and drop interactions for its children. It manages the drag and drop state and coordinates between draggable and droppable elements.
Basic Usage
Wrap your application or a section with DragDropProvider:
import { DragDropProvider } from "dnd-kit-solid";
function App() {
return (
<DragDropProvider>
<YourDraggableContent />
</DragDropProvider>
);
}Event Handling
Listen to drag and drop events to respond to user interactions:
function App() {
return (
<DragDropProvider
onBeforeDragStart={({ source, event }) => {
// Optionally prevent dragging
if (shouldPreventDrag(source)) {
event.preventDefault();
}
}}
onDragStart={({ source }) => {
console.log("Started dragging", source.id);
}}
onDragMove={({ operation }) => {
const { position } = operation;
console.log("Current position:", position);
}}
onDragOver={({ source, target }) => {
console.log(`${source.id} is over ${target.id}`);
}}
onDragEnd={({ source, target }) => {
if (target) {
console.log(`Dropped ${source.id} onto ${target.id}`);
}
}}
>
<YourDraggableContent />
</DragDropProvider>
);
}Multiple Contexts
You can create multiple independent drag and drop contexts:
function App() {
return (
<div>
<DragDropProvider>
<FileList /> {/* Files can only be dropped in this context */}
</DragDropProvider>
<DragDropProvider>
<TaskList /> {/* Tasks can only be dropped in this context */}
</DragDropProvider>
</div>
);
}Configuration
Customize behavior with plugins, sensors, and modifiers:
import {
DragDropProvider,
PointerSensor,
KeyboardSensor,
RestrictToWindow,
} from "dnd-kit-solid";
function App() {
return (
<DragDropProvider
// Custom input detection
sensors={[PointerSensor, KeyboardSensor]}
// Extend functionality
plugins={[AutoScroller, Accessibility]}
// Modify drag behavior
modifiers={[RestrictToWindow]}
>
<YourDraggableContent />
</DragDropProvider>
);
}useDraggable
Use the useDraggable hook to make elements draggable that can be dropped over droppable targets.
The useDraggable hook requires an id and accepts all the same options as the Draggable class.
The hook must be used within a
DragDropProvideror provided with a manager instance.
Basic Usage
import { useDraggable } from "dnd-kit-solid";
function Draggable(props) {
const { ref } = useDraggable({
id: props.id,
// Optional: provide a manager if not using DragDropProvider
// manager: props.manager,
});
return <div ref={ref}>Draggable</div>;
}Specifying a Drag Handle
To specify a drag handle, use the handleRef returned by the hook:
function Draggable(props) {
const { ref, handleRef } = useDraggable({
id: props.id,
});
return (
<div ref={ref}>
Draggable
<button ref={handleRef}>Drag handle</button>
</div>
);
}When you connect a drag handle element, only the element that is connected to the handleRef will initiate the drag operation.
Restricting Dragging Using Modifiers
Use modifiers to modify or restrict the behavior of draggable elements:
import { useDraggable } from "dnd-kit-solid";
import { RestrictToHorizontalAxis } from "@dnd-kit/abstract/modifiers";
function Draggable({ id }) {
const { ref } = useDraggable({
id,
modifiers: [RestrictToHorizontalAxis],
});
return <div ref={ref}>Draggable</div>;
}API Reference
The useDraggable hook is a thin wrapper around the Draggable class that makes it easier to create draggable elements in SolidJS. It therefore accepts all of the same input arguments.
Input
The useDraggable hook accepts the following arguments:
id(required): The identifier of the draggable element. Should be unique within the same drag and drop context.type: Optionally supply a type to only allow this draggable element to be dropped over droppable targets that accept this type.element: If you already have a reference to the element, you can pass it instead of using theref.handle: If you already have a reference to the drag handle element, you can pass it instead of using thehandleRef.disabled: Set totrueto prevent the draggable element from being draggable.feedback: The type of feedback that should be displayed when the element is being dragged ('default' | 'clone' | 'move' | 'none').modifiers: An array of modifiers that can be used to modify or restrict the behavior of the draggable element.sensors: An array of sensors that can be bound to the draggable element to detect drag interactions.data: Additional data about the draggable element that can be accessed in event handlers, modifiers, sensors or custom plugins.
Output
The useDraggable hook returns an object containing:
ref: A ref callback function that can be attached to the element you want to make draggable.handleRef: A ref callback function that can be attached to an element to create a drag handle.isDragging: A boolean signal that indicates whether the draggable is currently being dragged.isDropping: A boolean signal that indicates whether the draggable is being dropped.isDragSource: A boolean signal that indicates whether the draggable is the source of the drag operation.draggable: The draggable instance that is created by the hook.
useDroppable
Use the useDroppable hook to create droppable targets for draggable elements.
The useDroppable hook requires an id and accepts all the same options as the Droppable class.
The hook must be used within a
DragDropProvideror provided with a manager instance.
Basic Usage
import { useDroppable } from "dnd-kit-solid";
function Droppable(props) {
const { isDropTarget, ref } = useDroppable({
id: props.id,
// Optional: provide a manager if not using DragDropProvider
// manager: props.manager,
});
return (
<div ref={ref}>
{isDropTarget()
? "Draggable element is over me"
: "Drag something over me"}
</div>
);
}Accepting Specific Types
You can restrict which draggable elements can be dropped by specifying the accept property:
function Droppable(props) {
const { isDropTarget, ref } = useDroppable({
id: props.id,
accept: "item", // Only draggable elements with type "item" can be dropped here
});
return (
<div ref={ref}>
{isDropTarget() ? "Item is over me" : "Drag an item over me"}
</div>
);
}Custom Collision Detection
You can provide a custom collision detector function:
function Droppable(props) {
const { isDropTarget, ref } = useDroppable({
id: props.id,
collisionDetector: (input) => {
// Custom collision detection logic
return {
id: props.id,
data: input.draggable.data,
};
},
});
return <div ref={ref}>Drop zone</div>;
}API Reference
The useDroppable hook is a thin wrapper around the Droppable class that makes it easier to create droppable targets in SolidJS. It therefore accepts all of the same input arguments.
Input
The useDroppable hook accepts the following arguments:
id(required): The identifier of the droppable element. Should be unique within the same drag and drop context.element: If you already have a reference to the element, you can pass it instead of using theref.accept: Optionally supply a type of draggable element to only allow it to be dropped over certain droppable targets that accept this type.collisionDetector: Optionally supply a collision detector function to detect collisions between the droppable element and draggable elements.collisionPriority: Optionally supply a number to set the collision priority of the droppable element. Higher numbers have higher priority when detecting collisions.disabled: Set totrueto prevent the droppable element from being a drop target.data: Additional data about the droppable element that can be accessed in event handlers, modifiers, sensors or custom plugins.effects: Advanced feature for setting up reactive effects that are automatically cleaned up when the component is unmounted.
Output
The useDroppable hook returns an object containing:
ref: A ref callback function that can be attached to the element you want to use as a droppable target.isDropTarget: A boolean signal that indicates whether the element is currently being dragged over.droppable: The droppable instance that is created by the hook.
useSortable
Use the useSortable hook to reorder elements in a list or across multiple lists.
The useSortable hook requires an id and an index. It accepts all the same options as the Sortable class.
The hook must be used within a
DragDropProvideror provided with a manager instance.
Basic Usage
import { useSortable } from "dnd-kit-solid";
function SortableItem(props) {
const { ref, isDragging } = useSortable({
id: props.id,
index: props.index,
// Optional: provide a manager if not using DragDropProvider
// manager: props.manager,
});
return (
<div
ref={ref}
style={{
opacity: isDragging() ? 0.5 : 1,
}}
>
Item {props.id}
</div>
);
}Using a Drag Handle
You can specify a drag handle to control which part of the element initiates the drag:
function SortableItem(props) {
const { ref, handleRef, isDragging } = useSortable({
id: props.id,
index: props.index,
});
return (
<div ref={ref}>
<div>Item {props.id}</div>
<button ref={handleRef}>Drag handle</button>
</div>
);
}Animating Transitions
You can add smooth transitions when items are reordered:
function SortableItem(props) {
const { ref, isDragging } = useSortable({
id: props.id,
index: props.index,
transition: {
duration: 200,
easing: "ease",
idle: true,
},
});
return (
<div
ref={ref}
style={{
opacity: isDragging() ? 0.5 : 1,
transition: "all 200ms ease",
}}
>
Item {props.id}
</div>
);
}API Reference
The useSortable hook is a thin wrapper around the Sortable class that makes it easier to create sortable elements in SolidJS. It combines the functionality of useDraggable and useDroppable hooks.
Input
The useSortable hook accepts all the same arguments as the useDraggable and useDroppable hooks, plus:
id(required): The identifier of the sortable element. Should be unique within the same drag and drop context.index(required): The index of the sortable element in the list.transition: Optional configuration for animating the sortable element:duration: The duration of the transition in millisecondseasing: The easing function to use for the transitionidle: Whether to animate when the index changes without dragging
element: If you already have a reference to the element, you can pass it instead of using theref.handle: If you already have a reference to the drag handle element, you can pass it instead of using thehandleRef.target: If you already have a reference to the droppable target element, you can pass it instead of using thetargetRef.source: If you already have a reference to the draggable source element, you can pass it instead of using thesourceRef.accept: Optionally supply a type of draggable element to only allow it to be dropped over certain droppable targets.collisionDetector: Optionally supply a collision detector function.collisionPriority: Optionally supply a number to set the collision priority.disabled: Set totrueto prevent the sortable element from being sortable.data: Additional data about the sortable element.effects: Advanced feature for setting up reactive effects.
Output
The useSortable hook returns an object containing:
ref: A ref callback function for the main sortable element.targetRef: A ref callback function for the droppable target element.sourceRef: A ref callback function for the draggable source element.handleRef: A ref callback function for the drag handle element.isDropTarget: A boolean signal indicating if the element is a drop target.isDragSource: A boolean signal indicating if the element is the drag source.isDragging: A boolean signal indicating if the element is being dragged.isDropping: A boolean signal indicating if the element is being dropped.sortable: The sortable instance that is created by the hook.
useDragDropMonitor
Monitor drag and drop events in your SolidJS components.
The useDragDropMonitor hook allows you to monitor drag and drop events within a DragDropProvider.
Basic Usage
import { useDragDropMonitor } from "dnd-kit-solid";
function DragMonitor() {
useDragDropMonitor({
onBeforeDragStart(event, manager) {
// Optionally prevent dragging
if (shouldPreventDrag(event.operation.source)) {
event.preventDefault();
}
},
onDragStart(event, manager) {
console.log("Started dragging", event.operation.source);
},
onDragMove(event, manager) {
console.log("Current position:", event.operation.position);
},
onDragOver(event, manager) {
console.log("Over droppable:", event.operation.target);
},
onDragEnd(event, manager) {
const { operation, canceled } = event;
if (canceled) {
console.log("Drag cancelled");
return;
}
if (operation.target) {
console.log(
`Dropped ${operation.source.id} onto ${operation.target.id}`
);
}
},
onCollision(event, manager) {
console.log("Collisions:", event.collisions);
},
});
return null;
}Make sure to use the useDragDropMonitor hook within a component that is wrapped in a DragDropProvider component.
Events
| Event | Description | Preventable | Data | | --------------- | ---------------------------- | ----------- | -------------------------------- | | beforeDragStart | Fires before drag begins | Yes | operation | | dragStart | Fires when drag starts | No | operation, nativeEvent | | dragMove | Fires during movement | Yes | operation, to, by, nativeEvent | | dragOver | Fires when over a droppable | Yes | operation | | collision | Fires on droppable collision | Yes | collisions | | dragEnd | Fires when drag ends | No | operation, canceled, nativeEvent |
Notes
- The hook must be used within a
DragDropProvider - Event handlers receive both the event data and the manager instance
- Use
event.preventDefault()on preventable events to stop their default behavior - The
dragEndevent includes acanceledproperty that replaces the oldonDragCancelevent - Event handlers are automatically cleaned up when the component is unmounted
useDragOperation
Access the current drag operation state in your SolidJS components.
The useDragOperation hook provides reactive access to the current drag operation state, including the dragged element, drop target, and position.
Basic Usage
import { useDragOperation } from "dnd-kit-solid";
function DragOperationMonitor() {
const operation = useDragOperation();
return (
<div>
{operation.source && <div>Dragging: {operation.source.id}</div>}
{operation.target && <div>Over: {operation.target.id}</div>}
{operation.position && (
<div>
Position: {operation.position.x}, {operation.position.y}
</div>
)}
<div>Status: {operation.status}</div>
{operation.canceled && <div>Operation was canceled</div>}
</div>
);
}Using with Custom Manager
If you need to use the hook outside of a DragDropProvider, you can pass a manager instance:
function CustomDragMonitor(props) {
const operation = useDragOperation({
manager: props.manager,
});
return <div>{operation.source?.id}</div>;
}API Reference
Input
The useDragOperation hook accepts an optional options object:
manager: OptionalDragDropManagerinstance. If not provided, the hook will use the manager from the nearestDragDropProvider.
Output
The useDragOperation hook returns a reactive store containing:
source: The currently dragged element (ornullif no drag is in progress)target: The current drop target (ornullif not over a valid target)position: The current drag coordinates (ornullif no drag is in progress)status: The current operation statuscanceled: Whether the operation was canceled
Notes
- The hook must be used within a
DragDropProviderunless a manager is explicitly provided - All properties are reactive and will update automatically when the drag operation state changes
- The store is automatically disposed when the component is unmounted
Development
Setup
# Install dependencies
npm installDevelopment Mode
Start the development server with the playground:
npm run devThis will start a development server at http://localhost:3000 where you can see your changes in real-time.
Building
Build the library for production:
npm run buildThis generates:
- CommonJS build in
dist/cjs/ - ES Modules build in
dist/esm/ - TypeScript type definitions in
dist/types/
Testing
# Run tests
npm test
# Check code quality
npm run lint
# Fix linting issues
npm run lint:fixContributing
We welcome contributions! Here's how to get started:
- Fork the repository
- Clone your fork:
git clone https://github.com/your-username/sorta.git cd sorta - Install dependencies:
npm install - Create a new branch for your feature:
git checkout -b feature/your-feature-name - Make your changes and ensure:
- All tests pass (
npm test) - Code is properly linted (
npm run lint) - TypeScript types are correct
- Documentation is updated
- All tests pass (
- Commit your changes with a descriptive message
- Push to your fork
- Create a Pull Request
Development Guidelines
- Follow the existing code style and patterns
- Write tests for new features
- Update documentation as needed
- Keep the bundle size minimal
- Ensure cross-browser compatibility
- Use TypeScript for type safety
- Maintain compatibility with the original dnd-kit API where possible
- Follow SolidJS best practices
Code of Conduct
Please be respectful and considerate of others when contributing. We follow the Contributor Covenant code of conduct.
License
MIT
