Doramagic 项目包 · 项目说明书
dnd 项目
💅 为 React 列表提供美观且易用的拖拽排序功能。⭐️ 点 Star 支持我们的工作!
Core API: DragDropContext, Draggable, and Droppable
@hello-pangea/dnd 是一个面向 React 应用的高级拖拽与放置(Drag and Drop)库,专门为「列表式」交互(垂直、水平、跨列表、嵌套列表)设计,并强调可访问性与键盘支持。该库对外暴露的核心 API 由三个组件构成——DragDropContext、Droppable、Draggable——它们构成嵌套的容器结构,缺一不可。
继续阅读本节完整说明和来源证据。
概述与设计目标
@hello-pangea/dnd 是一个面向 React 应用的高级拖拽与放置(Drag and Drop)库,专门为「列表式」交互(垂直、水平、跨列表、嵌套列表)设计,并强调可访问性与键盘支持。该库对外暴露的核心 API 由三个组件构成——DragDropContext、Droppable、Draggable——它们构成嵌套的容器结构,缺一不可。
资料来源:README.md 中说明 <DragDropContext /> 用于包裹整个拖拽区域,<Droppable /> 是可放置区域,其内部嵌套若干 <Draggable />。这种三层结构清晰地划分了职责:上下文负责状态与传感器注册,Droppable 负责放置区几何信息,Draggable 负责被拖拽元素本身。
资料来源:README.md 中说明 <DragDropContext /> 用于包裹整个拖拽区域,<Droppable /> 是可放置区域,其内部嵌套若干 <Draggable />。这种三层结构清晰地划分了职责:上下文负责状态与传感器注册,Droppable 负责放置区几何信息,Draggable 负责被拖拽元素本身。
State Management and Internal Architecture
@hello-pangea/dnd 内部以 Redux 作为核心状态引擎,借助 react-redux v9 将状态分发给拖拽上下文中的子组件(Droppable / Draggable)。从 package.json 可看到运行依赖中显式声明了 react-redux ^9.2.0 与 redux ^5.0.1(v17 起升级到 redux v5、react-redux ...
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述与设计目标
@hello-pangea/dnd 内部以 Redux 作为核心状态引擎,借助 react-redux v9 将状态分发给拖拽上下文中的子组件(Droppable / Draggable)。从 package.json 可看到运行依赖中显式声明了 react-redux ^9.2.0 与 redux ^5.0.1(v17 起升级到 redux v5、react-redux v9,是当前主版本),这意味着整个拖拽生命周期都由单一可预测的状态机驱动。
状态层主要负责四件事:
- 集中维护拖拽阶段(
IDLE/DRAGGING/DROP_ANIMATING等); - 调度传感器(Sensors)上报的原始输入为统一动作(Actions);
- 通过中间件链(Middleware)触发副作用,例如自动滚动与尺寸采集;
- 把派生数据回传给消费者(responders),供
onDragStart/onDragUpdate/onDragEnd使用。
公共类型与组件入口在 src/index.ts 中导出,其中 Announce、DragStart、DragUpdate、DropResult、MovementMode、PreDragActions、SnapDragActions 等类型决定了与用户代码之间的契约面。
状态管理架构
整体架构遵循「View → Sensor → Store → Middleware → View」的闭环:传感器把指针或键盘事件翻译为动作,动作经过中间件预处理后被 reducer 处理,中间件再据此驱动 AutoScroller、DimensionMarshal 等子系统。
flowchart LR
A[View 组件<br/>Draggable / Droppable] -->|用户输入| B[Sensor<br/>mouse / touch / keyboard]
B -->|Lift / Drop / Publish| C[Redux Store]
C --> D[lift 中间件]
C --> E[auto-scroll 中间件]
C --> F[focus 中间件]
D --> G[DimensionMarshal<br/>采集尺寸]
E --> H[AutoScroller<br/>自动滚动]
F --> I[DOM 焦点恢复]
G --> C
H --> C
C -->|订阅| A核心动作类型在 src/state/action-creators.ts 中定义,包括 BeforeInitialCaptureAction、LiftAction、InitialPublishAction、PublishWhileDraggingAction、DropAction、DropPendingAction、DropCompleteAction、DropAnimationFinishedAction 等。其中 cancel() 是 drop({ reason: 'CANCEL' }) 的语法糖,对应 DropReason = 'DROP' | 'CANCEL'(见 src/types.ts)。
核心中间件
lift 中间件
lift 中间件位于 src/state/middleware/lift.ts,负责把 LIFT 动作转化为可发布的拖拽状态。其关键步骤包括:
- 若当前处于
DROP_ANIMATING,先 dispatchcompleteDrop以收尾上一次拖拽; - 使用
invariant断言新阶段为IDLE(来自 src/invariant.ts 的RbdInvariant错误类在生产环境会剥除消息); dispatch(flush())清空占位符;dispatch(beforeInitialCapture(...))触发onBeforeCapture回调;- 最终根据
movementMode决定是否立即发布(SNAP模式 →shouldPublishImmediately: true)。
auto-scroll 中间件
auto-scroll 中间件(src/state/middleware/auto-scroll.ts)通过 guard(action, 'DROP_COMPLETE' | 'DROP_ANIMATE' | 'FLUSH') 终止自动滚动,并在 INITIAL_PUBLISH 时启动自动滚动器。它直接持有 AutoScroller 实例,并在每次 reducer 更新后调用 autoScroller.scroll(store.getState()),与状态机保持严格同步。AutoScroller 接口定义在 src/state/auto-scroller/auto-scroller-types.ts,仅暴露 start(state: DraggingState)、stop()、scroll(state: State) 三个方法。
焦点与 DOM 协调
键盘传感器依赖 src/view/key-codes.ts 中定义的 tab/enter/space/escape/arrowLeft 等常量,把按键翻译为方向指令。焦点恢复则由独立的 focus 中间件负责,并通过 src/view/use-sensor-marshal/closest.ts 中的 closest() 工具向上回溯 DOM,确保拖拽手柄始终可定位。
拖拽阶段与移动模式
状态机把一次拖拽拆分为四个阶段:IDLE → DRAGGING → DROP_PENDING → DROP_ANIMATING → IDLE。MovementMode 在 src/types.ts 中定义为 'FLUID' | 'SNAP':
- FLUID:由高粒度输入(鼠标、触摸)驱动,位置随指针实时更新;
- SNAP:由离散命令(键盘空格、回车)驱动,需要在收到指令后才发布一次。
进入 LIFT 时会同时携带 clientSelection 与 movementMode,lift 中间件据此决定立即发布还是等待后续 PUBLISH_WHILE_DRAGGING。DimensionMarshal 通过 src/state/dimension-marshal/dimension-marshal-types.ts 中描述的 Callbacks 把 collectionStarting、publishWhileDragging、updateDroppableScroll、updateDroppableIsEnabled、updateDroppableIsCombineEnabled 等动作回送至 store。
错误处理与初始化守护
库内部对异常路径做了显式守护。invariant(condition, message) 在开发环境抛出带消息的 RbdInvariant,在生产环境仅保留前缀以避免泄漏。DragDropContext 在挂载时调用 src/view/drag-drop-context/check-doctype.ts 检查 document.doctype 是否为 HTML5,缺失或带 publicId 时发出 warning,这关系到后续尺寸测量与定位算法的一致性。
See Also
- Sensors 与键盘协议:src/view/key-codes.ts
- 公开 API 契约:src/index.ts
- 升级到 redux v5 / react-redux v9 的变更说明:Release 17.0.0
- 自定义
AutoScroller配置:Release 16.1.0
来源:https://github.com/hello-pangea/dnd / 项目说明书
Sensors and Input Handling
@hello-pangea/dnd 将鼠标、键盘、触屏等不同输入设备统一抽象为一组传感器(Sensors),由它们负责把浏览器事件翻译成对拖放状态机的指令。这一层抽象既保证了多设备交互的一致性,也允许通过自定义传感器接入任意输入源(例如游戏手柄或脚本化测试)。资料来源:[README.md]()
继续阅读本节完整说明和来源证据。
概述
@hello-pangea/dnd 将鼠标、键盘、触屏等不同输入设备统一抽象为一组传感器(Sensors),由它们负责把浏览器事件翻译成对拖放状态机的指令。这一层抽象既保证了多设备交互的一致性,也允许通过自定义传感器接入任意输入源(例如游戏手柄或脚本化测试)。资料来源:README.md
<DragDropContext /> 默认启用鼠标、键盘、触屏三类传感器;通过 enableDefaultSensors 可以禁用默认集合,再以 sensors 属性完全接管。资料来源:src/view/drag-drop-context/drag-drop-context.tsx
架构与组件
整个传感器子系统围绕 useSensorMarshal 协调器构建。它统一管理:动作锁(action lock)、传感器生命周期、当前拖放阶段(LockPhase 取值为 PRE_DRAG / DRAGGING / COMPLETED),并在阶段不匹配时通过 isActive 帮助函数决定是否触发开发模式告警。资料来源:src/view/use-sensor-marshal/use-sensor-marshal.ts
flowchart TB
DOM[DOM 事件源] --> Marshal[useSensorMarshal<br/>协调器与动作锁]
Marshal --> Mouse[useMouseSensor]
Marshal --> Touch[useTouchSensor]
Marshal --> Kbd[useKeyboardSensor]
Kbd --> KC[key-codes.ts<br/>按键常量]
Mouse --> Lift[lift 中间件]
Kbd --> Lift
Touch --> Lift
Lift --> Store[Redux Store<br/>状态机]
Marshal -.校验.-> Validate[useValidateSensorHooks]三个传感器 Hook 都从 src/view/use-sensor-marshal/index.ts 统一导出,并在 src/index.ts 中再次对外暴露,因此使用者可以单独 import 它们并自行组合。资料来源:src/view/use-sensor-marshal/index.ts, src/index.ts
键盘传感器详解
键盘传感器是整套交互中职责最明确的一类。所有按键码集中在 src/view/key-codes.ts,包括 tab(9)、enter(13)、escape(27)、space(32)、pageUp / pageDown、end / home 以及四个方向键,便于在不同传感器间复用同一份常量。资料来源:src/view/key-codes.ts
useKeyboardSensor 在 keydown 回调里把这些按键映射到拖放动作:space 触发落放(actions.drop()),escape 触发取消(actions.cancel()),pageUp / pageDown / home / end 则被列入 scrollJumpKeys,由传感器触发滚动跳转逻辑。资料来源:src/view/use-sensor-marshal/sensors/use-keyboard-sensor.ts
为了避免 space 引起页面滚动或 tab 引发焦点跳动,传感器在拖动期间会通过 preventStandardKeyEvents 阻止浏览器默认行为,并在拖动开始时绑定、在 stop() 时解绑全部键盘事件。资料来源:src/view/use-sensor-marshal/sensors/util/prevent-standard-key-events.ts
输入事件绑定与校验
协调器在事件回调中需要把指针或焦点位置映射到具体的 <Draggable />。findClosestDraggableIdFromEvent 借助 closest ponyfill 在 DOM 树中向上回溯;该 ponyfill 兼容 IE11 的 msMatchesSelector / webkitMatchesSelector 命名差异。资料来源:src/view/use-sensor-marshal/closest.ts
为了避免误触发,isEventInInteractiveElement 会判断事件目标是否位于按钮、链接等可交互元素中;当标签页切换到后台时,协调器还会通过 supportedPageVisibilityEventName 注册可见性事件,从而安全停止拖动。资料来源:src/view/use-sensor-marshal/use-sensor-marshal.ts
开发模式下,useValidateSensorHooks 利用 invariant 强制约束:传感器 Hook 的数量在组件挂载后必须保持不变。任何动态增删都会抛错以避免状态机错乱;非生产环境会附带完整 message,生产环境则仅抛出 RbdInvariant 并剥离文本。资料来源:src/view/use-sensor-marshal/use-validate-sensor-hooks.ts, src/invariant.ts
启动期检查
除了传感器自身,库在挂载时还会执行两类静态校验:
- Doctype 检查:若文档未声明 HTML5 doctype,
check-doctype会通过warning发出提示,确保布局与测量结果一致。资料来源:src/view/drag-drop-context/check-doctype.ts - React 版本检查:
check-react-version通过正则解析react的 peerDependency 与运行时React.version,使用isSatisfied对主/次/补丁号逐级比较。资料来源:src/view/drag-drop-context/check-react-version.ts
这两个检查与 v17.0.0 起的 React 18/19 适配、useId 支持(v16.2.0)等历史变更紧密相关;v17.0.0 之后官方只支持 React 18+,并在 v18.0.0-beta.0 中将 Node 引擎升级到 20.17.0。资料来源:package.json
常见失败模式
- 在
<DragDropContext />中动态增减sensors数量:会触发useValidateSensorHooks的 invariant 错误。资料来源:src/view/use-sensor-marshal/use-validate-sensor-hooks.ts - 在错误阶段发起
LIFTaction:lift中间件要求getState().phase === 'IDLE',若处于DROP_ANIMATING必须先completeDrop,否则会抛错。资料来源:src/state/middleware/lift.ts - 动作锁过期后调用处理器:
actions.drop()/actions.cancel()等若在 lock 失效后被调用,协调器会通过warning提示丢弃旧回调。资料来源:src/view/use-sensor-marshal/use-sensor-marshal.ts
公共入口
- 传感器 Hook:
useMouseSensor、useTouchSensor、useKeyboardSensor来自src/view/use-sensor-marshal,并在src/index.ts中对外导出。资料来源:src/index.ts - Redux 动作:
drop/dropPending/completeDrop等定义于src/state/action-creators.ts,由lift中间件在传感器派发LIFT/DROP时调用。资料来源:src/state/action-creators.ts
See Also
<DragDropContext />响应者(onDragStart/onDragUpdate/onDragEnd/onBeforeDragStart)- 自定义传感器 API(
Sensor/SensorAPI) - 自动滚动与键盘可达性指南
- 多设备拖放模式(虚拟列表、表格、多选拖拽)
来源:https://github.com/hello-pangea/dnd / 项目说明书
Advanced Features: Auto-Scrolling, Accessibility, and Patterns
@hello-pangea/dnd 旨在为 React 应用提供"漂亮且自然"的列表拖拽体验。本页聚焦三大高级主题:自动滚动(Auto-Scrolling)、可访问性(Accessibility) 与常见模式(Patterns)。这些能力共同支撑了项目"漂亮、可访问、高性能"的核心理念——正如 README.md 所宣称的"Beautiful and natural mov...
继续阅读本节完整说明和来源证据。
高级功能:自动滚动、可访问性与模式
概述
@hello-pangea/dnd 旨在为 React 应用提供"漂亮且自然"的列表拖拽体验。本页聚焦三大高级主题:自动滚动(Auto-Scrolling)、可访问性(Accessibility) 与常见模式(Patterns)。这些能力共同支撑了项目"漂亮、可访问、高性能"的核心理念——正如 README.md 所宣称的"Beautiful and natural movement of items 💐"与"powerful keyboard and screen reader support ♿️"。
来源:https://github.com/hello-pangea/dnd / 项目说明书
失败模式与踩坑日记
保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。
假设不成立时,用户拿不到承诺的能力。
新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
风险会影响是否适合普通用户安装。
可能影响升级、迁移或版本选择。
Pitfall Log / 踩坑日志
项目:hello-pangea/dnd
摘要:发现 7 个潜在踩坑项,其中 0 个为 high/blocking;最高优先级:能力坑 - 能力判断依赖假设。
1. 能力坑 · 能力判断依赖假设
- 严重度:medium
- 证据强度:source_linked
- 发现:README/documentation is current enough for a first validation pass.
- 对用户的影响:假设不成立时,用户拿不到承诺的能力。
- 证据:capability.assumptions | github_repo:321362240 | https://github.com/hello-pangea/dnd | README/documentation is current enough for a first validation pass.
2. 维护坑 · 维护活跃度未知
- 严重度:medium
- 证据强度:source_linked
- 发现:未记录 last_activity_observed。
- 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
- 证据:evidence.maintainer_signals | github_repo:321362240 | https://github.com/hello-pangea/dnd | last_activity_observed missing
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 证据:downstream_validation.risk_items | github_repo:321362240 | https://github.com/hello-pangea/dnd | no_demo; severity=medium
4. 安全/权限坑 · 存在评分风险
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:风险会影响是否适合普通用户安装。
- 证据:risks.scoring_risks | github_repo:321362240 | https://github.com/hello-pangea/dnd | no_demo; severity=medium
5. 安全/权限坑 · 来源证据:Dependency Dashboard
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个安全/权限相关的待验证问题:Dependency Dashboard
- 对用户的影响:可能影响升级、迁移或版本选择。
- 证据:community_evidence:github | https://github.com/hello-pangea/dnd/issues/210 | 来源讨论提到 node 相关条件,需在安装/试用前复核。
6. 维护坑 · issue/PR 响应质量未知
- 严重度:low
- 证据强度:source_linked
- 发现:issue_or_pr_quality=unknown。
- 对用户的影响:用户无法判断遇到问题后是否有人维护。
- 证据:evidence.maintainer_signals | github_repo:321362240 | https://github.com/hello-pangea/dnd | issue_or_pr_quality=unknown
7. 维护坑 · 发布节奏不明确
- 严重度:low
- 证据强度:source_linked
- 发现:release_recency=unknown。
- 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
- 证据:evidence.maintainer_signals | github_repo:321362240 | https://github.com/hello-pangea/dnd | release_recency=unknown
来源:Doramagic 发现、验证与编译记录