Getting started
Installation
Enter in your terminal:
# npm
npm i effdnd
# pnpm
pnpm add effdnd
# yarn
yarn add effdndQuick start
In short, effdnd uses two custom web component:
effdnd triggeris triggerring drag-and-drop,effdnd-actorindicates the areas of the layout that will participate in the drag-and-drop process (play their roles).
The web component effdnd-actor can "play" several roles:
item- the item being moved,target- the target of the move,scope- movement boundaries.
To set a role, you need to set the attribute of the same name on the effdnd-actor element. Multiple roles can be set for a single element.
At the beginning of drag-and-drop, the effdnd-trigger web component uses its attributes to select the participants in the process (actors):
- the
itemattribute allows you to select the item to move. If the attribute is not specified, the closest in the tree<effdnd-actor item='...'>with the specified attributeitemwill be used. If an attribute with the value#is set (<effdnd-trigger item='#'>) or there is not a single<effdnd-actor item='...'>in the DOM tree above, then the trigger itself will move; - the
scopeattribute allows you to select the available area of movement. If the attribute is not specified, the closest one in the tree will be used<effdnd-actor scope='...'>with the specifiedscopeattribute. If an attribute with the value#is set (<effdnd-trigger scope='#'>) or there is no<effdnd-actor scope='...'>in the DOM tree above, then the displacement area will be considereddocument.body; - the
targetattribute allows you to select the available movement targets. Movement targets generate special events when drag-and-drop ends on them. All<effdnd-actor target='...'>elements located inside thescopearea will be considered as movement targets, the names of which begin with the value of thetargetattribute of the trigger. This means that if the attribute is not specified, then all<effdnd-actor target='...'>are available inside the current<effdnd-actor scope='...'>.
To define both web components, simply call the useDnD function, and use event listening to the results of the call:
import { useRef } from 'react';
import { useDnD } from 'effdnd';
const { observe } = useDnD();
export const Component = () => {
const ref = useRef();
useEffect(() => {
// you can subscribe to Drag-and-Drop events
const unobserve = observe((e) => {
// the event is being processed here
}, ref.current);
// and you can unsubscribe
return () => unobserve();
});
return <effdnd-actor scope='top' ref={ref} >
<div className="targets-wrapper">
<effdnd-actor target='target-1'>...</effdnd-actor>
<effdnd-actor target='target-2'>...</effdnd-actor>
</div>
<div id="items-wrapper">
<effdnd-actor item='item-1'>
<effdnd-trigger>Trigger #1</effdnd-trigger>
</effdnd-actor>
<effdnd-actor item='item-2'>
<effdnd-trigger>Trigger #2</effdnd-trigger>
</effdnd-actor>
</div>
</effdnd-actor>;
}Active and passive elements
During drag-and-drop, the effdnd-trigger sets the dynamic state attribute on the selected effdnd-actor actors. That is, at the very beginning, the trigger determines what is moving (item), where it can move (scope) and where it should be moved (target). The elements can be
- without a state,
- in passive state (
state="passive"), - in active state (
state="active").
The element with the item role is active while it is being moved. In addition, an element with the role item can be in the cloned state (state="cloned") if its clone is active. To clone an item, turn on the item-mode="cloned" mode.
An element with the scope role ** is passive** when the item moves inside it. This state delineates the boundaries of the displacement area.
An element with the scope role becomes active if it is in a passive state and the item tries to go beyond it. This state indicates to the user that the cursor has moved beyond the acceptable limits.
An element with the target role is passive when it is inside a scope with an active or passive state. This state shows where the item can be moved.
An item with the target role becomes active if it is in a passive state and the cursor with the item is above it. This status indicates that the target is preparing to accept the item.
Role modes
The basic behavior of the library is quite low-level, so it is possible to refine the use case through the mode of each role:
item-modedefines theitemmode, can set the following values:item-mode="keep"- theitemelement will keep its new coordinates after moving;item-mode="clone"- a clone will be created for theitemelement, which will move;
target-modedefines thetargetmode after it is selected and can set the following values:target-mode="prepend"- theitemelement will be added to thetargetas the first child element;target-mode="append"- theitemelement will be added to thetargetas the last child element;target-mode="remove"- theitemelement will be removed from the DOM tree;
scope-modedefines thescopemode during drag-and-drop and can set the following values:scope-mode="order-x"- thescopeelement allows you to rearrange theitemelements along thexaxis;scope-mode="order-y"- thescopeelement allows you to rearrange theitemelements along theyaxis.
Styles
The appearance of <effdnd-actor item='...'> is set by the trigger using attributes during movement. You can also use attributes to set animation parameters, the axis of movement, and the minimum firing distance.
/export interface ITriggerAttrs {
/**
* Item name
* @description
* If the value is "#" then it will use `effnd-trigger` as item
*/
item?: string;
/**
* Bounding scope name
* @description
* If the value is "#" then it will use `document.body` as scope
*/
scope?: string;
/**
* Target name or its prefix
*/
target?: string;
/**
* Triggering distance
*/
dist?: string;
/**
* DnD axis
*/
axis?: string;
/**
* Triggering event
* @description
* Both 'touch' and 'mouse' by default
*/
event?: 'touch' | 'mouse';
/**
* Transition duration
*/
dur?: string;
/**
* Transition delay
*/
del?: string;
/**
* Transition timing-function
*/
tf?: string;
}By default, an SVG movement icon will be used inside the empty trigger, the appearance of which depends on the axis attribute. To hide the icon, use your content inside the effdnd-trigger.
The web component effdnd-actor already contains the initial styles for the passive and active states of roles, but it also allows you to cancel them using the unstyled attribute. At the same time, <effdnd-actor unstyled="target"> cancels only the built-in styles of target, <effdnd-actor unstyled="scope"> cancels only the built-in styles of scope, and <effdnd-actor unstyled> cancels all built-in styles.
/**
* DnD actor attributes
*/
export interface IActorAttrs {
/**
* Item name
*/
item?: string;
/**
* Scope name
*/
scope?: string;
/**
* Target name
*/
target?: string;
/**
* Actor state
*/
state?: 'active' | 'passive';
/**
* Reset target state styles
*/
unstyled?: boolean | 'target' | 'scope';
/**
* Display contents
*/
contents?: boolean;
/**
* Item mode
*/
'item-mode'?: 'keep' | 'clone';
/**
* Target mode
*/
'target-mode'?: 'append' | 'prepend' | 'remove';
/**
* Scope mode
*/
'scope-mode'?: 'order-x' | 'order-y';
/**
* Scope transition duration
*/
'scope-dur'?: number;
/**
* Scope transition delay
*/
'scope-del'?: number;
/**
* Scope transition timing-function
*/
'scope-tf'?: string;
}The scope-dur, scope-del, and scope-del attributes for an element with the scope role set the default values of the CSS transition property.
The contents attribute allows you to set the display:contents; style.
You can also set your own CSS styles for different states:
effdnd-actor[target][state=passive] .custom-dnd-target {
outline: 4px solid #646cffaa;
}
effdnd-actor[target][state=active] .custom-dnd-target {
outline: 4px solid #646cffaa;
background: #646cffaa;
}useDnD
The function declares the web components effdnd-trigger and effdnd-actor, and also returns handlers for subscribing to custom events:
export type TEventDetail = {
type: TEventType;
refs: TRefs;
keys: TKeys;
event: MouseEvent | TouchEvent;
};
interface TDnDEvent extends CustomEvent {
detail: TEventDetail;
}
export type TDnDCallback = (event: TDnDEvent) => void;
export type TUseDnD = () => {
/**
* Observe DnD events
* @param callback - event handler
* @param element - HTML element
*/
observe: (callback: TDnDCallback, element?: HTMLElement) => () => void;
/**
* Unobserve DnD events
* @param callback - event handler
* @param element - HTML element
*/
unobserve: (callback: TDnDCallback, element?: HTMLElement) => void;
};effdnd-trigger
The trigger web component contains several useful methods and properties:
/**
* DnD trigger element
*/
export interface ITriggerElement extends HTMLElement {
/**
* Is trigger active
*/
isActive: boolean;
/**
* DnD item y-offset
*/
dndY: number;
/**
* DnD item x-offset
*/
dndX: number;
/**
* DnD item
*/
dndItem: HTMLElement;
/**
* DnD scope
*/
dndScope: HTMLElement;
/**
* DnD targets
*/
dndTargets: Set<HTMLElement>;
/**
* Reset DnD translate
*/
resetDnD(): void;
}