Skip to content

Getting started (V1)

Installation

Type in your terminal:

sh
# npm
npm i effdnd

# pnpm
pnpm add effdnd

# yarn
yarn add effdnd

Quick start

In short, effdnd uses data attributes to set the roles of regular HTML elements:

  • dragging element item,
  • dragging target target,
  • the dragging area scope.

Custom element effdnd-trigger will start drag-and drop for closest item. It can be configured via attributes (see ITriggerAttrs interface for details). The effdnd-trigger itself can also have the item role, meaning it can initiate its own dragging.

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

During dragging, effdnd sets dynamic attributes that show the state of the elements. The elements can be

  • without a state,
  • in a passive state,
  • in an active state.

The item is active while dragging.

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

  • effdnd-trigger contains the scope attribute with a value equal to the key of scope,
  • effdnd-trigger does not contain the scope attribute, and this is the closest scope to it.

The scope element becomes active if it is in a passive state and the item tries to go beyond it. This state helps to show the user the available drag limits.

The target element is passive when it is inside the scope with an active or passive state and any of the conditions are met

  • effdnd-trigger contains the target attribute with a value equal to the group of the target element,
  • effdnd-trigger does not contain the target attribute (all target elements are suitable).

The target element becomes active if it is in a passive state and the item is dragged over 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.