Skip to content

入门

安装

在终端中输入:

sh
# npm
npm i effdnd

# pnpm
pnpm add effdnd

# yarn
yarn add effdnd

快速入门

简而言之,effdnd 使用了两个自定义 Web 组件:

  • effdnd trigger 会触发拖放操作,
  • effdnd-actor 指示布局中将参与拖放过程的区域(扮演各自的角色)。

Web 组件 effdnd-actor 可以扮演多种角色:

  • item - 被移动的物品,
  • target - 移动的目标,
  • scope - 移动边界。

要设置角色,需要在 effdnd-actor 元素上设置同名属性。一个元素可以设置多个角色。这些属性值将由 effdnd-trigger 组件用于识别拖放参与者。

在拖放操作开始时,effdnd-trigger Web 组件使用其属性来选择流程中的参与者(actors):

  • item 属性允许您选择要移动的元素。如果未指定该属性,则会使用树中具有指定 item 属性的最接近的 <effdnd-actor item='...'> 元素。如果设置了值为 # 的属性(<effdnd-trigger item='#'>)或上述 DOM 树中不存在 <effdnd-actor item='...'> 元素,则会移动触发器本身。如果触发器具有 clone 属性,则会创建一个 HTML 元素的副本用于移动;
  • scope 属性允许您选择可用的移动区域。如果未指定该属性,则会使用树中最近的具有指定 scope 属性的 <effdnd-actor scope='...'>。如果设置了值为 # 的属性(<effdnd-trigger scope='#'>)或上面的 DOM 树中没有 <effdnd-actor scope='...'>,则作用域区域将是 document.body
  • target 属性允许您选择可用的移动目标。当拖放操作结束时,移动目标会生成特殊事件。位于 scope 区域内的所有 <effdnd-actor target='...'> 元素都将被视为移动目标,其名称以触发器的 target 属性值开头。这意味着,如果未指定该属性,则所有位于 <effdnd-actor scope='...'> 内的 <effdnd-actor target='...'> 元素都可用。

要定义这两个 Web 组件,只需调用 useDnD 函数,并使用调用结果创建事件监听器即可。

jsx
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>;
}

主动元件和被动元件

在拖放过程中,effdnd-trigger 会设置选定的 effdnd-actor 元素的动态 state 属性。也就是说,在操作开始时,触发器会确定要移动的元素(item)、移动范围(scope)以及目标位置(target)。 这些元素可以是

  • 没有国家,
  • 处于被动状态(state="passive"),
  • 处于活动状态(state="active")。

具有 item 角色的元素在移动过程中处于活动状态。此外,如果具有 item 角色的元素的克隆体处于活动状态,则该元素可以处于克隆状态state="cloned")。

item 移动到具有 scope 角色的元素内部时,该元素处于被动状态

如果具有 scope 角色的元素处于 被动 状态,并且 item 尝试超出该角色范围,则该元素变为 主动 状态。此状态会向用户显示可用的移动范围限制。

当具有 target 角色的元素位于具有活动或被动状态的 scope 内时,该元素处于被动状态

如果具有 target 角色的物品处于 被动 状态,并且 item 移动到它上面,则该物品变为 主动 状态。

组件样式

<effdnd-actor item='...'> 的外观由触发器在移动过程中使用属性进行设置。您还可以使用属性来设置动画参数、移动轴和最小发射距离。

ts
/**
 * 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;
}

Web 组件 effdnd-actor 已包含 targetscope 被动和主动状态的初始样式,但您也可以使用 unstyled 属性取消这些样式。同时,<effdnd-actor unstyled="target"> 仅取消 target 的内置样式,<effdnd-actor unstyled="scope"> 仅取消 scope 的内置样式,而 <effdnd-actor unstyled> 则取消所有内置样式。

ts
/**
 * 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;
}

contents 属性允许您设置 display:contents; 样式。如果添加 unstyled 属性,您可以为不同的状态设置自定义 CSS 样式。

css
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

该函数接受过渡设置作为第一个参数,自定义样式作为第二个参数。它返回用于监听自定义事件的函数:

ts
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

触发器 Web 组件包含多个有用的方法和属性。

ts
/**
 * 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>;
}

根据 Apache-2.0 许可证发布。