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. The attribute values will be used to identify drag-and-drop participants by the effdnd-trigger component.
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 no<effdnd-actor item='...'>in the DOM tree above, then the trigger itself will move. If the trigger has thecloneattribute, a clone of the HTML element will be created for the movement; - the
scopeattribute allows you to select the available area of movement. If the attribute is not specified, the closest in the tree<effdnd-actor scope='...'>with the specifiedscopeattribute will be used. 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 scope area will bedocument.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<effdnd-actor scope='...'>.
To define both web components, simply call the useDnD function, and use the results of the call to create an event listeners
import { useDnD } from 'effdnd';
// you can pass style parameters to useDnD,
// to override the movement parameters
// (for more information, see the type `TUseDnD`)
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 elements. That is, at the very beginning, the trigger determines what is being moved (item), how is this limited (scope) and where it should be moved to (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.
The element with the scope role is passive when the item moves inside it.
The element with the scope role becomes active if it is in a passive state and the item tries to go beyond it. This state shows the user the available movement limits.
The element with the target role is passive when it is inside a scope with an active or passive state.
The item with the target role becomes active if it is in a passive state and the item moves over it.
Component 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.
/**
* DnD trigger attributes
*/
export interface ITriggerAttrs {
/**
* Item name
* @description
* If the value is "#" then it will use `effnd-trigger` as scope
*/
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;
/**
* Item z-index in active state
*/
zi?: string;
/**
* Item opacity in active state
*/
opacity?: string;
/**
* Use item clone while dragging
*/
clone?: boolean;
}The web component effdnd-actor already contains the initial styles for the passive and active states of both target and scope, 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;
}The contents attribute allows you to set the display:contents; style. If you add the unstyled attribute, you can 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 accepts transition settings as the first argument and custom styles as the second. It returns functions for listening 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 = (defs?: Partial<{
/**
* Duration
*/
dur: number;
/**
* Delay
*/
del: number;
/**
* Transition-timing-function
*/
tf: string;
/**
* Active DnD item z-index
*/
zi: number;
/**
* Active DnD item opacity
*/
opacity: number;
}>) => {
/**
* 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 {
/**
* 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 transforms
*/
resetDnD(options?: KeyframeAnimationOptions): Promise<void>;
}