Drag and Drop Examples

In all DFlex examples it's important to focus on three principles:

  • register/unregister component in the store.
  • Create dag and drop instance.
  • Call dragAt/endDragging when needed.

import { store, DnD } from "@dflex/dnd";
import type { DFlexDnDOpts, DFlexEvents } from "@dflex/dnd";
let dflexDnD: DnD | null;
interface Props {
Component: string | React.JSXElementConstructor<any>;
style?: React.CSSProperties;
className?: string;
children: React.ReactNode;
registerInput: {
id: string;
depth?: number;
readonly?: boolean;
};
opts?: DFlexDnDOpts;
}
const DFlexDnDComponent = ({
Component,
registerInput,
style,
className,
children,
opts,
}: Props) => {
const dndCompRef = React.useRef() as React.MutableRefObject<HTMLLIElement>;
const { id, depth, readonly } = registerInput;
React.useEffect(() => {
if (dndCompRef.current) {
store.register({ id, depth, readonly });
}
return () => {
store.unregister(id);
};
}, [dndCompRef.current]);
const onDFlexEvent = (e: DFlexEvents) => {
// eslint-disable-next-line no-console
console.log(`onDFlexEvent: ${e.type}`, e.detail);
};
const onMouseMove = (e: MouseEvent) => {
if (dflexDnD) {
const { clientX, clientY } = e;
dflexDnD.dragAt(clientX, clientY);
}
};
const onMouseUp = () => {
if (dflexDnD) {
dflexDnD.endDragging();
dflexDnD = null;
document.removeEventListener("mouseup", onMouseUp);
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("$onDragLeave", onDFlexEvent);
}
};
const onMouseDown = (e: React.MouseEvent) => {
const { button, clientX, clientY } = e;
// Avoid right mouse click and ensure id
if (typeof button === "number" && button === 0) {
if (id) {
document.addEventListener("mouseup", onMouseUp);
document.addEventListener("mousemove", onMouseMove);
dflexDnD = new DnD(id, { x: clientX, y: clientY }, opts);
document.addEventListener("$onDragLeave", onDFlexEvent);
}
}
};
return (
<Component
ref={dndCompRef}
id={id}
onMouseDown={onMouseDown}
className={className}
style={style}
>
{children}
</Component>
);
};

import { store } from "@dflex/dnd";
const App = () => {
React.useEffect(() => {
const unsubscribe = store.listeners.subscribe((e) => {
console.info("new layout state", e);
}, "layoutState");
return () => {
unsubscribe();
};
}, []);
React.useEffect(() => {
const unsubscribe = store.listeners.subscribe((e) => {
console.info("new mutation state", e);
}, "mutation");
return () => {
unsubscribe();
};
}, []);
React.useEffect(() => {
return () => {
store.destroy();
};
}, []);
return <MYDnDComponents />;
};

DOM elements are not re-ordered

Reordering the element on each movement will eventually lead to performance disaster. Increase scripting and painting time. Waste all the enhancements you've made on your application.

Instead, simple calculations are made directly to animate each element with translate(x,y).

The new coordinates are stored in the store, so each time the user decides to manipulate the layout DFlex already knows the coordinates without communicating with the browser. Another win for your application performance.

Continuity

One of the most important concepts in DFlex is to preserve the layout state. And this is guaranteed temporarily and permanently by keeping track of each change in the layout and make sure it can be restored automatically in case DOM node is updated outside DFlex territory.

Minimal side effect

Because DFlex operates on each DOM node individually it doesn't affect far siblings. Let's suppose you switch first element with the second in a container list contains 100 elements. In mot solutions all the siblings will be notified and manipulated even if they are not effected. With DFlex this is not the case, If you switch A with B then only A and B will be affected and manipulated.