Modern drag and drop package for all JavaScript frameworks
npm install @dflex/dnd
src="https://img.shields.io/github/workflow/status/dflex-js/dflex/Unit Test"
alt="Dflex build status" />
src="https://img.shields.io/github/issues-pr/dflex-js/dflex"
alt="number of opened pull requests"/>
src="https://img.shields.io/npm/v/@dflex/dnd"
alt="DFlex last released version" />
src="https://img.shields.io/github/issues/dflex-js/dflex"
alt="number of opened issues"/>
src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"
alt="Dflex welcomes pull request" />
src="https://img.shields.io/twitter/url?label=Follow%20%40dflex_js&style=social&url=https%3A%2F%2Ftwitter.com%2Fdflex_js"
alt="Follow DFlex on twitter" />
``bash`
npm install @dflex/dnd
DFlex DnD depends on three principles to achieve DOM interactivity:
- Register element in the store.
- Start dragging when mouse is down.
- End dragging to release element when mouse is up.
`js`
import { store, DnD } from "@dflex/dnd";
Each element should be registered in DFlex DnD Store in order to be active for drag
and drop later.
`ts`
store.register(RegisterInputOpts): void;
Where RegisterInputOpts is an object with the following properties:
- id: string Targeted element-id.depth?: number
- The depth of targeted element starting from zero (The default value is zero).readonly?: boolean
- True for elements that won't be transformed during DnD
but belongs to the same interactive container.
The responsive drag and drop session should be created when onmousedown is
fired. So it can initialize the element and its siblings before start dragging.
`ts`
const dflexDnD = new DnD(id, coordinate, opts);
- id: string registered element-id in the store.coordinate: AxesPoint
- is an object with {x: number, y: number} contains the coordinates of theopts?: DFlexDnDOpts
mouse/touch click.
- is DnD options object. You can see DFlex DnD options
full documentation by clicking here.
`ts`
dflexDnD.dragAt(x, y);
- x: number is event.clientX, the horizontal click coordinate.y: number
- is event.clientY, the vertical click coordinate.
`ts`
dflexDnD.endDragging();
It's necessary to cleanup the element from store when the element won't be used
or will be removed/unmounted from the DOM to prevent any potential memory leaks.
`ts`
store.unregister(id: string): void
You can pass options when creating a DnD instance that controls each element
individually. So your options can be different from each other.
The threshold object defines when the dragging event should be fired and
triggers the response of other sibling elements.
#### Threshold Interface
`ts
interface ThresholdPercentages {
/* vertical threshold in percentage from 0-100 /
vertical: number;
/* horizontal threshold in percentage from 0-100 /
horizontal: number;
}
`
#### Threshold Definition
`ts`
interface DFlexDnDOpts {
// ... other options.
threshold?: Partial
}
#### Threshold Default Value
`json`
{
"threshold": {
"vertical": 60,
"horizontal": 60
}
}
DFlex is built to manipulate DOM elements with transformation indefinitely. This
means you can always drag and drop elements without reconstruction of the DOM.
Still, it comes with a reconciler that tracks elements' changes and only
reconciles the elements that have changed their position from their origin.
#### Commit Interface
`ts`
interface CommitInterface {
enableAfterEndingDrag: boolean;
enableForScrollOnly: boolean;
}
#### Commit Definition
`ts`
interface DFlexDnDOpts {
// ... other options.
commit?: Partial
}
#### Commit Default Value
`json`
{
"commit": {
"enableAfterEndingDrag": true,
"enableForScrollOnly": true
}
}
You can define the dragging restrictions for each element relative:
1. Element position.
2. Element parent container.
3. Screen viewport (automatically enabled).
#### Restrictions Interface
`ts`
interface Restrictions {
self: {
allowLeavingFromTop: boolean;
allowLeavingFromBottom: boolean;
allowLeavingFromLeft: boolean;
allowLeavingFromRight: boolean;
};
container: {
allowLeavingFromTop: boolean;
allowLeavingFromBottom: boolean;
allowLeavingFromLeft: boolean;
allowLeavingFromRight: boolean;
};
}
#### Restrictions Definition
`ts`
interface DFlexDnDOpts {
// ... other options.
restrictions?: {
self?: Partial
container?: Partial
};
}
#### Restrictions Default Value
`json`
{
"restrictions": {
"self": {
"allowLeavingFromTop": true,
"allowLeavingFromBottom": true,
"allowLeavingFromLeft": true,
"allowLeavingFromRight": true
},
"container": {
"allowLeavingFromTop": true,
"allowLeavingFromBottom": true,
"allowLeavingFromLeft": true,
"allowLeavingFromRight": true
}
}
}
#### Auto-Scroll Interface
`ts`
interface ScrollOptions {
enable?: boolean;
initialSpeed?: number;
threshold?: Partial
}
#### Auto-Scroll Definition
`ts`
interface DFlexDnDOpts {
// ... other options.
scroll?: Partial
}
#### Auto-Scroll Default Value
`json`
{
"scroll": {
"enable": true,
"initialSpeed": 10,
"threshold": {
"vertical": 15,
"horizontal": 15
}
}
}
DFlex has three (3) types of custom
events.
1. DFlex Dragged Event.
2. DFlex Interactivity Event.
3. DFlex Siblings Event.
`jsonDFlexEvent: ${e.type}
// DFlex event handler.
const onDFlexEvent = (e: DFlexEvents) => {
// Do something.
console.log(, e.detail);
};
// Dragged Events.
const ON_OUT_CONTAINER = "$onDragOutContainer";
const ON_OUT_THRESHOLD = "$onDragOutThreshold";
// Interactivity Events.
const ON_DRAG_OVER = "$onDragOver";
const ON_DRAG_LEAVE = "$onDragLeave";
// Sibling Events.
const ON_LIFT_UP = "$onLiftUpSiblings";
const ON_MOVE_DOWN = "$onMoveDownSiblings";
// Capture DFlex event.
document.addEventListener(
ON_OUT_CONTAINER /* or another event /,
onDFlexEvent
);
// Remove it later when dragging is done.
document.removeEventListener(
ON_OUT_CONTAINER /* or another event /,
onDFlexEvent
);
`
It's an event related to capturing dragged positions. This event is fired when
the dragged is out of its threshold position $onDragOutContainer or out of its$onDragOutThreshold
container .
#### DraggedEvent interface
`ts
interface PayloadDraggedEvent {
/* Returns element id in the registry /
id: string;
/* Returns dragged temp index /
index: number;
}
/* For dragged out of threshold or container event. /
type DFlexDraggedEvent = CustomEvent
`
#### Interactivity Event
It's an event related to capturing dragged interactions with other elements.
This event is fired when the dragged is over another element $onDragOver or$onDragLeave
when the dragged is leaving the occupied position .
##### InteractivityEvent interface
`ts
interface PayloadInteractivityEvent {
/* Returns element id in the registry /
id: string;
/* Returns element current index /
index: number;
/* Returns the element that triggered the event /
target: HTMLElement;
}
/* For dragged over an element or leaving an element. /
type DFlexInteractivityEvent = CustomEvent
`
#### Siblings Event
It's an event related to capturing siblings' positions. This event is fired when
the siblings are lifting up $onLiftUpSiblings or moving down $onMoveDownSiblings
##### SiblingsEvent interface
`ts
interface PayloadSiblingsEvent {
/* Returns the index where the dragged left /
from: number;
/* Returns the last index effected of the dragged leaving/entering /
to: number;
/* Returns an array of sibling ids in order /
siblings: string[];
}
/* When dragged movement triggers the siblings up/down. /
type DFlexSiblingsEvent = CustomEvent
`
DFlex listeners are more generic than the custom events and responsible for
monitoring the entire layout and reporting back to you.
DFlex has two (2) types of listeners:
1. Layout state listener.
2. Mutation listener.
`js
// app/index.js
const unsubscribeLayout = store.listeners.subscribe((e) => {
console.info("new layout state", e);
}, "layoutState");
// call it later for clear listeners from memory.
unsubscribeLayout();
const unsubscribeMutation = store.listeners.subscribe((e) => {
console.info("new mutation state", e);
}, "mutation");
// call it later for clear listeners from memory.
unsubscribeMutation();
`
Responsible for monitoring any change that happens to layout interactivity.
#### Layout state listener interface
`ts
type LayoutState =
| "pending" // when DnD is initiated but not activated yet.
| "ready" // When clicking over the registered element. The element is ready but not being dragged.
| "dragging" // as expected.
| "dragEnd" // as expected.
| "dragCancel"; // When releasing the drag without settling in the new position.
interface DFlexLayoutStateEvent {
type: "layoutState";
status: LayoutState;
}
`
Responsible for monitoring DOM mutation that happens during reconciliation.
#### Mutation listener interface
`ts
type ElmMutationType = "committed";
interface DFlexElmMutationEvent {
type: "mutation";
status: ElmMutationType;
payload: {
target: HTMLElement; // HTML element container.
ids: string[]; // Committed Elements' id in order.
};
}
`
DFlex elements are serialized and exported accordingly.
`ts
store.getSerializedElm(elmID: string): DFlexSerializedElement | null
type DFlexSerializedElement = {
type: string;
version: number;
id: string;
translate: PointNum | null;
grid: PointNum;
order: DFlexDOMGenOrder;
initialPosition: AxesPoint;
rect: BoxRectAbstract;
hasTransformedFromOrigin: boolean;
hasPendingTransformation: boolean;
isVisible: boolean;
};
`
DFlex scroll containers are serialized and exported accordingly. You can get any
scroll container for any registered element id.
`ts
store.getSerializedScrollContainer(elmID: string): DFlexSerializedScroll | null
type DFlexSerializedScroll = {
type: string;
version: number;
key: string;
hasOverFlow: AxesPoint
hasDocumentAsContainer: boolean;
scrollRect: AbstractBox;
scrollContainerRect: AbstractBox;
invisibleDistance: AbstractBox;
visibleScreen: Dimensions;
};
`
Commit changes to the DOM. commit will always do surgical reconciliation. and
it's the same function that's used in the options
`ts`
store.commit(): void
True when DFlex is not transforming any elements and not executing any task.
`ts`
isLayoutAvailable(): boolean
safely removing element from store.
`ts`
store.unregister(id: string): void
To destroy all DFlex instances. This is what you should do when you are done
with DnD completely and your app is about to be closed.
`ts``
store.destroy(): void;
For documentation, more information about DFlex and a live demo, be sure to visit the DFlex website
DFlex is MIT License.