When building 100Ideas project and it's board view where you can drag and drop ideas around the div I wanted to use interact.js. The project though was in plain JS. I tried a few codesandbox examples that ports it to React, but it didn't work for me in the project. This writeup is a compilation of every hiccup I faced when building this drag and drop UI and how I over came it.
As I mentioned before for some reason, I couldn't implement the interactable logic in React way with components, so I went with a pure JS way.
For this we need to first install interactablejs package
npm install interactablejs
Next we are going to hook the interact function of this package to all the dragItem div
Interactable.js
import interact from'interactjs'// target elements with the "draggable" classinteract('.draggable').draggable({ inertia:false,// Ensures the element stays with in the parent div modifiers: [interact.modifiers.restrictRect({ restriction:'#App', endOnly:true }) ],// enable autoScroll autoScroll:false, listeners: {// this function is called when card is moved move: dragMoveListener, } })
Listener to Move the Card
Unlike what I thought the logic to move the card was rather simple, look for yourself
Interactable.js
functiondragMoveListener(event) {// the element we are movingconsttarget=event.target;// Add the relative position to current position of the divconstx= (parseFloat(target.getAttribute("data-x")) ||0) +event.dx;consty= (parseFloat(target.getAttribute("data-y")) ||0) +event.dy;// translate the element to new positiontarget.style.webkitTransform =target.style.transform ="translate("+ x +"px, "+ y +"px)";// update the posiion attributestarget.setAttribute("data-x", x);target.setAttribute("data-y", y);// These two lines is very important for the next sectionevent.stopImmediatePropagation();return [x, y]}
ReactJS Way
This is an alternative to the Pure JS way. If the pure js way works fine why would we need the ReactJS way? Here's why?
React works with states and components
The position of a dragItem is technically a state that changes
In pure JS way we have no way of tracking the changes to this state. So what?
Let's say you want to store the items and it's associated position in a DB or send via an API call, the app compoent will have no way of knowing the current position
Hence we need a component that can track the position. We call this the ineteractable component
To get to the code directly checkout this Codesandbox
Creating Interactable Component
Interactable is a custom component that we are going to introduce that will help us track the positions. The listener remains the same, but instead of hooking it directly to the interact method we will hook it to our custom component
import interact from"interactjs";import { Component, cloneElement } from"react";import PropTypes from"prop-types";import { findDOMNode } from"react-dom";exportdefaultclassInteractableextendsComponent {static defaultProps = { draggable:true,// preparing an object to hook the listener, this is the format supported by interact.js draggableOptions: {onmove: dragMoveListener}, };render() {returncloneElement(this.props.children, {ref: node => (this.node = node), draggable:false }); }componentDidMount() {// wrapping the component in interact method this.interact =interact(findDOMNode(this.node));// hooking the listener inthis.interact.draggable(this.props.draggableOptions); }}Interactable.propTypes = { children:PropTypes.node.isRequired, draggable:PropTypes.bool, draggableOptions:PropTypes.object, dropzone:PropTypes.bool, dropzoneOptions:PropTypes.object, resizable:PropTypes.bool, resizableOptions:PropTypes.object};
That's our Interactable component. It does nothing but takes the element it's wrapped with and make it draggable. Let's take it back to our original App.js where we will track the position as a state
Making Div's Interactable
Notice how the elements are wrapped with interactable component.
The code will work absolutely fine at this point as well, but our goal is to persist the position of the cards at the App level. To do that we need to introduce a data structure to store the card data