Skip to content

Getting started

Installation

Type in your terminal:

sh
# npm
npm i effdnd

# pnpm
pnpm add effdnd

# yarn
yarn add effdnd

Quick start

In short, scope is used to limit the area of DnD, target is used as the destination of DnD, item is the element that is being moved. Custom element effdnd-trigger will start DnD for closest item. It can be configured via effdnd-trigger attributes (see ITriggerAttrs interface for details).

Just call useDnD function to define effdnd-trigger and use its result to create special data-attributes.

jsx
import { useDnD } from 'effdnd';

// you can pass style params to useDnD to override global CSS for DnD elements (see `TUseDnD` type for details)
const { scope, item, target, css, observe } = useDnD();
// create scope attrs
const scopeAttrs = scope('local');
// create target attrs
const fisrtTargetAttrs = target('first');
// targets can be grouped
const secondTargetAttrs = target('second', 'group');
// create item attrs
const fisrtItemAttrs = item('first');
const secondItemAttrs = item('second');
// you can even set up separate styles for different DnD elements
const separateStyleAttrs = css({
    passiveTarget: 'border: 4px solid grey',
});

export const Component = () => {
    const ref = useRef();
    useEffect(() => {
      // you can observe DnD events
      const unobserve = observe((e) => {
        // DnD event reaction
      }, ref.current);
      // and you can unobserve
      return () => unobserve();
    })
    // just apply ready attrs - and magic happen
    // don't forget to use `<effdnd-trigger>` inside items
    return <div ref={ref} {...scopeAttrs}>
        <div className="targets-wrapper">
            <div {...fisrtTargetAttrs}>...</div>
            <div {...secondTargetAttrs} {...separateStyleAttrs}>...</div>
        </div>
        <div id="items-wrapper">
            <div {...fisrtItemAttrs}>
                <effdnd-trigger>Trigger #1</effdnd-trigger>
            </div>
            <div {...secondItemAttrs}>
                <effdnd-trigger>Trigger #2</effdnd-trigger>
            </div>
        </div>
    </div>;
}

Active and passive elements

When dragging effdnd set special dynamic attributes indicating the activity of the element.

The item is active while dragging.

The scope is passive when the drag item is inside it and at least one condition is truthy

  • effdnd-trigger has no scope attribute (closest scope is suitable)
  • effdnd-trigger has scope attribute and its value is equal to scope key

The scope becomes active when it is passive and the drag item is trying to get out.

The target is passive when it is inside scope and at least one condition is truthy

  • effdnd-trigger has no target attribute (all targets are suitable)
  • effdnd-trigger has target attribute and its value is equal to target group

The target becomes active when it is passive and the drag item is trying to get on top of it.

useDnD

The function accepts transition settings as the first argument and custom styles as the second one. It returns attribute resolvers and event handlers:

ts
type TEventDetail = {
    type: TEventType;
    refs: TRefs;
    keys: TKeys;
    event: MouseEvent | TouchEvent;
};

interface TDnDEvent extends CustomEvent {
    detail: TEventDetail;
}

type TDnDCallback = (event: TDnDEvent) => void;

type TDynamicAttrs = Partial<{
    /**
     * Active DnD item styles
     */
    activeItem: string;
    /**
     * Passive DnD target styles
     */
    passiveTarget: string;
    /**
     * Active DnD target styles
     */
    activeTarget: string;
    /**
     * Passive DnD scope styles
     */
    passiveScope: string;
    /**
     * Active DnD scope styles
     */
    activeScope: string;
}>;

type TUseDnD = {
    (transition?: Partial<{
        /**
         * Duration
         */
        dur: string | number;
        /**
         * Delay
         */
        del: string | number;
        /**
         * Transition-timing-function
         */
        tf: string;
    }>, css?: TDynamicAttrs): {
        /**
         * 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;
        /**
         * Use DnD item
         * @param key - item key
         */
        item: (key: string) => object;
        /**
         * Use DnD target
         * @param key - target key
         */
        target: (key: string, group?: string) => object;
        /**
         * Use DnD scope
         * @param key - scope key
         */
        scope: (key?: string) => object;
        /**
         * Use different CSS for DnD elements
         */
        css: (params: TDynamicAttrs) => object;
    };
    customCount?: number;
    stylesheet?: CSSStyleSheet;
}

effdnd-trigger

The element can be configured via attributes:

ts
interface ITriggerAttrs {
    /**
     * Bounding scope key
     */
    scope?: string;
    /**
     * Targets group
     */
    target?: string;
    /**
     * Triggering distance
     */
    dist?: string;
    /**
     * Triggering event
     * @description
     * Both 'touch' and 'mouse' by default
     */
    event?: 'touch' | 'mouse';
}

Released under the Apache-2.0 License.