入门
安装
在终端中输入:
# 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 函数,并使用事件监听器监听调用结果即可:
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='...'> 的外观由触发器在移动过程中使用属性进行设置。您还可以使用属性来设置动画参数、移动轴和最小发射距离。
/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> 则取消所有内置样式。
/**
* 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-dur、scope-del 和 scope-del 属性设置 CSS transition 属性的默认值。
contents 属性允许您设置 display:contents; 样式。
您还可以为不同的状态设置自定义 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-trigger 和 effdnd-actor,并返回用于订阅自定义事件的处理程序:
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 组件包含多个有用的方法和属性。
/**
* 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;
}