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 { useRef } from 'react';
import { useDnD } from 'effdnd';

const { observe } = useDnD();

export const Component = () => {
    const ref = useRef();
    useEffect(() => {
      // 您可以订阅拖放事件
      const unobserve = observe((e) => {
        // 自定义事件正在此处处理。
      }, ref.current);
      // 您可以取消订阅
      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-mode="cloned" 模式。

当元素在其内部移动时,具有 scope 角色的元素处于被动状态。此状态界定了位移区域的边界。

如果具有 scope 角色的元素处于 被动 状态,并且 item 尝试超出该状态,则该元素会变为 主动 状态。此状态会向用户表明光标已超出可接受的范围。

当一个元素位于一个具有激活或被动状态的“作用域”内时,如果该元素具有 target 角色,则该元素处于被动状态。此状态决定了该元素可以移动到何处。

如果一个具有 target 角色的项处于 被动 状态,且光标位于该项上方,则该项变为 活动 状态。此状态表示 target 正在准备接收该项。

角色模式

该库的基本行为相当底层,因此可以通过每个角色的模式来细化用例:

  • item-mode 定义了 item 元素的模式,可以设置以下值:
    • item-mode="keep" - item 元素移动后将保持其新的坐标;
    • item-mode="clone" - 将为 item 元素创建一个克隆,该克隆将进行移动;
  • target-mode 定义了选中 target 元素后的模式,可以设置以下值:
    • target-mode="prepend" - 将 item 元素作为第一个子元素添加到 target 元素中;
    • target-mode="append" - 将 item 元素作为最后一个子元素添加到 target 元素中;
    • target-mode="remove" - 从 DOM 树中移除 item 元素;
  • scope-mode 定义了拖放过程中的 scope 模式,可以设置以下值:
    • scope-mode="order-x" - scope 元素允许您沿 x 轴重新排列 item 元素;
    • scope-mode="order-y" - scope 元素允许您沿 y 轴重新排列 item 元素。

款式

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

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

默认情况下,空触发器内会使用 SVG 移动图标,其外观取决于 axis 属性。要隐藏该图标,请在 effdnd-trigger 内使用您的内容。

Web 组件 effdnd-actor 已包含角色被动和主动状态的初始样式,但您也可以使用 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;
    /**
     * 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;
}

对于具有 scope 角色的元素,scope-durscope-delscope-del 属性设置 CSS transition 属性的默认值。

contents 属性允许您设置 display:contents; 样式。

您还可以为不同的状态设置自定义 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

该函数声明了 Web 组件 effdnd-triggereffdnd-actor,并返回用于订阅自定义事件的处理程序:

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 = () => {
    /**
     * 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 {
    /**
     * 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;
}

根据 Apache-2.0 许可证发布。